diff --git a/src/expert-claim/dto/expert-claim-v2.dto.spec.ts b/src/expert-claim/dto/expert-claim-v2.dto.spec.ts index d731c70..ecf6bb3 100644 --- a/src/expert-claim/dto/expert-claim-v2.dto.spec.ts +++ b/src/expert-claim/dto/expert-claim-v2.dto.spec.ts @@ -43,4 +43,22 @@ describe("SubmitExpertReplyV2Dto", () => { expect(await validate(dto)).toHaveLength(0); }); + + it("accepts a repair part without daghi", async () => { + const dto = plainToInstance(SubmitExpertReplyV2Dto, { + description: "Damage assessment", + parts: [ + { + partId: 201, + typeOfDamage: TypeOfDamage.Repair, + price: "10000", + salary: "0", + totalPayment: "10000", + factorNeeded: false, + }, + ], + }); + + expect(await validate(dto)).toHaveLength(0); + }); }); diff --git a/src/expert-claim/dto/expert-claim-v2.dto.ts b/src/expert-claim/dto/expert-claim-v2.dto.ts index 8c12e96..3a8418b 100644 --- a/src/expert-claim/dto/expert-claim-v2.dto.ts +++ b/src/expert-claim/dto/expert-claim-v2.dto.ts @@ -94,11 +94,17 @@ export class PartPricingV2Dto { @IsRepairLineAmountToman({ allowZero: true }) totalPayment: string; - @ApiProperty({ type: DaghiDetailsV2Dto }) + @ApiPropertyOptional({ + type: DaghiDetailsV2Dto, + description: "Required for change lines; omit for repair lines.", + }) + @ValidateIf( + (part: PartPricingV2Dto) => part.typeOfDamage === TypeOfDamage.Change, + ) @IsNotEmpty() @ValidateNested() @Type(() => DaghiDetailsV2Dto) - daghi: DaghiDetailsV2Dto; + daghi?: DaghiDetailsV2Dto; @ApiProperty({ example: false, diff --git a/src/expert-claim/expert-claim.service.spec.ts b/src/expert-claim/expert-claim.service.spec.ts index 3455681..e9cc395 100644 --- a/src/expert-claim/expert-claim.service.spec.ts +++ b/src/expert-claim/expert-claim.service.spec.ts @@ -27,6 +27,31 @@ function createService() { } describe("ExpertClaimService expert-reply pricing", () => { + it("allows a repair line without daghi and removes a stray daghi payload", () => { + const service = createService() as any; + + expect( + service.validateAndNormalizeDaghiForExpertReplyV2([ + { + partId: 201, + typeOfDamage: TypeOfDamage.Repair, + price: "10000", + salary: "0", + totalPayment: "10000", + daghi: { option: DaghiOption.NO_VALUE }, + }, + ]), + ).toEqual([ + { + partId: 201, + typeOfDamage: TypeOfDamage.Repair, + price: "10000", + salary: "0", + totalPayment: "10000", + }, + ]); + }); + it("rejects a blank pricing line on the legacy submit endpoint before changing status", async () => { const service = createService() as any; const findAndUpdate = jest.fn(); diff --git a/src/expert-claim/expert-claim.service.ts b/src/expert-claim/expert-claim.service.ts index 9418dce..0960f63 100644 --- a/src/expert-claim/expert-claim.service.ts +++ b/src/expert-claim/expert-claim.service.ts @@ -102,6 +102,7 @@ import { ClaimFactorsImageDbService } from "src/claim-request-management/entites import { ClaimRequiredDocumentDbService } from "src/claim-request-management/entites/db-service/claim-required-document.db.service"; import { FactorStatus } from "src/Types&Enums/claim-request-management/factor-status.enum"; import { DaghiOption } from "src/Types&Enums/claim-request-management/daghi-option.enum"; +import { TypeOfDamage } from "src/Types&Enums/claim-request-management/type-of-damage.enum"; import { BranchDbService } from "src/client/entities/db-service/branch.db.service"; import { ClaimSubmitResendV2Dto } from "./dto/expert-claim-v2.dto"; import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service"; @@ -947,6 +948,7 @@ export class ExpertClaimService { parts: import("./dto/expert-claim-v2.dto").PartPricingV2Dto[], ) { for (const part of parts) { + if (part.typeOfDamage === TypeOfDamage.Repair) continue; if (!part.daghi || !part.daghi.option) { throw new BadRequestException( `Daghi option is required for part ${part.partId}`, @@ -972,16 +974,23 @@ export class ExpertClaimService { } } - return parts.map((part) => ({ - ...part, - daghi: { - option: part.daghi.option, - ...(part.daghi.price && { price: part.daghi.price }), - ...(part.daghi.branchId && Types.ObjectId.isValid(part.daghi.branchId) && { - branchId: new Types.ObjectId(part.daghi.branchId), - }), - }, - })); + return parts.map((part) => { + if (part.typeOfDamage === TypeOfDamage.Repair) { + const { daghi: _daghi, ...repairPart } = part; + return repairPart; + } + return { + ...part, + daghi: { + option: part.daghi!.option, + ...(part.daghi!.price && { price: part.daghi!.price }), + ...(part.daghi!.branchId && + Types.ObjectId.isValid(part.daghi!.branchId) && { + branchId: new Types.ObjectId(part.daghi!.branchId), + }), + }, + }; + }); } private findClosestCar(input: string, cars: { carName: string }[]) { @@ -1711,6 +1720,7 @@ export class ExpertClaimService { // Validate daghi fields if (reply.parts && reply.parts.length > 0) { for (const part of reply.parts) { + if (part.typeOfDamage === TypeOfDamage.Repair) continue; if (!part.daghi || !part.daghi.option) { throw new BadRequestException( `Daghi option is required for part ${part.partId}`, @@ -1758,16 +1768,22 @@ export class ExpertClaimService { : ClaimStepsEnum.WaitingForUserToReact; // Process parts: convert branchId string to ObjectId if present - const processedParts = reply.parts.map((part) => ({ - ...part, - daghi: { - option: part.daghi.option, - ...(part.daghi.price && { price: part.daghi.price }), - ...(part.daghi.branchId && { - branchId: new Types.ObjectId(part.daghi.branchId), - }), - }, - })); + const processedParts = reply.parts.map((part) => { + if (part.typeOfDamage === TypeOfDamage.Repair) { + const { daghi: _daghi, ...repairPart } = part; + return repairPart; + } + return { + ...part, + daghi: { + option: part.daghi!.option, + ...(part.daghi!.price && { price: part.daghi!.price }), + ...(part.daghi!.branchId && { + branchId: new Types.ObjectId(part.daghi!.branchId), + }), + }, + }; + }); const expertProfileSnapshot = await this.snapshotDamageExpert(userId.sub); diff --git a/src/helpers/expert-reply-pricing.spec.ts b/src/helpers/expert-reply-pricing.spec.ts index d87bfef..efd425e 100644 --- a/src/helpers/expert-reply-pricing.spec.ts +++ b/src/helpers/expert-reply-pricing.spec.ts @@ -33,6 +33,20 @@ describe("getExpertReplyPricingValidationError", () => { ).toBeNull(); }); + it("accepts a repair line without daghi", () => { + expect( + getExpertReplyPricingValidationError([ + { + partId: 201, + typeOfDamage: TypeOfDamage.Repair, + price: "10000", + salary: "0", + totalPayment: "10000", + }, + ]), + ).toBeNull(); + }); + it("rejects a repair line without a price", () => { expect( getExpertReplyPricingValidationError([ diff --git a/src/helpers/expert-reply-pricing.ts b/src/helpers/expert-reply-pricing.ts index 796171b..c32d318 100644 --- a/src/helpers/expert-reply-pricing.ts +++ b/src/helpers/expert-reply-pricing.ts @@ -42,9 +42,11 @@ export function getExpertReplyPricingValidationError( return `${label} requires typeOfDamage to be either '${TypeOfDamage.Repair}' or '${TypeOfDamage.Change}'.`; } + const daghi = part.daghi; if ( - !part.daghi || - !Object.values(DaghiOption).includes(part.daghi.option as DaghiOption) + part.typeOfDamage === TypeOfDamage.Change && + (!daghi || + !Object.values(DaghiOption).includes(daghi.option as DaghiOption)) ) { return `${label} requires a valid daghi option.`; } @@ -52,7 +54,7 @@ export function getExpertReplyPricingValidationError( const price = parseMoneyAmountToman(part.price); const salary = parseMoneyAmountToman(part.salary); const totalPayment = parseMoneyAmountToman(part.totalPayment); - const daghiPrice = parseMoneyAmountToman(part.daghi.price); + const daghiPrice = parseMoneyAmountToman(daghi?.price); const hasPrice = part.price != null && (typeof part.price !== "string" || part.price.trim() !== ""); @@ -80,7 +82,7 @@ export function getExpertReplyPricingValidationError( } if ( - part.daghi.option === DaghiOption.RECYCLED_PARTS_VALUE && + daghi?.option === DaghiOption.RECYCLED_PARTS_VALUE && (daghiPrice === null || daghiPrice < REPAIR_LINE_AMOUNT_TOMAN.MIN || daghiPrice > REPAIR_LINE_AMOUNT_TOMAN.MAX)