1
0
forked from Yara724/api

Fixed 3 upload documents for capture part

This commit is contained in:
SepehrYahyaee
2026-05-16 11:16:08 +03:30
parent 2c851725a5
commit 7c76149c95
6 changed files with 339 additions and 91 deletions

View File

@@ -120,16 +120,13 @@ import {
resolvePartCaptureIndex,
} from "src/helpers/outer-damage-parts";
/** Same `requiredDocuments` keys as in getCaptureRequirementsV2; upload allowed during CAPTURE_PART_DAMAGES. */
const CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS = [
"damaged_chassis_number",
"damaged_engine_photo",
"damaged_metal_plate",
] as const;
function isCapturePhaseDamagedPartyDocKey(key: string): boolean {
return (CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS as readonly string[]).includes(key);
}
import {
CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS,
capturePhaseSequenceMessage,
getClaimCaptureProgress,
isCapturePhaseDamagedPartyDocKey,
isClaimCaptureStepComplete,
} from "src/helpers/claim-capture-phase";
@Injectable()
export class ClaimRequestManagementService {
@@ -203,12 +200,6 @@ export class ClaimRequestManagementService {
return !!doc?.uploaded;
}
private capturePhaseDamagedPartyDocsComplete(claimCase: any): boolean {
return CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS.every((k) =>
this.isRequiredDocumentUploadedOnClaim(claimCase, k),
);
}
private allV2OwnerDocumentsComplete(
claimCase: any,
isCarBody: boolean,
@@ -4633,15 +4624,31 @@ export class ClaimRequestManagementService {
const anglesCaptured = carAngles.filter(a => a.captured).length;
const partsCaptured = damagedParts.filter(p => p.captured).length;
const totalRemaining =
(requiredDocuments.length - documentsUploaded) +
(carAngles.length - anglesCaptured) +
(damagedParts.length - partsCaptured);
const captureProgress = getClaimCaptureProgress(claimCase);
const capturePhaseDocsRemaining = captureProgress.capturePhaseDocsRemaining;
const partsRemaining = Math.max(
0,
captureProgress.partsTotal - captureProgress.partsCaptured,
);
const anglesRemaining = Math.max(
0,
captureProgress.anglesTotal - captureProgress.anglesCaptured,
);
const postCaptureDocsRemaining = requiredDocuments.filter(
(d) => !d.preferUploadDuringCapture && !d.uploaded,
).length;
const totalRemaining =
partsRemaining + anglesRemaining + capturePhaseDocsRemaining;
return {
claimRequestId: claimCase._id.toString(),
publicId: claimCase.publicId,
currentStep: claimCase.workflow?.currentStep || '',
captureSequencePhase: captureProgress.sequencePhase,
captureSequenceHint: capturePhaseSequenceMessage(
captureProgress.sequencePhase,
),
requiredDocuments,
carAngles,
damagedParts,
@@ -4653,6 +4660,8 @@ export class ClaimRequestManagementService {
anglesTotal: carAngles.length,
partsCaptured,
partsTotal: damagedParts.length,
capturePhaseDocsRemaining,
postCaptureDocumentsRemaining: postCaptureDocsRemaining,
},
};
} catch (error) {
@@ -4796,9 +4805,22 @@ export class ClaimRequestManagementService {
if (!isResendUpload) {
if (step === ClaimWorkflowStep.CAPTURE_PART_DAMAGES && !isCapturePhaseDocUpload) {
throw new BadRequestException(
`During ${ClaimWorkflowStep.CAPTURE_PART_DAMAGES} only chassis, engine, and damaged metal plate photos may be uploaded here (${CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS.join(", ")}).`,
`During ${ClaimWorkflowStep.CAPTURE_PART_DAMAGES} only chassis, engine, and damaged metal plate photos may be uploaded here (${CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS.join(", ")}), and only after all damaged parts and car angles are captured.`,
);
}
if (isCapturePhaseDocUpload) {
const captureProgress = getClaimCaptureProgress(claimCase);
if (!captureProgress.partsComplete) {
throw new BadRequestException(
"Upload photos for all selected damaged parts before uploading chassis, engine, or metal plate photos.",
);
}
if (!captureProgress.anglesComplete) {
throw new BadRequestException(
"Capture all four car angles (front, back, left, right) before uploading chassis, engine, or metal plate photos.",
);
}
}
const allowedInitialUploadStep =
step === ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS || isCapturePhaseDocUpload;
if (!allowedInitialUploadStep) {
@@ -4896,31 +4918,14 @@ export class ClaimRequestManagementService {
allDocumentsUploaded = false;
if (remaining === 0) {
// After this upload all 3 capture-phase docs will be on the claim.
// Check angles + parts captures using the in-memory claim media.
const carAnglesData = (claimCase.media as any)?.carAngles;
const damagedPartsData = (claimCase.media as any)?.damagedParts;
const selectedNorm = normalizeDamageSelectedParts(
claimCase.damage?.selectedParts,
claimCase.vehicle?.carType as ClaimVehicleTypeV2,
(claimCase.damage as any)?.selectedOuterParts,
);
const anglesKeys = ["front", "back", "left", "right"];
const anglesCaptured = anglesKeys.filter((k) =>
hasClaimCarAngleCapture(
carAnglesData,
damagedPartsData,
k as ClaimCarAngleKey,
),
).length;
const partsCaptured = selectedNorm.filter((sp) => {
const ck = sp.catalogKey ?? catalogLikeKeyFromPart(sp);
return hasDamagedPartCapture(damagedPartsData, ck, selectedNorm);
}).length;
const progressAfterDoc = getClaimCaptureProgress(claimCase, {
assumeCapturePhaseDocKey: body.documentKey,
});
if (
anglesCaptured >= 4 &&
partsCaptured >= selectedNorm.length
progressAfterDoc.partsComplete &&
progressAfterDoc.anglesComplete &&
progressAfterDoc.capturePhaseDocsComplete
) {
captureStepCompletedOnThisUpload = true;
updateData["status"] = ClaimCaseStatus.UPLOADING_REQUIRED_DOCUMENTS;
@@ -5031,8 +5036,12 @@ export class ClaimRequestManagementService {
? captureStepCompletedOnThisUpload
? "Capture-phase documents and all damage captures are complete. Please proceed to upload the remaining required documents."
: remaining > 0
? `Document saved. ${remaining} capture-phase document(s) still required (chassis, engine, metal plate) before you can finish damage capture.`
: "Document saved. All capture-phase documents are uploaded; finish car angles and part photos to proceed."
? `Document saved. ${remaining} capture-phase document(s) still required (chassis, engine, metal plate).`
: capturePhaseSequenceMessage(
getClaimCaptureProgress(claimCase, {
assumeCapturePhaseDocKey: body.documentKey,
}).sequencePhase,
)
: allDocumentsUploaded
? "All documents uploaded successfully. Your claim is now ready for damage expert review."
: `Document uploaded successfully. ${remaining} documents remaining.`;
@@ -5114,6 +5123,19 @@ export class ClaimRequestManagementService {
}
}
if (!isResendCapture) {
const captureProgressBefore = getClaimCaptureProgress(claimCase);
const willBeAngle =
body.captureType === "angle" ||
(body.captureType === "part" &&
canonicalizeClaimCarAngleKey(body.captureKey) !== null);
if (willBeAngle && !captureProgressBefore.partsComplete) {
throw new BadRequestException(
"Upload photos for all selected damaged parts before capturing car angles.",
);
}
}
const angleCanon = canonicalizeClaimCarAngleKey(body.captureKey);
if (body.captureType === "angle" && !angleCanon) {
throw new BadRequestException(
@@ -5269,40 +5291,8 @@ export class ClaimRequestManagementService {
};
}
const hasCapture = (data: any, key: string) =>
data && (data instanceof Map ? data.get(key) : data[key]);
// Use same logic as getCaptureRequirementsV2 to determine completion
const anglesKeys = ["front", "back", "left", "right"];
const carAnglesData = updatedClaim?.media?.carAngles as any;
const damagedPartsData = updatedClaim?.media?.damagedParts as any;
const selectedNormAfter = normalizeDamageSelectedParts(
updatedClaim?.damage?.selectedParts,
updatedClaim?.vehicle?.carType as ClaimVehicleTypeV2,
(updatedClaim?.damage as any)?.selectedOuterParts,
);
const anglesCaptured = anglesKeys.filter((k) =>
hasClaimCarAngleCapture(
carAnglesData,
damagedPartsData,
k as ClaimCarAngleKey,
),
).length;
const partsCaptured = selectedNormAfter.filter((sp) => {
const ck = sp.catalogKey ?? catalogLikeKeyFromPart(sp);
return hasDamagedPartCapture(
damagedPartsData,
ck,
selectedNormAfter,
);
}).length;
const capturePhaseDocsDone = this.capturePhaseDamagedPartyDocsComplete(updatedClaim);
const allCapturesComplete =
anglesCaptured >= 4 &&
partsCaptured >= selectedNormAfter.length &&
capturePhaseDocsDone;
const captureProgress = getClaimCaptureProgress(updatedClaim);
const allCapturesComplete = isClaimCaptureStepComplete(updatedClaim);
if (allCapturesComplete) {
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
@@ -5329,16 +5319,21 @@ export class ClaimRequestManagementService {
});
}
const captureDocsRemaining = CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS.filter(
(k) => !this.isRequiredDocumentUploadedOnClaim(updatedClaim, k),
).length;
const partsRemaining = Math.max(
0,
captureProgress.partsTotal - captureProgress.partsCaptured,
);
const anglesRemaining = Math.max(
0,
captureProgress.anglesTotal - captureProgress.anglesCaptured,
);
const remaining =
(4 - anglesCaptured) +
(selectedNormAfter.length - partsCaptured) +
captureDocsRemaining;
partsRemaining +
anglesRemaining +
captureProgress.capturePhaseDocsRemaining;
const message = allCapturesComplete
? 'All captures complete. Please proceed to upload required documents.'
: `${body.captureType === 'angle' ? 'Angle' : 'Part'} captured successfully. ${remaining} items remaining (angles, parts, and capture-phase documents).`;
? "All captures complete. Please proceed to upload the remaining required documents."
: `${body.captureType === "angle" ? "Angle" : "Part"} captured successfully. ${capturePhaseSequenceMessage(captureProgress.sequencePhase)} (${remaining} item(s) left in this step).`;
return {
claimRequestId: claimCase._id.toString(),
@@ -5393,6 +5388,18 @@ export class ClaimRequestManagementService {
);
}
const captureProgress = getClaimCaptureProgress(claimCase);
if (!captureProgress.partsComplete) {
throw new BadRequestException(
"Upload photos for all selected damaged parts before uploading the walk-around video.",
);
}
if (!captureProgress.anglesComplete) {
throw new BadRequestException(
"Capture all four car angles (front, back, left, right) before uploading the walk-around video.",
);
}
if (claimCase.media?.videoCaptureId) {
throw new ConflictException('A video has already been uploaded for this claim.');
}