1
0
forked from Yara724/api

update the resend request and user side

This commit is contained in:
2026-04-19 17:23:14 +03:30
parent 08a4d754c1
commit fca88bc151
13 changed files with 521 additions and 80 deletions

View File

@@ -6,6 +6,7 @@ import {
IsBoolean,
IsOptional,
ValidateNested,
IsEnum,
} from 'class-validator';
import { Type } from 'class-transformer';
import { ClaimRequiredDocumentType } from 'src/Types&Enums/claim-request-management/required-document-type.enum';
@@ -72,12 +73,28 @@ export class SubmitExpertReplyV2Dto {
}
export class ClaimSubmitResendV2Dto {
@ApiProperty({ required: true })
resendDescription: string;
@ApiPropertyOptional({
description:
"Instructions for the owner. At least one of description, resendDocuments, or resendCarParts must be non-empty.",
})
@IsOptional()
@IsString()
resendDescription?: string;
@ApiProperty({ required: true, examples: ClaimRequiredDocumentType })
resendDocuments: ClaimRequiredDocumentType[];
@ApiPropertyOptional({
type: [String],
enum: ClaimRequiredDocumentType,
description: "Extra document keys to upload (may include types not in the initial claim form).",
})
@IsOptional()
@IsArray()
@IsEnum(ClaimRequiredDocumentType, { each: true })
resendDocuments?: ClaimRequiredDocumentType[];
@ApiProperty({ required: true, type: [DamagedPartItem] })
resendCarParts: DamagedPartItem[];
@ApiPropertyOptional({ type: [DamagedPartItem] })
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => DamagedPartItem)
resendCarParts?: DamagedPartItem[];
}

View File

@@ -58,6 +58,7 @@ import {
buildMutualAgreementExpertDecision,
enrichBlamePartiesForAgreementView,
} from "src/helpers/blame-party-agreement-decision";
import { resendRequestHasPayload } from "src/helpers/claim-expert-resend";
@Injectable()
export class ExpertClaimService {
@@ -2069,38 +2070,57 @@ export class ExpertClaimService {
throw new ForbiddenException('This claim is locked by another expert');
}
// Block duplicate resend while a prior request is still unfinished (stale EXPERT_REVIEWING + pending resend).
// After a successful submit, status becomes WAITING_FOR_USER_RESEND so this endpoint is not callable until the owner finishes.
const existingResend = claim.evaluation?.damageExpertResend;
const resendHasContent =
if (
existingResend &&
(String(existingResend.resendDescription || '').trim() !== '' ||
(existingResend.resendDocuments?.length ?? 0) > 0 ||
(existingResend.resendCarParts?.length ?? 0) > 0);
if (resendHasContent) {
throw new ConflictException('A resend request already exists for this claim');
!existingResend.fulfilledAt &&
resendRequestHasPayload(existingResend)
) {
throw new ConflictException(
"A resend request is still open for this claim. Wait until the owner completes uploads or contact support if the case is stuck.",
);
}
const docs = reply.resendDocuments ?? [];
const parts = reply.resendCarParts ?? [];
const desc = String(reply.resendDescription ?? "").trim();
if (!desc && docs.length === 0 && parts.length === 0) {
throw new BadRequestException(
"Provide resendDescription and/or resendDocuments and/or resendCarParts.",
);
}
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
$set: {
status: ClaimCaseStatus.WAITING_FOR_USER_RESEND,
claimStatus: ClaimStatus.NEEDS_REVISION,
'workflow.locked': false,
'evaluation.damageExpertResend': {
resendDescription: reply.resendDescription,
resendDocuments: reply.resendDocuments ?? [],
resendCarParts: reply.resendCarParts ?? [],
"workflow.locked": false,
"workflow.currentStep": ClaimWorkflowStep.USER_EXPERT_RESEND,
"workflow.nextStep": ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
"evaluation.damageExpertResend": {
resendDescription: desc || undefined,
resendDocuments: docs,
resendCarParts: parts,
},
},
$unset: {
"workflow.lockedAt": "",
"workflow.lockedBy": "",
},
$push: {
history: {
type: 'EXPERT_RESEND_REQUESTED',
type: "EXPERT_RESEND_REQUESTED",
actor: {
actorId: new Types.ObjectId(actor.sub),
actorName: actor.fullName,
actorType: 'damage_expert',
actorType: "damage_expert",
},
timestamp: new Date(),
metadata: {
documentCount: reply.resendDocuments?.length ?? 0,
carPartCount: reply.resendCarParts?.length ?? 0,
documentCount: docs.length,
carPartCount: parts.length,
},
},
},
@@ -2108,9 +2128,12 @@ export class ExpertClaimService {
return {
claimRequestId,
status: ClaimCaseStatus.WAITING_FOR_USER_RESEND,
claimStatus: ClaimStatus.NEEDS_REVISION,
currentStep: ClaimWorkflowStep.USER_EXPERT_RESEND,
nextStep: ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
message:
'Resend request recorded. The damaged party can review requirements and submit an objection or updated materials.',
"Resend request recorded. The owner must upload the requested documents and/or part photos (or confirm if only instructions were given). The claim will return to the damage expert queue when complete.",
};
}

View File

@@ -90,9 +90,10 @@ export class ExpertClaimV2Controller {
@Put("reply/resend/:claimRequestId")
@ApiOperation({
summary: "Submit expert damage resend request. ",
summary: "Submit expert damage resend request",
description:
"Claim must be locked by this expert (status EXPERT_REVIEWING). Submitting unlocks the claim. If any part has factorNeeded=true the claim moves to EXPERT_COST_EVALUATION; otherwise moves to INSURER_REVIEW. Total payment cap is 30,000,000.",
"Claim must be locked by this expert (`EXPERT_REVIEWING`). Sets `WAITING_FOR_USER_RESEND`, `USER_EXPERT_RESEND`, `NEEDS_REVISION`, clears the lock, and stores `evaluation.damageExpertResend`. " +
"The owner uploads via the same document/capture endpoints or `POST .../expert-resend/acknowledge` when only instructions were given.",
})
@ApiParam({ name: "claimRequestId" })
@ApiBody({ type: ClaimSubmitResendV2Dto })