forked from Yara724/api
Compare commits
11 Commits
e06178f804
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ac185c861 | ||
| f07e510c5f | |||
|
|
8b65e43c60 | ||
| 9dd0ec0c00 | |||
| a9adad3c1b | |||
| bbe5b7b77b | |||
| 1e13bb3a3c | |||
| a8d8df574b | |||
| cf07a33951 | |||
| 6f8a6fed69 | |||
| dbf46cfdda |
@@ -461,6 +461,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</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>
|
<h2>Flow V1 / V2 — User-Initiated Blame & Claim</h2>
|
||||||
<p class="section-intro">
|
<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>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 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 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>
|
<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>
|
</table>
|
||||||
</div>
|
</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 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 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 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-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 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>
|
<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 {
|
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_green_card",
|
CAR_GREEN_CARD = "car_green_card",
|
||||||
CAR_CERTIFICATE = "car_certificate",
|
CAR_CERTIFICATE = "car_certificate",
|
||||||
@@ -34,4 +37,3 @@ export enum CarAngle {
|
|||||||
LEFT = "left",
|
LEFT = "left",
|
||||||
RIGHT = "right",
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -66,15 +66,15 @@ import {
|
|||||||
toFanavaranHullExpertiseDmgSection,
|
toFanavaranHullExpertiseDmgSection,
|
||||||
toFanavaranHullExpertisePayload,
|
toFanavaranHullExpertisePayload,
|
||||||
vehicleCurrentValueFromHullPolicyRecord,
|
vehicleCurrentValueFromHullPolicyRecord,
|
||||||
|
FANAVARAN_DEFAULT_HULL_REPAIR_DURATION,
|
||||||
} from "./fanavaran-hull-expertise";
|
} from "./fanavaran-hull-expertise";
|
||||||
import {
|
import {
|
||||||
asHullDmgAccessoryRows,
|
|
||||||
FANAVARAN_DEFAULT_HULL_DMG_COST_KIND_ID,
|
FANAVARAN_DEFAULT_HULL_DMG_COST_KIND_ID,
|
||||||
FANAVARAN_DEFAULT_HULL_DMG_KIND_ID,
|
FANAVARAN_DEFAULT_HULL_DMG_KIND_ID,
|
||||||
|
FANAVARAN_DEFAULT_HULL_VEHICLE_ACCESSORY_ID,
|
||||||
FANAVARAN_HULL_DMG_COST_KIND_CAPTION,
|
FANAVARAN_HULL_DMG_COST_KIND_CAPTION,
|
||||||
FANAVARAN_HULL_DMG_KIND_CAPTION,
|
FANAVARAN_HULL_DMG_KIND_CAPTION,
|
||||||
findLookupIdByCaption,
|
findLookupIdByCaption,
|
||||||
resolveVehicleHullAccessoryId,
|
|
||||||
} from "./fanavaran-hull-expertise-lookups";
|
} from "./fanavaran-hull-expertise-lookups";
|
||||||
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
||||||
import { CreateClaimFromBlameResponseDto } from "./dto/create-claim-v2.dto";
|
import { CreateClaimFromBlameResponseDto } from "./dto/create-claim-v2.dto";
|
||||||
@@ -1639,7 +1639,9 @@ export class ClaimRequestManagementService {
|
|||||||
private getRequiredDocumentTypes(
|
private getRequiredDocumentTypes(
|
||||||
claimRequest: any,
|
claimRequest: any,
|
||||||
): ClaimRequiredDocumentType[] {
|
): ClaimRequiredDocumentType[] {
|
||||||
const allTypes = Object.values(ClaimRequiredDocumentType);
|
const allTypes = Object.values(ClaimRequiredDocumentType).filter(
|
||||||
|
(type) => type !== ClaimRequiredDocumentType.ACCIDENT_SKETCH,
|
||||||
|
);
|
||||||
|
|
||||||
// Check if it's CAR_BODY type
|
// Check if it's CAR_BODY type
|
||||||
const isCarBody = claimRequest.blameFile?.type === "CAR_BODY";
|
const isCarBody = claimRequest.blameFile?.type === "CAR_BODY";
|
||||||
@@ -4480,7 +4482,7 @@ export class ClaimRequestManagementService {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const [dmgKindRows, dmgCostKindRows, policyRecord, accessoriesRaw] =
|
const [dmgKindRows, dmgCostKindRows, policyRecord] =
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.getFanavaranLookupRows(input.clientKey, "vehicle-hull-dmg-kind"),
|
this.getFanavaranLookupRows(input.clientKey, "vehicle-hull-dmg-kind"),
|
||||||
this.getFanavaranLookupRows(
|
this.getFanavaranLookupRows(
|
||||||
@@ -4494,13 +4496,6 @@ export class ClaimRequestManagementService {
|
|||||||
contractOptions,
|
contractOptions,
|
||||||
)
|
)
|
||||||
: Promise.resolve(null),
|
: Promise.resolve(null),
|
||||||
policyId != null
|
|
||||||
? this.fanavaranLookupService.vehicleHullDmgAccessoriesByPolicyId(
|
|
||||||
input.clientKey,
|
|
||||||
policyId,
|
|
||||||
contractOptions,
|
|
||||||
)
|
|
||||||
: Promise.resolve([]),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const dmgKindId =
|
const dmgKindId =
|
||||||
@@ -4512,13 +4507,6 @@ export class ClaimRequestManagementService {
|
|||||||
FANAVARAN_HULL_DMG_COST_KIND_CAPTION,
|
FANAVARAN_HULL_DMG_COST_KIND_CAPTION,
|
||||||
) ?? FANAVARAN_DEFAULT_HULL_DMG_COST_KIND_ID;
|
) ?? FANAVARAN_DEFAULT_HULL_DMG_COST_KIND_ID;
|
||||||
|
|
||||||
const accessories = asHullDmgAccessoryRows(accessoriesRaw);
|
|
||||||
if (policyId != null && accessories.length === 0) {
|
|
||||||
input.warnings.push(
|
|
||||||
`No hull dmg-accessories for policy ${policyId}; VehicleHullAccessoryId may be missing.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let vehicle = pickHullVehicleIdentity(input.blame);
|
let vehicle = pickHullVehicleIdentity(input.blame);
|
||||||
let colorId: number | null = null;
|
let colorId: number | null = null;
|
||||||
const policy = asObjectRecord(policyRecord);
|
const policy = asObjectRecord(policyRecord);
|
||||||
@@ -4562,16 +4550,6 @@ export class ClaimRequestManagementService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const hullSections = input.parts.map((part: any) => {
|
const hullSections = input.parts.map((part: any) => {
|
||||||
const accessoryKindId = parseCatalogPartIdInput(part?.partId);
|
|
||||||
const vehicleHullAccessoryId = resolveVehicleHullAccessoryId(
|
|
||||||
accessories,
|
|
||||||
accessoryKindId,
|
|
||||||
);
|
|
||||||
if (vehicleHullAccessoryId == null) {
|
|
||||||
input.warnings.push(
|
|
||||||
`No VehicleHullAccessoryId for part "${this.fanavaranPartLabel(part)}" (AccessoryKindId=${accessoryKindId ?? "none"}).`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return toFanavaranHullExpertiseDmgSection({
|
return toFanavaranHullExpertiseDmgSection({
|
||||||
partId: part?.partId,
|
partId: part?.partId,
|
||||||
desc: this.fanavaranPartLabel(part),
|
desc: this.fanavaranPartLabel(part),
|
||||||
@@ -4581,7 +4559,7 @@ export class ClaimRequestManagementService {
|
|||||||
this.parseFanavaranMoney(part?.salary),
|
this.parseFanavaranMoney(part?.salary),
|
||||||
dmgKindId,
|
dmgKindId,
|
||||||
dmgCostKindId,
|
dmgCostKindId,
|
||||||
vehicleHullAccessoryId,
|
vehicleHullAccessoryId: FANAVARAN_DEFAULT_HULL_VEHICLE_ACCESSORY_ID,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -4594,10 +4572,7 @@ export class ClaimRequestManagementService {
|
|||||||
vehicleCurrentValue = vehicleCurrentValueFromHullPolicyRecord(policy);
|
vehicleCurrentValue = vehicleCurrentValueFromHullPolicyRecord(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
const repairDuration =
|
const repairDuration = FANAVARAN_DEFAULT_HULL_REPAIR_DURATION;
|
||||||
typeof input.profile.defaults.HullExpertiseRepairDuration === "number"
|
|
||||||
? input.profile.defaults.HullExpertiseRepairDuration
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
replyKey: input.replyKey,
|
replyKey: input.replyKey,
|
||||||
@@ -7898,7 +7873,18 @@ export class ClaimRequestManagementService {
|
|||||||
this.logger.log(
|
this.logger.log(
|
||||||
`[Fanavaran hull sync] PUT ${url} keys=${Object.keys(hull).join(",")}`,
|
`[Fanavaran hull sync] PUT ${url} keys=${Object.keys(hull).join(",")}`,
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
await this.putFanavaranJson(url, hull, clientKey, undefined, claimCaseId);
|
await this.putFanavaranJson(url, hull, clientKey, undefined, claimCaseId);
|
||||||
|
} catch (error) {
|
||||||
|
// Parsian bonyan often has no PUT route on vehicle-hull-claims/{Id}
|
||||||
|
// ("No HTTP resource was found.../vehicle-hull-claims/{Id}"). Do not
|
||||||
|
// block GEN.05 files / GEN.06 expertise on that.
|
||||||
|
this.logger.warn(
|
||||||
|
`[Fanavaran hull sync] PUT ${url} failed; continuing with nested stages: ${
|
||||||
|
error instanceof Error ? error.message : error
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async postFanavaranMultipart(
|
private async postFanavaranMultipart(
|
||||||
@@ -10348,7 +10334,8 @@ export class ClaimRequestManagementService {
|
|||||||
if (!catalogByKey.has(item.key)) catalogByKey.set(item.key, item);
|
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
|
const blameRequest = claimCase.blameRequestId
|
||||||
? await this.blameRequestDbService.findById(
|
? await this.blameRequestDbService.findById(
|
||||||
claimCase.blameRequestId.toString(),
|
claimCase.blameRequestId.toString(),
|
||||||
@@ -10356,53 +10343,68 @@ export class ClaimRequestManagementService {
|
|||||||
: null;
|
: null;
|
||||||
const isCarBody = blameRequest?.type === BlameRequestType.CAR_BODY;
|
const isCarBody = blameRequest?.type === BlameRequestType.CAR_BODY;
|
||||||
const requiredDocsDefinition = [
|
const requiredDocsDefinition = [
|
||||||
|
{
|
||||||
|
key: ClaimRequiredDocumentType.ACCIDENT_SKETCH,
|
||||||
|
label_fa: "کروکی",
|
||||||
|
label_en: "Accident Sketch",
|
||||||
|
category: "general",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "damaged_driving_license_front",
|
key: "damaged_driving_license_front",
|
||||||
label_fa: "گواهینامه طرف آسیبدیده - جلو",
|
label_fa: "گواهینامه طرف آسیبدیده - جلو",
|
||||||
label_en: "Damaged Party License - Front",
|
label_en: "Damaged Party License - Front",
|
||||||
category: "damaged_party",
|
category: "damaged_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "damaged_driving_license_back",
|
key: "damaged_driving_license_back",
|
||||||
label_fa: "گواهینامه طرف آسیبدیده - پشت",
|
label_fa: "گواهینامه طرف آسیبدیده - پشت",
|
||||||
label_en: "Damaged Party License - Back",
|
label_en: "Damaged Party License - Back",
|
||||||
category: "damaged_party",
|
category: "damaged_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "damaged_chassis_number",
|
key: "damaged_chassis_number",
|
||||||
label_fa: "شماره شاسی",
|
label_fa: "شماره شاسی",
|
||||||
label_en: "Chassis Number",
|
label_en: "Chassis Number",
|
||||||
category: "damaged_party",
|
category: "damaged_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "damaged_engine_photo",
|
key: "damaged_engine_photo",
|
||||||
label_fa: "عکس موتور",
|
label_fa: "عکس موتور",
|
||||||
label_en: "Engine Photo",
|
label_en: "Engine Photo",
|
||||||
category: "damaged_party",
|
category: "damaged_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "damaged_car_card_front",
|
key: "damaged_car_card_front",
|
||||||
label_fa: "کارت خودرو آسیبدیده - جلو",
|
label_fa: "کارت خودرو آسیبدیده - جلو",
|
||||||
label_en: "Damaged Car Card - Front",
|
label_en: "Damaged Car Card - Front",
|
||||||
category: "damaged_party",
|
category: "damaged_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "damaged_car_card_back",
|
key: "damaged_car_card_back",
|
||||||
label_fa: "کارت خودرو آسیبدیده - پشت",
|
label_fa: "کارت خودرو آسیبدیده - پشت",
|
||||||
label_en: "Damaged Car Card - Back",
|
label_en: "Damaged Car Card - Back",
|
||||||
category: "damaged_party",
|
category: "damaged_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "damaged_metal_plate",
|
key: "damaged_metal_plate",
|
||||||
label_fa: "پلاک فلزی آسیبدیده",
|
label_fa: "پلاک فلزی آسیبدیده",
|
||||||
label_en: "Damaged Metal Plate",
|
label_en: "Damaged Metal Plate",
|
||||||
category: "damaged_party",
|
category: "damaged_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "car_green_card",
|
key: "car_green_card",
|
||||||
label_fa: "کارت سبز خودرو",
|
label_fa: "کارت سبز خودرو",
|
||||||
label_en: "Car Green Card",
|
label_en: "Car Green Card",
|
||||||
category: "damaged_party",
|
category: "damaged_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
...(isCarBody
|
...(isCarBody
|
||||||
? []
|
? []
|
||||||
@@ -10412,30 +10414,35 @@ export class ClaimRequestManagementService {
|
|||||||
label_fa: "گواهینامه طرف مقصر - جلو",
|
label_fa: "گواهینامه طرف مقصر - جلو",
|
||||||
label_en: "Guilty Party License - Front",
|
label_en: "Guilty Party License - Front",
|
||||||
category: "guilty_party",
|
category: "guilty_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "guilty_driving_license_back",
|
key: "guilty_driving_license_back",
|
||||||
label_fa: "گواهینامه طرف مقصر - پشت",
|
label_fa: "گواهینامه طرف مقصر - پشت",
|
||||||
label_en: "Guilty Party License - Back",
|
label_en: "Guilty Party License - Back",
|
||||||
category: "guilty_party",
|
category: "guilty_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "guilty_car_card_front",
|
key: "guilty_car_card_front",
|
||||||
label_fa: "کارت خودرو مقصر - جلو",
|
label_fa: "کارت خودرو مقصر - جلو",
|
||||||
label_en: "Guilty Car Card - Front",
|
label_en: "Guilty Car Card - Front",
|
||||||
category: "guilty_party",
|
category: "guilty_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "guilty_car_card_back",
|
key: "guilty_car_card_back",
|
||||||
label_fa: "کارت خودرو مقصر - پشت",
|
label_fa: "کارت خودرو مقصر - پشت",
|
||||||
label_en: "Guilty Car Card - Back",
|
label_en: "Guilty Car Card - Back",
|
||||||
category: "guilty_party",
|
category: "guilty_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "guilty_metal_plate",
|
key: "guilty_metal_plate",
|
||||||
label_fa: "پلاک فلزی مقصر",
|
label_fa: "پلاک فلزی مقصر",
|
||||||
label_en: "Guilty Metal Plate",
|
label_en: "Guilty Metal Plate",
|
||||||
category: "guilty_party",
|
category: "guilty_party",
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
];
|
];
|
||||||
@@ -10447,6 +10454,7 @@ export class ClaimRequestManagementService {
|
|||||||
label_fa: doc.label_fa,
|
label_fa: doc.label_fa,
|
||||||
label_en: doc.label_en,
|
label_en: doc.label_en,
|
||||||
category: doc.category,
|
category: doc.category,
|
||||||
|
required: doc.required,
|
||||||
uploaded: docData?.uploaded || false,
|
uploaded: docData?.uploaded || false,
|
||||||
preferUploadDuringCapture: isCapturePhaseDamagedPartyDocKey(doc.key),
|
preferUploadDuringCapture: isCapturePhaseDamagedPartyDocKey(doc.key),
|
||||||
};
|
};
|
||||||
@@ -10509,7 +10517,10 @@ export class ClaimRequestManagementService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Calculate progress
|
// Calculate progress
|
||||||
const documentsUploaded = requiredDocuments.filter(
|
const requiredDocumentItems = requiredDocuments.filter(
|
||||||
|
(d) => d.required,
|
||||||
|
);
|
||||||
|
const documentsUploaded = requiredDocumentItems.filter(
|
||||||
(d) => d.uploaded,
|
(d) => d.uploaded,
|
||||||
).length;
|
).length;
|
||||||
const anglesCaptured = carAngles.filter((a) => a.captured).length;
|
const anglesCaptured = carAngles.filter((a) => a.captured).length;
|
||||||
@@ -10527,7 +10538,7 @@ export class ClaimRequestManagementService {
|
|||||||
captureProgress.anglesTotal - captureProgress.anglesCaptured,
|
captureProgress.anglesTotal - captureProgress.anglesCaptured,
|
||||||
);
|
);
|
||||||
const postCaptureDocsRemaining = requiredDocuments.filter(
|
const postCaptureDocsRemaining = requiredDocuments.filter(
|
||||||
(d) => !d.preferUploadDuringCapture && !d.uploaded,
|
(d) => d.required && !d.preferUploadDuringCapture && !d.uploaded,
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
const totalRemaining =
|
const totalRemaining =
|
||||||
@@ -10547,7 +10558,7 @@ export class ClaimRequestManagementService {
|
|||||||
totalRemaining,
|
totalRemaining,
|
||||||
progress: {
|
progress: {
|
||||||
documentsUploaded,
|
documentsUploaded,
|
||||||
documentsTotal: requiredDocuments.length,
|
documentsTotal: requiredDocumentItems.length,
|
||||||
anglesCaptured,
|
anglesCaptured,
|
||||||
anglesTotal: carAngles.length,
|
anglesTotal: carAngles.length,
|
||||||
partsCaptured,
|
partsCaptured,
|
||||||
@@ -10710,6 +10721,8 @@ export class ClaimRequestManagementService {
|
|||||||
|
|
||||||
const step = claimCase.workflow?.currentStep;
|
const step = claimCase.workflow?.currentStep;
|
||||||
const isResendUpload = step === ClaimWorkflowStep.USER_EXPERT_RESEND;
|
const isResendUpload = step === ClaimWorkflowStep.USER_EXPERT_RESEND;
|
||||||
|
const isOptionalDocument =
|
||||||
|
body.documentKey === ClaimRequiredDocumentType.ACCIDENT_SKETCH;
|
||||||
const isCapturePhaseDocUpload =
|
const isCapturePhaseDocUpload =
|
||||||
step === ClaimWorkflowStep.CAPTURE_PART_DAMAGES &&
|
step === ClaimWorkflowStep.CAPTURE_PART_DAMAGES &&
|
||||||
(isCapturePhaseDamagedPartyDocKey(body.documentKey) ||
|
(isCapturePhaseDamagedPartyDocKey(body.documentKey) ||
|
||||||
@@ -10740,7 +10753,9 @@ export class ClaimRequestManagementService {
|
|||||||
}
|
}
|
||||||
const allowedInitialUploadStep =
|
const allowedInitialUploadStep =
|
||||||
step === ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS ||
|
step === ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS ||
|
||||||
isCapturePhaseDocUpload;
|
isCapturePhaseDocUpload ||
|
||||||
|
(isOptionalDocument &&
|
||||||
|
step === ClaimWorkflowStep.USER_SUBMISSION_COMPLETE);
|
||||||
if (!allowedInitialUploadStep) {
|
if (!allowedInitialUploadStep) {
|
||||||
throw new BadRequestException(
|
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}`,
|
`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}`,
|
||||||
@@ -10859,7 +10874,7 @@ export class ClaimRequestManagementService {
|
|||||||
let remaining = 0;
|
let remaining = 0;
|
||||||
let captureStepCompletedOnThisUpload = false;
|
let captureStepCompletedOnThisUpload = false;
|
||||||
|
|
||||||
if (!isResendUpload) {
|
if (!isResendUpload && !isOptionalDocument) {
|
||||||
if (isCapturePhaseDocUpload) {
|
if (isCapturePhaseDocUpload) {
|
||||||
const afterThis = (k: string) =>
|
const afterThis = (k: string) =>
|
||||||
k === body.documentKey ||
|
k === body.documentKey ||
|
||||||
@@ -10997,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
|
// Build the final MongoDB update — all operators explicit and clean
|
||||||
const updatePayload: Record<string, unknown> = { $set };
|
const updatePayload: Record<string, unknown> = { $set };
|
||||||
|
|
||||||
@@ -11101,7 +11138,12 @@ export class ClaimRequestManagementService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auto-submit Fanavaran attachments when all documents are uploaded
|
// 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(
|
this.submitFanavaranAttachmentsV2(claimRequestId, resolveFanavaranClientKey()).catch(
|
||||||
(err) => this.logger.warn(`Fanavaran attachments auto-submit failed: ${err?.message}`),
|
(err) => this.logger.warn(`Fanavaran attachments auto-submit failed: ${err?.message}`),
|
||||||
);
|
);
|
||||||
@@ -11141,7 +11183,9 @@ export class ClaimRequestManagementService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = isCapturePhaseDocUpload
|
const message = isOptionalDocument
|
||||||
|
? "Optional accident sketch uploaded successfully."
|
||||||
|
: isCapturePhaseDocUpload
|
||||||
? captureStepCompletedOnThisUpload
|
? captureStepCompletedOnThisUpload
|
||||||
? options?.v3InPersonFlow
|
? options?.v3InPersonFlow
|
||||||
? "Capture-phase documents and all damage captures are complete. Upload the walk-around video (car-capture) to finalise."
|
? "Capture-phase documents and all damage captures are complete. Upload the walk-around video (car-capture) to finalise."
|
||||||
@@ -11164,7 +11208,9 @@ export class ClaimRequestManagementService {
|
|||||||
documentKey: body.documentKey,
|
documentKey: body.documentKey,
|
||||||
fileUrl,
|
fileUrl,
|
||||||
allDocumentsUploaded,
|
allDocumentsUploaded,
|
||||||
currentStep: isCapturePhaseDocUpload
|
currentStep: isOptionalDocument
|
||||||
|
? step || ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS
|
||||||
|
: isCapturePhaseDocUpload
|
||||||
? captureStepCompletedOnThisUpload
|
? captureStepCompletedOnThisUpload
|
||||||
? ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS
|
? ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS
|
||||||
: ClaimWorkflowStep.CAPTURE_PART_DAMAGES
|
: ClaimWorkflowStep.CAPTURE_PART_DAMAGES
|
||||||
@@ -12921,7 +12967,10 @@ export class ClaimRequestManagementService {
|
|||||||
const preCaptureDocs = base.requiredDocuments.filter(
|
const preCaptureDocs = base.requiredDocuments.filter(
|
||||||
(d) => !d.preferUploadDuringCapture,
|
(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 {
|
return {
|
||||||
...base,
|
...base,
|
||||||
requiredDocuments: preCaptureDocs,
|
requiredDocuments: preCaptureDocs,
|
||||||
@@ -12933,8 +12982,9 @@ export class ClaimRequestManagementService {
|
|||||||
totalRemaining: remaining,
|
totalRemaining: remaining,
|
||||||
progress: {
|
progress: {
|
||||||
...base.progress,
|
...base.progress,
|
||||||
documentsTotal: preCaptureDocs.length,
|
documentsTotal: requiredPreCaptureDocs.length,
|
||||||
documentsUploaded: preCaptureDocs.filter((d) => d.uploaded).length,
|
documentsUploaded: requiredPreCaptureDocs.filter((d) => d.uploaded)
|
||||||
|
.length,
|
||||||
partsCaptured: 0,
|
partsCaptured: 0,
|
||||||
partsTotal: 0,
|
partsTotal: 0,
|
||||||
anglesCaptured: 0,
|
anglesCaptured: 0,
|
||||||
@@ -12989,6 +13039,7 @@ export class ClaimRequestManagementService {
|
|||||||
label_fa: "تصویر نقطه آسیبدیده مقصر",
|
label_fa: "تصویر نقطه آسیبدیده مقصر",
|
||||||
label_en: "Guilty Car Damaged Area",
|
label_en: "Guilty Car Damaged Area",
|
||||||
category: "guilty_party",
|
category: "guilty_party",
|
||||||
|
required: true,
|
||||||
uploaded: guiltyAreaUploaded,
|
uploaded: guiltyAreaUploaded,
|
||||||
preferUploadDuringCapture: true,
|
preferUploadDuringCapture: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -801,6 +801,7 @@ Returns status of each item (uploaded/captured or not).
|
|||||||
**Workflow Step:** UPLOAD_REQUIRED_DOCUMENTS (Step 5 of Claim)
|
**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):
|
**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_driving_license_front/back
|
||||||
- damaged_chassis_number, damaged_engine_photo
|
- damaged_chassis_number, damaged_engine_photo
|
||||||
- damaged_car_card_front/back, damaged_metal_plate
|
- 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_front",
|
||||||
"guilty_car_card_back",
|
"guilty_car_card_back",
|
||||||
"guilty_metal_plate",
|
"guilty_metal_plate",
|
||||||
|
"accident_sketch",
|
||||||
],
|
],
|
||||||
example: "damaged_driving_license_front",
|
example: "damaged_driving_license_front",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ export class RequiredDocumentItem {
|
|||||||
example: true,
|
example: true,
|
||||||
})
|
})
|
||||||
preferUploadDuringCapture?: boolean;
|
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_front",
|
||||||
"guilty_car_card_back",
|
"guilty_car_card_back",
|
||||||
"guilty_metal_plate",
|
"guilty_metal_plate",
|
||||||
|
"accident_sketch",
|
||||||
],
|
],
|
||||||
example: "damaged_driving_license_front",
|
example: "damaged_driving_license_front",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ export const FANAVARAN_HULL_DMG_COST_KIND_CAPTION = "مجموع لوازم";
|
|||||||
export const FANAVARAN_DEFAULT_HULL_DMG_KIND_ID = 5485;
|
export const FANAVARAN_DEFAULT_HULL_DMG_KIND_ID = 5485;
|
||||||
/** Fallback when lookup fetch fails (`vehicle-hull-dmg-cost-kinds` Id). */
|
/** Fallback when lookup fetch fails (`vehicle-hull-dmg-cost-kinds` Id). */
|
||||||
export const FANAVARAN_DEFAULT_HULL_DMG_COST_KIND_ID = 1;
|
export const FANAVARAN_DEFAULT_HULL_DMG_COST_KIND_ID = 1;
|
||||||
|
/** GEN.06 factory (فابریک) accessory row. Used for every hull DmgSection. */
|
||||||
|
export const FANAVARAN_DEFAULT_HULL_VEHICLE_ACCESSORY_ID = 3043330;
|
||||||
|
|
||||||
export type FanavaranHullDmgAccessoryRow = {
|
export type FanavaranHullDmgAccessoryRow = {
|
||||||
Id?: unknown;
|
Id?: unknown;
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ export {
|
|||||||
FANAVARAN_HULL_DMG_KIND_CAPTION,
|
FANAVARAN_HULL_DMG_KIND_CAPTION,
|
||||||
} from "./fanavaran-hull-expertise-lookups";
|
} from "./fanavaran-hull-expertise-lookups";
|
||||||
|
|
||||||
|
/** GEN.06 RepairDuration in days for every hull expertise. */
|
||||||
|
export const FANAVARAN_DEFAULT_HULL_REPAIR_DURATION = 3;
|
||||||
|
|
||||||
/** GEN.06 VehicleCurrentValue from GET car/vehicle-hull-policies/{PolicyId} → VehicleValue. */
|
/** GEN.06 VehicleCurrentValue from GET car/vehicle-hull-policies/{PolicyId} → VehicleValue. */
|
||||||
export function vehicleCurrentValueFromHullPolicyRecord(
|
export function vehicleCurrentValueFromHullPolicyRecord(
|
||||||
policy: Record<string, unknown> | null | undefined,
|
policy: Record<string, unknown> | null | undefined,
|
||||||
@@ -117,7 +120,8 @@ export function toFanavaranHullExpertisePayload(input: {
|
|||||||
ComponentReplacementCost: input.componentReplacementCost,
|
ComponentReplacementCost: input.componentReplacementCost,
|
||||||
WasteValue: input.wasteValue,
|
WasteValue: input.wasteValue,
|
||||||
CarryAndRescueCost: null,
|
CarryAndRescueCost: null,
|
||||||
RepairDuration: input.repairDuration ?? null,
|
RepairDuration:
|
||||||
|
input.repairDuration ?? FANAVARAN_DEFAULT_HULL_REPAIR_DURATION,
|
||||||
VehicleCurrentValue: input.vehicleCurrentValue,
|
VehicleCurrentValue: input.vehicleCurrentValue,
|
||||||
WreckHighestValue: null,
|
WreckHighestValue: null,
|
||||||
InspectionDeduction: null,
|
InspectionDeduction: null,
|
||||||
|
|||||||
@@ -373,6 +373,7 @@ Returns status of each item (uploaded/captured or not).
|
|||||||
"guilty_car_card_front",
|
"guilty_car_card_front",
|
||||||
"guilty_car_card_back",
|
"guilty_car_card_back",
|
||||||
"guilty_metal_plate",
|
"guilty_metal_plate",
|
||||||
|
"accident_sketch",
|
||||||
],
|
],
|
||||||
example: "damaged_driving_license_front",
|
example: "damaged_driving_license_front",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog";
|
|||||||
import { buildCoefficientsFromPartSeverities } from "./claim-price-drop";
|
import { buildCoefficientsFromPartSeverities } from "./claim-price-drop";
|
||||||
import {
|
import {
|
||||||
catalogPartIdFromSelectedPart,
|
catalogPartIdFromSelectedPart,
|
||||||
|
normalizeDamageSelectedParts,
|
||||||
resolveCatalogPartByPartId,
|
resolveCatalogPartByPartId,
|
||||||
resolvePartForExpertReply,
|
resolvePartForExpertReply,
|
||||||
resolveSelectedPartByPartId,
|
resolveSelectedPartByPartId,
|
||||||
@@ -86,6 +87,31 @@ describe("resolveSelectedPartByPartId", () => {
|
|||||||
expect(catalogPartIdFromSelectedPart(row)).toBeNull();
|
expect(catalogPartIdFromSelectedPart(row)).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("preserves a live Fanavaran id that is newer than the static snapshot", () => {
|
||||||
|
const [row] = normalizeDamageSelectedParts(
|
||||||
|
[{
|
||||||
|
id: 96,
|
||||||
|
name: "96",
|
||||||
|
side: "",
|
||||||
|
label_fa: "تیوتر",
|
||||||
|
catalogKey: "96",
|
||||||
|
}],
|
||||||
|
ClaimVehicleTypeV2.SEDAN,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(row).toEqual({
|
||||||
|
id: 96,
|
||||||
|
name: "96",
|
||||||
|
side: "",
|
||||||
|
label_fa: "تیوتر",
|
||||||
|
catalogKey: "96",
|
||||||
|
});
|
||||||
|
expect(catalogPartIdFromSelectedPart(row)).toBe(96);
|
||||||
|
expect(resolvePartForExpertReply(96, [row], ClaimVehicleTypeV2.SEDAN)).toBe(
|
||||||
|
row,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("recovers outer part wrongly stored as internal with Persian label", () => {
|
it("recovers outer part wrongly stored as internal with Persian label", () => {
|
||||||
const fixed = sanitizeDamageSelectedPartV2(
|
const fixed = sanitizeDamageSelectedPartV2(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -466,6 +466,28 @@ export function sanitizeDamageSelectedPartV2(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fanavaran serves this catalog live and can add ids that do not exist in
|
||||||
|
// our fallback snapshot yet. Live rows are persisted with their numeric id
|
||||||
|
// repeated as `name`/`catalogKey` and an empty side. Preserve that identity
|
||||||
|
// instead of downgrading the row to an internal free-text part. Otherwise a
|
||||||
|
// newly-added Fanavaran part (for example id 96) disappears from the expert
|
||||||
|
// reply payload and later fails with PART_NOT_ON_CLAIM.
|
||||||
|
const numericCatalogIdentity =
|
||||||
|
id != null &&
|
||||||
|
Number.isFinite(id) &&
|
||||||
|
Number(id) > 0 &&
|
||||||
|
sideTrim === "" &&
|
||||||
|
(nameTrim === String(id) || String(catalogKey ?? "").trim() === String(id));
|
||||||
|
if (numericCatalogIdentity) {
|
||||||
|
return {
|
||||||
|
id: Number(id),
|
||||||
|
name: String(id),
|
||||||
|
side: "",
|
||||||
|
label_fa: labelTrim || nameTrim || String(id),
|
||||||
|
catalogKey: String(id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (isInternalDamageSide(side)) {
|
if (isInternalDamageSide(side)) {
|
||||||
return {
|
return {
|
||||||
id: null,
|
id: null,
|
||||||
|
|||||||
Reference in New Issue
Block a user