forked from Yara724/api
Compare commits
6 Commits
01c1be40c7
...
07cd4776e9
| Author | SHA1 | Date | |
|---|---|---|---|
| 07cd4776e9 | |||
|
|
ab1e64c87b | ||
| dd6ee8ee20 | |||
|
|
cb23455bcd | ||
| d528af5c1d | |||
|
|
793dc52640 |
@@ -244,6 +244,32 @@ interface FanavaranAttachmentCandidate {
|
|||||||
submittedFileId?: unknown;
|
submittedFileId?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns today's Jalali date as a packed integer YYYYMMDD for cheap comparison.
|
||||||
|
* Uses the browser/Node `fa-IR` locale which is available everywhere Node runs.
|
||||||
|
*/
|
||||||
|
function jalaliDateToInt(d: Date): number {
|
||||||
|
const parts = d
|
||||||
|
.toLocaleDateString("fa-IR", { year: "numeric", month: "2-digit", day: "2-digit" })
|
||||||
|
.split("/")
|
||||||
|
.map((p) => parseInt(p.replace(/[۰-۹]/g, (c) => String(c.charCodeAt(0) - 0x06f0)), 10));
|
||||||
|
return parts[0] * 10000 + parts[1] * 100 + parts[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a Jalali date string in "YYYY/MM/DD" or "YYYY-MM-DD" form to the same
|
||||||
|
* packed integer YYYYMMDD. Returns null when the string cannot be parsed.
|
||||||
|
*/
|
||||||
|
function jalaliStringToInt(raw: string): number | null {
|
||||||
|
const m = raw.match(/(\d{4})[\/\-](\d{1,2})[\/\-](\d{1,2})/);
|
||||||
|
if (!m) return null;
|
||||||
|
const y = parseInt(m[1], 10);
|
||||||
|
const mo = parseInt(m[2], 10);
|
||||||
|
const d = parseInt(m[3], 10);
|
||||||
|
if (!y || !mo || !d) return null;
|
||||||
|
return y * 10000 + mo * 100 + d;
|
||||||
|
}
|
||||||
|
|
||||||
const FANAVARAN_ACCIDENT_LOCATION_ADDRESS = "استان تهران شهر تهران";
|
const FANAVARAN_ACCIDENT_LOCATION_ADDRESS = "استان تهران شهر تهران";
|
||||||
const FANAVARAN_DEFAULT_ACCIDENT_CAUSE_ID = 6;
|
const FANAVARAN_DEFAULT_ACCIDENT_CAUSE_ID = 6;
|
||||||
const FANAVARAN_DEFAULT_ACCIDENT_LEVEL = 5456;
|
const FANAVARAN_DEFAULT_ACCIDENT_LEVEL = 5456;
|
||||||
@@ -577,6 +603,9 @@ export class ClaimRequestManagementService {
|
|||||||
async getOuterPartsCatalogV2(): Promise<OuterPartCatalogItemDto[]> {
|
async getOuterPartsCatalogV2(): Promise<OuterPartCatalogItemDto[]> {
|
||||||
const clientKey = resolveFanavaranClientKey();
|
const clientKey = resolveFanavaranClientKey();
|
||||||
const rows = await this.getFanavaranLookupRows(clientKey, "car-components");
|
const rows = await this.getFanavaranLookupRows(clientKey, "car-components");
|
||||||
|
// Build today's Jalali date as a comparable integer (YYYYMMDD) so we can
|
||||||
|
// filter expired rows without any Jalali→Gregorian conversion risk.
|
||||||
|
const todayJalaliInt = jalaliDateToInt(new Date());
|
||||||
return rows
|
return rows
|
||||||
.filter(
|
.filter(
|
||||||
(r): r is { Id: number; Caption: string } =>
|
(r): r is { Id: number; Caption: string } =>
|
||||||
@@ -585,6 +614,13 @@ export class ClaimRequestManagementService {
|
|||||||
typeof (r as any).Id === "number" &&
|
typeof (r as any).Id === "number" &&
|
||||||
typeof (r as any).Caption === "string",
|
typeof (r as any).Caption === "string",
|
||||||
)
|
)
|
||||||
|
.filter((r) => {
|
||||||
|
const toDate = (r as any).ToDate;
|
||||||
|
if (!toDate) return true;
|
||||||
|
const toDateInt = jalaliStringToInt(String(toDate));
|
||||||
|
if (toDateInt === null) return true;
|
||||||
|
return toDateInt >= todayJalaliInt;
|
||||||
|
})
|
||||||
.map((r) => ({ id: r.Id, label_fa: r.Caption }));
|
.map((r) => ({ id: r.Id, label_fa: r.Caption }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8198,12 +8234,8 @@ export class ClaimRequestManagementService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Validate outer parts haven't been selected yet.
|
// 4. Validate outer parts haven't been selected yet
|
||||||
// Skip this guard when FileMaker rejected the claim and the FileReviewer
|
|
||||||
// is updating (overwriting) the previous selection.
|
|
||||||
const isFileMakerRejectedRerun = !!(claimCase as any).fileMakerRejectedPendingReview;
|
|
||||||
if (
|
if (
|
||||||
!isFileMakerRejectedRerun &&
|
|
||||||
claimCase.damage?.selectedParts &&
|
claimCase.damage?.selectedParts &&
|
||||||
claimCase.damage.selectedParts.length > 0
|
claimCase.damage.selectedParts.length > 0
|
||||||
) {
|
) {
|
||||||
@@ -8270,9 +8302,7 @@ export class ClaimRequestManagementService {
|
|||||||
}));
|
}));
|
||||||
const selectedPartIds = selectedItems.map((p) => p.id);
|
const selectedPartIds = selectedItems.map((p) => p.id);
|
||||||
|
|
||||||
// 6. Update claim case with selected parts and move to next step.
|
// 6. Update claim case with selected parts and move to next step
|
||||||
// Also clear fileMakerRejectedPendingReview now that the reviewer has
|
|
||||||
// actively re-submitted outer parts.
|
|
||||||
const updatedClaim = await this.claimCaseDbService.findByIdAndUpdate(
|
const updatedClaim = await this.claimCaseDbService.findByIdAndUpdate(
|
||||||
claimRequestId,
|
claimRequestId,
|
||||||
{
|
{
|
||||||
@@ -8282,10 +8312,7 @@ export class ClaimRequestManagementService {
|
|||||||
status: ClaimCaseStatus.SELECTING_OTHER_PARTS,
|
status: ClaimCaseStatus.SELECTING_OTHER_PARTS,
|
||||||
"workflow.currentStep": ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
"workflow.currentStep": ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
||||||
"workflow.nextStep": ClaimWorkflowStep.CAPTURE_PART_DAMAGES,
|
"workflow.nextStep": ClaimWorkflowStep.CAPTURE_PART_DAMAGES,
|
||||||
$unset: {
|
$unset: { "damage.selectedOuterParts": "" },
|
||||||
"damage.selectedOuterParts": "",
|
|
||||||
"fileMakerRejectedPendingReview": "",
|
|
||||||
},
|
|
||||||
$push: {
|
$push: {
|
||||||
"workflow.completedSteps": ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
"workflow.completedSteps": ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
||||||
history: {
|
history: {
|
||||||
@@ -8410,11 +8437,8 @@ export class ClaimRequestManagementService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Validate other parts and bank info haven't been submitted yet.
|
// 4. Validate other parts and bank info haven't been submitted yet
|
||||||
// Skip when FileMaker rejected the claim — the FileReviewer can update bank info.
|
|
||||||
const isFileMakerRejectedRerun = !!(claimCase as any).fileMakerRejectedPendingReview;
|
|
||||||
if (
|
if (
|
||||||
!isFileMakerRejectedRerun &&
|
|
||||||
claimCase.money?.sheba &&
|
claimCase.money?.sheba &&
|
||||||
claimCase.money?.nationalCodeOfInsurer &&
|
claimCase.money?.nationalCodeOfInsurer &&
|
||||||
claimCase.workflow?.currentStep !== ClaimWorkflowStep.USER_EXPERT_RESEND
|
claimCase.workflow?.currentStep !== ClaimWorkflowStep.USER_EXPERT_RESEND
|
||||||
|
|||||||
@@ -10615,51 +10615,30 @@ export class RequestManagementService {
|
|||||||
const newRejectionCount = currentRejections + 1;
|
const newRejectionCount = currentRejections + 1;
|
||||||
const actorName = `${fileMaker.firstName || ""} ${fileMaker.lastName || ""}`.trim();
|
const actorName = `${fileMaker.firstName || ""} ${fileMaker.lastName || ""}`.trim();
|
||||||
|
|
||||||
// Steps that belong to the re-run assessment cycle.
|
|
||||||
// Pulled from completedSteps so workflow ordering guards pass on re-run.
|
|
||||||
const assessmentSteps = [
|
|
||||||
ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
|
||||||
ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
|
||||||
ClaimWorkflowStep.CAPTURE_PART_DAMAGES,
|
|
||||||
ClaimWorkflowStep.USER_SUBMISSION_COMPLETE,
|
|
||||||
];
|
|
||||||
|
|
||||||
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
|
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
|
||||||
$set: {
|
$set: {
|
||||||
status: ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT,
|
status: ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT,
|
||||||
claimStatus: ClaimStatus.NEEDS_REVISION,
|
claimStatus: ClaimStatus.NEEDS_REVISION,
|
||||||
fileMakerRejectionCount: newRejectionCount,
|
fileMakerRejectionCount: newRejectionCount,
|
||||||
fileMakerRejectionReason: reason ?? null,
|
fileMakerRejectionReason: reason ?? null,
|
||||||
// Land directly at SELECT_OUTER_PARTS: the FileReviewer sees the
|
"workflow.currentStep": ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
|
||||||
// existing parts pre-populated and can adjust or confirm them.
|
"workflow.nextStep": ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
|
||||||
"workflow.currentStep": ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
|
||||||
"workflow.nextStep": ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
|
||||||
"workflow.locked": false,
|
"workflow.locked": false,
|
||||||
// Flag consumed by selectOuterPartsV2 / selectOtherPartsV2 to allow
|
|
||||||
// overwriting previously submitted data instead of throwing ConflictException.
|
|
||||||
"fileMakerRejectedPendingReview": true,
|
|
||||||
},
|
},
|
||||||
// Clear stale lock/evaluation fields and media that must be re-captured.
|
// Clear stale lock fields and the owner-cycle evaluation data.
|
||||||
// Part selections (damage.selectedParts, damage.otherParts) are intentionally
|
// evaluation.damageExpertReply is intentionally preserved so the FileReviewer
|
||||||
// preserved as a pre-populated starting point for the FileReviewer to adjust.
|
// sees their previous prices / daghi / severity pre-populated in the claim
|
||||||
|
// detail and can adjust them. submitExpertReplyV2 overwrites it with $set.
|
||||||
|
// - assignedForReviewBy intentionally kept so the same reviewer
|
||||||
|
// is still scoped to this file on re-lock.
|
||||||
$unset: {
|
$unset: {
|
||||||
"workflow.lockedAt": "",
|
"workflow.lockedAt": "",
|
||||||
"workflow.expiredAt": "",
|
"workflow.expiredAt": "",
|
||||||
"workflow.lockedBy": "",
|
"workflow.lockedBy": "",
|
||||||
"workflow.preLockQueueSnapshot": "",
|
"workflow.preLockQueueSnapshot": "",
|
||||||
"evaluation.damageExpertReply": "",
|
|
||||||
"evaluation.damageExpertResend": "",
|
"evaluation.damageExpertResend": "",
|
||||||
"evaluation.ownerInsurerApproval": "",
|
"evaluation.ownerInsurerApproval": "",
|
||||||
"evaluation.ownerPricedPartsApproval": "",
|
"evaluation.ownerPricedPartsApproval": "",
|
||||||
// Photos and walk-around video must be re-taken for the new part set.
|
|
||||||
"media.damagedParts": "",
|
|
||||||
"media.captures": "",
|
|
||||||
"media.videoCaptureId": "",
|
|
||||||
},
|
|
||||||
// Remove assessment-cycle steps from completedSteps so workflow ordering
|
|
||||||
// guards pass when the FileReviewer re-runs each step.
|
|
||||||
$pull: {
|
|
||||||
"workflow.completedSteps": { $in: assessmentSteps },
|
|
||||||
},
|
},
|
||||||
$push: {
|
$push: {
|
||||||
history: {
|
history: {
|
||||||
@@ -11102,6 +11081,9 @@ export class RequestManagementService {
|
|||||||
: { ...(req as any) };
|
: { ...(req as any) };
|
||||||
// Attach linked claim ID if present
|
// Attach linked claim ID if present
|
||||||
const claim = await this.claimCaseDbService.findOne({ blameRequestId: (req as any)._id });
|
const claim = await this.claimCaseDbService.findOne({ blameRequestId: (req as any)._id });
|
||||||
|
const claimPlain = claim && typeof (claim as any).toObject === "function"
|
||||||
|
? (claim as any).toObject({ versionKey: false })
|
||||||
|
: claim ? { ...(claim as any) } : null;
|
||||||
return {
|
return {
|
||||||
_id: plain._id,
|
_id: plain._id,
|
||||||
publicId: plain.publicId,
|
publicId: plain.publicId,
|
||||||
@@ -11136,7 +11118,19 @@ export class RequestManagementService {
|
|||||||
inquiryComplete: !!(p.person?.inquiriesCompleted),
|
inquiryComplete: !!(p.person?.inquiriesCompleted),
|
||||||
hasSigned: p.confirmation != null,
|
hasSigned: p.confirmation != null,
|
||||||
})),
|
})),
|
||||||
linkedClaimId: claim ? String((claim as any)._id) : null,
|
linkedClaimId: claimPlain ? String(claimPlain._id) : null,
|
||||||
|
...(claimPlain ? {
|
||||||
|
claimStatus: claimPlain.status,
|
||||||
|
claimWorkflow: claimPlain.workflow,
|
||||||
|
fileMakerRejectionCount: claimPlain.fileMakerRejectionCount ?? 0,
|
||||||
|
fileMakerRejectionReason: claimPlain.fileMakerRejectionReason ?? null,
|
||||||
|
evaluation: claimPlain.evaluation
|
||||||
|
? {
|
||||||
|
damageExpertReply: claimPlain.evaluation.damageExpertReply ?? null,
|
||||||
|
damageExpertReplyFinal: claimPlain.evaluation.damageExpertReplyFinal ?? null,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
} : {}),
|
||||||
createdAt: plain.createdAt,
|
createdAt: plain.createdAt,
|
||||||
updatedAt: plain.updatedAt,
|
updatedAt: plain.updatedAt,
|
||||||
};
|
};
|
||||||
@@ -11190,6 +11184,9 @@ export class RequestManagementService {
|
|||||||
? (req as any).toObject({ versionKey: false })
|
? (req as any).toObject({ versionKey: false })
|
||||||
: { ...(req as any) };
|
: { ...(req as any) };
|
||||||
const claim = await this.claimCaseDbService.findOne({ blameRequestId: (req as any)._id });
|
const claim = await this.claimCaseDbService.findOne({ blameRequestId: (req as any)._id });
|
||||||
|
const claimPlain = claim && typeof (claim as any).toObject === "function"
|
||||||
|
? (claim as any).toObject({ versionKey: false })
|
||||||
|
: claim ? { ...(claim as any) } : null;
|
||||||
return {
|
return {
|
||||||
_id: plain._id,
|
_id: plain._id,
|
||||||
publicId: plain.publicId,
|
publicId: plain.publicId,
|
||||||
@@ -11237,7 +11234,19 @@ export class RequestManagementService {
|
|||||||
} : undefined,
|
} : undefined,
|
||||||
accidentFields: plain.expert.accidentFields,
|
accidentFields: plain.expert.accidentFields,
|
||||||
} : undefined,
|
} : undefined,
|
||||||
linkedClaimId: claim ? String((claim as any)._id) : null,
|
linkedClaimId: claimPlain ? String(claimPlain._id) : null,
|
||||||
|
...(claimPlain ? {
|
||||||
|
claimStatus: claimPlain.status,
|
||||||
|
claimWorkflow: claimPlain.workflow,
|
||||||
|
fileMakerRejectionCount: claimPlain.fileMakerRejectionCount ?? 0,
|
||||||
|
fileMakerRejectionReason: claimPlain.fileMakerRejectionReason ?? null,
|
||||||
|
evaluation: claimPlain.evaluation
|
||||||
|
? {
|
||||||
|
damageExpertReply: claimPlain.evaluation.damageExpertReply ?? null,
|
||||||
|
damageExpertReplyFinal: claimPlain.evaluation.damageExpertReplyFinal ?? null,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
} : {}),
|
||||||
createdAt: plain.createdAt,
|
createdAt: plain.createdAt,
|
||||||
updatedAt: plain.updatedAt,
|
updatedAt: plain.updatedAt,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user