forked from Yara724/api
Merge pull request 'feat(claims): support optional accident sketch' (#337) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#337
This commit is contained in:
@@ -461,6 +461,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">Optional document shared by every claim flow</div>
|
||||
<p class="step-note">
|
||||
Every V1–V6 document-upload step also offers
|
||||
<code>accident_sketch</code> (کروکی). It is optional for both
|
||||
<code>THIRD_PARTY</code> and <code>CAR_BODY</code>, may be omitted without
|
||||
blocking progression, and is returned in expert and insurer case details when uploaded.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════════ -->
|
||||
<h2>Flow V1 / V2 — User-Initiated Blame & Claim</h2>
|
||||
<p class="section-intro">
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
<tr><td><span class="method post">POST</span></td><td><code>run-inquiries/:id</code> / <code>run-inquiries-vin/:id</code></td><td>Run plate or VIN inquiry. First call = guilty (+ auto-creates claim). Second call = damaged (THIRD_PARTY only).</td></tr>
|
||||
<tr><td><span class="method post">POST</span></td><td><code>add-detail-location/:id</code> / <code>add-detail-description/:id</code> / <code>upload-voice/:id</code></td><td>Add location, description, and voice for current party (partyRole param selects FIRST/SECOND).</td></tr>
|
||||
<tr><td><span class="method put">PUT</span></td><td><code>sign/:id</code></td><td>Upload party signature (partyRole=FIRST then SECOND). After second signature, file is sealed.</td></tr>
|
||||
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>Upload licences / car cards against the auto-created claim.</td></tr>
|
||||
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>Upload licences / car cards and the optional <code>accident_sketch</code> (کروکی) against the auto-created claim. The sketch never gates completion.</td></tr>
|
||||
<tr><td><span class="method get">GET</span></td><td><code>capture-requirements/:claimId</code></td><td>Step-aware capture requirements (phases: pre-capture docs vs damaged parts + chassis/engine).</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -498,7 +498,7 @@
|
||||
<tr><td><span class="method get">GET</span></td><td><code>claim-id/:requestId</code></td><td>Get the auto-created claim ID (from FileMaker's guilty-party inquiry).</td></tr>
|
||||
<tr><td><span class="method post">POST</span></td><td><code>accident-fields/:requestId</code></td><td>Step 1 (FileReviewer): save accident fields (accidentWay, accidentReason, accidentType).</td></tr>
|
||||
<tr><td><span class="method get">GET</span></td><td><code>capture-requirements/:claimId</code></td><td>Step-aware capture requirements (pre-capture docs phase vs capture-parts phase).</td></tr>
|
||||
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>Upload chassis / engine / metal-plate documents.</td></tr>
|
||||
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>Upload chassis / engine / metal-plate documents; <code>accident_sketch</code> (کروکی) remains optional and does not affect completion.</td></tr>
|
||||
<tr><td><span class="method patch">PATCH</span></td><td><code>select-outer-parts/:claimId</code></td><td>Select outer (body) damaged parts.</td></tr>
|
||||
<tr><td><span class="method patch">PATCH</span></td><td><code>select-other-parts/:claimId</code></td><td>Select other (non-body) damaged parts.</td></tr>
|
||||
<tr><td><span class="method post">POST</span></td><td><code>capture-part/:claimId</code></td><td>Capture part photos + angles for each selected damaged part.</td></tr>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export enum ClaimRequiredDocumentType {
|
||||
/** Optional police accident sketch (Persian: کروکی). Never gates claim progress. */
|
||||
ACCIDENT_SKETCH = "accident_sketch",
|
||||
|
||||
// Car green card
|
||||
CAR_GREEN_CARD = "car_green_card",
|
||||
CAR_CERTIFICATE = "car_certificate",
|
||||
@@ -34,4 +37,3 @@ export enum CarAngle {
|
||||
LEFT = "left",
|
||||
RIGHT = "right",
|
||||
}
|
||||
|
||||
|
||||
45
src/claim-request-management/claim-optional-document.spec.ts
Normal file
45
src/claim-request-management/claim-optional-document.spec.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { ClaimRequiredDocumentType } from "src/Types&Enums/claim-request-management/required-document-type.enum";
|
||||
import { ClaimRequestManagementService } from "./claim-request-management.service";
|
||||
|
||||
describe("optional accident sketch", () => {
|
||||
const service = Object.create(
|
||||
ClaimRequestManagementService.prototype,
|
||||
) as ClaimRequestManagementService;
|
||||
|
||||
it("is excluded from legacy required-document completion", () => {
|
||||
const requiredTypes = (service as any).getRequiredDocumentTypes({
|
||||
blameFile: { type: "THIRD_PARTY" },
|
||||
});
|
||||
|
||||
expect(requiredTypes).not.toContain(
|
||||
ClaimRequiredDocumentType.ACCIDENT_SKETCH,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not gate V2 owner-document completion", () => {
|
||||
const mandatoryKeys = (service as any).requiredDocumentKeysV2(false);
|
||||
const requiredDocuments = Object.fromEntries(
|
||||
[...mandatoryKeys, ClaimRequiredDocumentType.CAR_GREEN_CARD].map(
|
||||
(key) => [key, { uploaded: true }],
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
(service as any).allV2OwnerDocumentsComplete(
|
||||
{ requiredDocuments },
|
||||
false,
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
requiredDocuments[ClaimRequiredDocumentType.ACCIDENT_SKETCH] = {
|
||||
uploaded: false,
|
||||
};
|
||||
|
||||
expect(
|
||||
(service as any).allV2OwnerDocumentsComplete(
|
||||
{ requiredDocuments },
|
||||
false,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1639,7 +1639,9 @@ export class ClaimRequestManagementService {
|
||||
private getRequiredDocumentTypes(
|
||||
claimRequest: any,
|
||||
): ClaimRequiredDocumentType[] {
|
||||
const allTypes = Object.values(ClaimRequiredDocumentType);
|
||||
const allTypes = Object.values(ClaimRequiredDocumentType).filter(
|
||||
(type) => type !== ClaimRequiredDocumentType.ACCIDENT_SKETCH,
|
||||
);
|
||||
|
||||
// Check if it's CAR_BODY type
|
||||
const isCarBody = claimRequest.blameFile?.type === "CAR_BODY";
|
||||
@@ -10332,7 +10334,8 @@ export class ClaimRequestManagementService {
|
||||
if (!catalogByKey.has(item.key)) catalogByKey.set(item.key, item);
|
||||
}
|
||||
|
||||
// Required documents: CAR_BODY needs 7 (damaged + green card); THIRD_PARTY needs 13 (add guilty_*)
|
||||
// Required documents vary by claim type. The accident sketch is offered
|
||||
// in every flow, but is excluded from every completion calculation.
|
||||
const blameRequest = claimCase.blameRequestId
|
||||
? await this.blameRequestDbService.findById(
|
||||
claimCase.blameRequestId.toString(),
|
||||
@@ -10340,53 +10343,68 @@ export class ClaimRequestManagementService {
|
||||
: null;
|
||||
const isCarBody = blameRequest?.type === BlameRequestType.CAR_BODY;
|
||||
const requiredDocsDefinition = [
|
||||
{
|
||||
key: ClaimRequiredDocumentType.ACCIDENT_SKETCH,
|
||||
label_fa: "کروکی",
|
||||
label_en: "Accident Sketch",
|
||||
category: "general",
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
key: "damaged_driving_license_front",
|
||||
label_fa: "گواهینامه طرف آسیبدیده - جلو",
|
||||
label_en: "Damaged Party License - Front",
|
||||
category: "damaged_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "damaged_driving_license_back",
|
||||
label_fa: "گواهینامه طرف آسیبدیده - پشت",
|
||||
label_en: "Damaged Party License - Back",
|
||||
category: "damaged_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "damaged_chassis_number",
|
||||
label_fa: "شماره شاسی",
|
||||
label_en: "Chassis Number",
|
||||
category: "damaged_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "damaged_engine_photo",
|
||||
label_fa: "عکس موتور",
|
||||
label_en: "Engine Photo",
|
||||
category: "damaged_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "damaged_car_card_front",
|
||||
label_fa: "کارت خودرو آسیبدیده - جلو",
|
||||
label_en: "Damaged Car Card - Front",
|
||||
category: "damaged_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "damaged_car_card_back",
|
||||
label_fa: "کارت خودرو آسیبدیده - پشت",
|
||||
label_en: "Damaged Car Card - Back",
|
||||
category: "damaged_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "damaged_metal_plate",
|
||||
label_fa: "پلاک فلزی آسیبدیده",
|
||||
label_en: "Damaged Metal Plate",
|
||||
category: "damaged_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "car_green_card",
|
||||
label_fa: "کارت سبز خودرو",
|
||||
label_en: "Car Green Card",
|
||||
category: "damaged_party",
|
||||
required: true,
|
||||
},
|
||||
...(isCarBody
|
||||
? []
|
||||
@@ -10396,30 +10414,35 @@ export class ClaimRequestManagementService {
|
||||
label_fa: "گواهینامه طرف مقصر - جلو",
|
||||
label_en: "Guilty Party License - Front",
|
||||
category: "guilty_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "guilty_driving_license_back",
|
||||
label_fa: "گواهینامه طرف مقصر - پشت",
|
||||
label_en: "Guilty Party License - Back",
|
||||
category: "guilty_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "guilty_car_card_front",
|
||||
label_fa: "کارت خودرو مقصر - جلو",
|
||||
label_en: "Guilty Car Card - Front",
|
||||
category: "guilty_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "guilty_car_card_back",
|
||||
label_fa: "کارت خودرو مقصر - پشت",
|
||||
label_en: "Guilty Car Card - Back",
|
||||
category: "guilty_party",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "guilty_metal_plate",
|
||||
label_fa: "پلاک فلزی مقصر",
|
||||
label_en: "Guilty Metal Plate",
|
||||
category: "guilty_party",
|
||||
required: true,
|
||||
},
|
||||
]),
|
||||
];
|
||||
@@ -10431,6 +10454,7 @@ export class ClaimRequestManagementService {
|
||||
label_fa: doc.label_fa,
|
||||
label_en: doc.label_en,
|
||||
category: doc.category,
|
||||
required: doc.required,
|
||||
uploaded: docData?.uploaded || false,
|
||||
preferUploadDuringCapture: isCapturePhaseDamagedPartyDocKey(doc.key),
|
||||
};
|
||||
@@ -10493,7 +10517,10 @@ export class ClaimRequestManagementService {
|
||||
});
|
||||
|
||||
// Calculate progress
|
||||
const documentsUploaded = requiredDocuments.filter(
|
||||
const requiredDocumentItems = requiredDocuments.filter(
|
||||
(d) => d.required,
|
||||
);
|
||||
const documentsUploaded = requiredDocumentItems.filter(
|
||||
(d) => d.uploaded,
|
||||
).length;
|
||||
const anglesCaptured = carAngles.filter((a) => a.captured).length;
|
||||
@@ -10511,7 +10538,7 @@ export class ClaimRequestManagementService {
|
||||
captureProgress.anglesTotal - captureProgress.anglesCaptured,
|
||||
);
|
||||
const postCaptureDocsRemaining = requiredDocuments.filter(
|
||||
(d) => !d.preferUploadDuringCapture && !d.uploaded,
|
||||
(d) => d.required && !d.preferUploadDuringCapture && !d.uploaded,
|
||||
).length;
|
||||
|
||||
const totalRemaining =
|
||||
@@ -10531,7 +10558,7 @@ export class ClaimRequestManagementService {
|
||||
totalRemaining,
|
||||
progress: {
|
||||
documentsUploaded,
|
||||
documentsTotal: requiredDocuments.length,
|
||||
documentsTotal: requiredDocumentItems.length,
|
||||
anglesCaptured,
|
||||
anglesTotal: carAngles.length,
|
||||
partsCaptured,
|
||||
@@ -10694,6 +10721,8 @@ export class ClaimRequestManagementService {
|
||||
|
||||
const step = claimCase.workflow?.currentStep;
|
||||
const isResendUpload = step === ClaimWorkflowStep.USER_EXPERT_RESEND;
|
||||
const isOptionalDocument =
|
||||
body.documentKey === ClaimRequiredDocumentType.ACCIDENT_SKETCH;
|
||||
const isCapturePhaseDocUpload =
|
||||
step === ClaimWorkflowStep.CAPTURE_PART_DAMAGES &&
|
||||
(isCapturePhaseDamagedPartyDocKey(body.documentKey) ||
|
||||
@@ -10724,7 +10753,9 @@ export class ClaimRequestManagementService {
|
||||
}
|
||||
const allowedInitialUploadStep =
|
||||
step === ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS ||
|
||||
isCapturePhaseDocUpload;
|
||||
isCapturePhaseDocUpload ||
|
||||
(isOptionalDocument &&
|
||||
step === ClaimWorkflowStep.USER_SUBMISSION_COMPLETE);
|
||||
if (!allowedInitialUploadStep) {
|
||||
throw new BadRequestException(
|
||||
`Invalid workflow step. Expected ${ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS}, ${ClaimWorkflowStep.CAPTURE_PART_DAMAGES} (capture-phase documents only), or ${ClaimWorkflowStep.USER_EXPERT_RESEND}, but current step is ${claimCase.workflow?.currentStep}`,
|
||||
@@ -10843,7 +10874,7 @@ export class ClaimRequestManagementService {
|
||||
let remaining = 0;
|
||||
let captureStepCompletedOnThisUpload = false;
|
||||
|
||||
if (!isResendUpload) {
|
||||
if (!isResendUpload && !isOptionalDocument) {
|
||||
if (isCapturePhaseDocUpload) {
|
||||
const afterThis = (k: string) =>
|
||||
k === body.documentKey ||
|
||||
@@ -10981,6 +11012,28 @@ export class ClaimRequestManagementService {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isResendUpload && isOptionalDocument) {
|
||||
allDocumentsUploaded = options?.v3InPersonFlow
|
||||
? this.allV3PreCaptureDocumentsComplete(
|
||||
claimCase,
|
||||
isCarBodyUpload,
|
||||
undefined,
|
||||
options?.skipMetalPlate,
|
||||
)
|
||||
: this.allV2OwnerDocumentsComplete(claimCase, isCarBodyUpload);
|
||||
remaining = options?.v3InPersonFlow
|
||||
? this.countRemainingV3PreCaptureDocuments(
|
||||
claimCase,
|
||||
isCarBodyUpload,
|
||||
undefined,
|
||||
options?.skipMetalPlate,
|
||||
)
|
||||
: this.countRemainingV2OwnerDocuments(
|
||||
claimCase,
|
||||
isCarBodyUpload,
|
||||
);
|
||||
}
|
||||
|
||||
// Build the final MongoDB update — all operators explicit and clean
|
||||
const updatePayload: Record<string, unknown> = { $set };
|
||||
|
||||
@@ -11085,7 +11138,12 @@ export class ClaimRequestManagementService {
|
||||
}
|
||||
|
||||
// Auto-submit Fanavaran attachments when all documents are uploaded
|
||||
if (allDocumentsUploaded && !isResendUpload && !options?.v3InPersonFlow) {
|
||||
if (
|
||||
allDocumentsUploaded &&
|
||||
!isOptionalDocument &&
|
||||
!isResendUpload &&
|
||||
!options?.v3InPersonFlow
|
||||
) {
|
||||
this.submitFanavaranAttachmentsV2(claimRequestId, resolveFanavaranClientKey()).catch(
|
||||
(err) => this.logger.warn(`Fanavaran attachments auto-submit failed: ${err?.message}`),
|
||||
);
|
||||
@@ -11125,7 +11183,9 @@ export class ClaimRequestManagementService {
|
||||
};
|
||||
}
|
||||
|
||||
const message = isCapturePhaseDocUpload
|
||||
const message = isOptionalDocument
|
||||
? "Optional accident sketch uploaded successfully."
|
||||
: isCapturePhaseDocUpload
|
||||
? captureStepCompletedOnThisUpload
|
||||
? options?.v3InPersonFlow
|
||||
? "Capture-phase documents and all damage captures are complete. Upload the walk-around video (car-capture) to finalise."
|
||||
@@ -11148,7 +11208,9 @@ export class ClaimRequestManagementService {
|
||||
documentKey: body.documentKey,
|
||||
fileUrl,
|
||||
allDocumentsUploaded,
|
||||
currentStep: isCapturePhaseDocUpload
|
||||
currentStep: isOptionalDocument
|
||||
? step || ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS
|
||||
: isCapturePhaseDocUpload
|
||||
? captureStepCompletedOnThisUpload
|
||||
? ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS
|
||||
: ClaimWorkflowStep.CAPTURE_PART_DAMAGES
|
||||
@@ -12905,7 +12967,10 @@ export class ClaimRequestManagementService {
|
||||
const preCaptureDocs = base.requiredDocuments.filter(
|
||||
(d) => !d.preferUploadDuringCapture,
|
||||
);
|
||||
const remaining = preCaptureDocs.filter((d) => !d.uploaded).length;
|
||||
const requiredPreCaptureDocs = preCaptureDocs.filter((d) => d.required);
|
||||
const remaining = requiredPreCaptureDocs.filter(
|
||||
(d) => !d.uploaded,
|
||||
).length;
|
||||
return {
|
||||
...base,
|
||||
requiredDocuments: preCaptureDocs,
|
||||
@@ -12917,8 +12982,9 @@ export class ClaimRequestManagementService {
|
||||
totalRemaining: remaining,
|
||||
progress: {
|
||||
...base.progress,
|
||||
documentsTotal: preCaptureDocs.length,
|
||||
documentsUploaded: preCaptureDocs.filter((d) => d.uploaded).length,
|
||||
documentsTotal: requiredPreCaptureDocs.length,
|
||||
documentsUploaded: requiredPreCaptureDocs.filter((d) => d.uploaded)
|
||||
.length,
|
||||
partsCaptured: 0,
|
||||
partsTotal: 0,
|
||||
anglesCaptured: 0,
|
||||
@@ -12973,6 +13039,7 @@ export class ClaimRequestManagementService {
|
||||
label_fa: "تصویر نقطه آسیبدیده مقصر",
|
||||
label_en: "Guilty Car Damaged Area",
|
||||
category: "guilty_party",
|
||||
required: true,
|
||||
uploaded: guiltyAreaUploaded,
|
||||
preferUploadDuringCapture: true,
|
||||
};
|
||||
|
||||
@@ -801,6 +801,7 @@ Returns status of each item (uploaded/captured or not).
|
||||
**Workflow Step:** UPLOAD_REQUIRED_DOCUMENTS (Step 5 of Claim)
|
||||
|
||||
**Upload one of the required documents** (12 for THIRD_PARTY; CAR_BODY may require fewer — see capture-requirements):
|
||||
- accident_sketch (optional; never blocks workflow completion)
|
||||
- damaged_driving_license_front/back
|
||||
- damaged_chassis_number, damaged_engine_photo
|
||||
- damaged_car_card_front/back, damaged_metal_plate
|
||||
@@ -836,6 +837,7 @@ Returns status of each item (uploaded/captured or not).
|
||||
"guilty_car_card_front",
|
||||
"guilty_car_card_back",
|
||||
"guilty_metal_plate",
|
||||
"accident_sketch",
|
||||
],
|
||||
example: "damaged_driving_license_front",
|
||||
},
|
||||
|
||||
@@ -42,6 +42,13 @@ export class RequiredDocumentItem {
|
||||
example: true,
|
||||
})
|
||||
preferUploadDuringCapture?: boolean;
|
||||
|
||||
@ApiProperty({
|
||||
description:
|
||||
'Whether this document is mandatory for workflow completion. Optional documents are uploadable but never included in remaining/progress counts.',
|
||||
example: true,
|
||||
})
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -416,6 +416,7 @@ Returns status of each item (uploaded/captured or not).
|
||||
"guilty_car_card_front",
|
||||
"guilty_car_card_back",
|
||||
"guilty_metal_plate",
|
||||
"accident_sketch",
|
||||
],
|
||||
example: "damaged_driving_license_front",
|
||||
},
|
||||
|
||||
@@ -373,6 +373,7 @@ Returns status of each item (uploaded/captured or not).
|
||||
"guilty_car_card_front",
|
||||
"guilty_car_card_back",
|
||||
"guilty_metal_plate",
|
||||
"accident_sketch",
|
||||
],
|
||||
example: "damaged_driving_license_front",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user