forked from Yara724/api
fix: query policies only for current holder
This commit is contained in:
@@ -159,6 +159,7 @@
|
|||||||
<div class="decision-tree">
|
<div class="decision-tree">
|
||||||
<strong>برای هر پرسوجوی مبتنی بر پلاک:</strong>
|
<strong>برای هر پرسوجوی مبتنی بر پلاک:</strong>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>درخواست همیشه با پلاک فعلی ارسالشده و بیمهگذار نهایی همان نوع بیمه انجام میشود. متادیتای انتقال اخیر هیچ استعلامی برای پلاک یا بیمهگذار قبلی ایجاد نمیکند.</li>
|
||||||
<li>۱. بررسی دادههای آفلاین (MongoDB) — اگر داده مطابق یافت شد، آن را برگردانده و تمام HTTP را رد کن.</li>
|
<li>۱. بررسی دادههای آفلاین (MongoDB) — اگر داده مطابق یافت شد، آن را برگردانده و تمام HTTP را رد کن.</li>
|
||||||
<li>۲. اگر <code>CLIENT_ID=8</code> (تنانت پارسیان/ESG) → مسیریابی به <strong>ESG</strong> <code>/inquiry/policyByPlate</code> یا <code>/inquiry/policyByChassis</code>.</li>
|
<li>۲. اگر <code>CLIENT_ID=8</code> (تنانت پارسیان/ESG) → مسیریابی به <strong>ESG</strong> <code>/inquiry/policyByPlate</code> یا <code>/inquiry/policyByChassis</code>.</li>
|
||||||
<li>۳. در غیر این صورت → مسیریابی به <strong>پرسوجوی تجارت</strong> <code>/block-inquiry-tejarat</code> (THIRD_PARTY) یا <code>/block-inquiry-tejarat/badane</code> (CAR_BODY).</li>
|
<li>۳. در غیر این صورت → مسیریابی به <strong>پرسوجوی تجارت</strong> <code>/block-inquiry-tejarat</code> (THIRD_PARTY) یا <code>/block-inquiry-tejarat/badane</code> (CAR_BODY).</li>
|
||||||
|
|||||||
@@ -154,6 +154,7 @@
|
|||||||
<div class="decision-tree">
|
<div class="decision-tree">
|
||||||
<strong>For every plate-based block inquiry:</strong>
|
<strong>For every plate-based block inquiry:</strong>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>The request always uses the submitted current plate and the resolved policyholder for that policy type. Recent-transfer metadata never triggers a previous-plate or previous-policyholder lookup.</li>
|
||||||
<li>1. Check offline-inquiry seeds (MongoDB) — if a matching seed exists, return it and skip all HTTP.</li>
|
<li>1. Check offline-inquiry seeds (MongoDB) — if a matching seed exists, return it and skip all HTTP.</li>
|
||||||
<li>2. If <code>CLIENT_ID=8</code> (Parsian/ESG tenant) → route to <strong>ESG</strong> <code>/inquiry/policyByPlate</code> or <code>/inquiry/policyByChassis</code>.</li>
|
<li>2. If <code>CLIENT_ID=8</code> (Parsian/ESG tenant) → route to <strong>ESG</strong> <code>/inquiry/policyByPlate</code> or <code>/inquiry/policyByChassis</code>.</li>
|
||||||
<li>3. Otherwise → route to <strong>Tejarat inquiry</strong> <code>/block-inquiry-tejarat</code> (THIRD_PARTY) or <code>/block-inquiry-tejarat/badane</code> (CAR_BODY).</li>
|
<li>3. Otherwise → route to <strong>Tejarat inquiry</strong> <code>/block-inquiry-tejarat</code> (THIRD_PARTY) or <code>/block-inquiry-tejarat/badane</code> (CAR_BODY).</li>
|
||||||
|
|||||||
@@ -157,7 +157,7 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
سیستم ابتدا پلاک فعلی را با کد ملی بیمهگذار فعلیِ مرتبط با نوع بیمه استعلام میکند. اگر نتیجه ناموجود، منقضی یا فاقد بیمهنامه مرتبط باشد، پلاک قبلی را با `previousPolicyholderNationalCode` امتحان میکند. نتیجه پلاک قبلی فقط در صورت تطبیق VIN پذیرفته میشود؛ پلاک قبلی هرگز جایگزین پلاک فعلی نمیشود.
|
اطلاعات انتقال اخیر فقط بهعنوان متادیتای پرونده ذخیره میشوند. در route پلاک، سیستم فقط `currentPlate` را با کد ملی بیمهگذار نهاییِ مرتبط با نوع بیمه استعلام میکند و هیچ fallbackای به `previousPlate` یا `previousPolicyholderNationalCode` ندارد. در route شماره شاسی نیز فقط `vehicle.vin` با همان بیمهگذار نهایی استعلام میشود.
|
||||||
|
|
||||||
حتی در route مربوط به VIN، آبجکت `vehicle` از قرارداد مشترک استفاده میکند و `currentPlate` در قرارداد فعلی الزامی است. مقدار VIN در `vehicle.vin` قرار میگیرد، نه در فیلد سطح بالای `vin`.
|
حتی در route مربوط به VIN، آبجکت `vehicle` از قرارداد مشترک استفاده میکند و `currentPlate` در قرارداد فعلی الزامی است. مقدار VIN در `vehicle.vin` قرار میگیرد، نه در فیلد سطح بالای `vin`.
|
||||||
|
|
||||||
|
|||||||
@@ -79,9 +79,9 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
مقدار پیشفرض `registrationState` برابر `CURRENT` است و برای این مسیر استثنایی مقدار `RECENTLY_TRANSFERRED` استفاده میشود. در انتقال اخیر، `previousPlate`، `previousPolicyholderNationalCode` و `vin` الزامیاند؛ فیلدهای مربوط به پلاک قبلی در حالت عادی `CURRENT` نباید ارسال شوند. پلاک فعلی همچنان شناسه اصلی خودرو است. هماهنگکننده استعلام ابتدا پلاک فعلی را با کد ملی بیمهگذار فعلی بررسی میکند و اگر نتیجه ناموجود، قدیمی یا فاقد بیمهنامه مرتبط بود، پلاک قبلی را با `previousPolicyholderNationalCode` استعلام میکند.
|
مقدار پیشفرض `registrationState` برابر `CURRENT` است و برای این مسیر استثنایی مقدار `RECENTLY_TRANSFERRED` استفاده میشود. در انتقال اخیر، `previousPlate`، `previousPolicyholderNationalCode` و `vin` الزامیاند؛ فیلدهای مربوط به پلاک قبلی در حالت عادی `CURRENT` نباید ارسال شوند. این اطلاعات انتقال فقط بهعنوان متادیتای پرونده نگهداری میشوند و پلاک فعلی همچنان شناسه اصلی خودرو است.
|
||||||
|
|
||||||
پیش از پذیرش نتیجه پلاک قبلی، بکاند باید یکسان بودن VIN/شماره شاسی را بررسی کند. در صورت مغایرت، انتخاب خودکار متوقف و اصلاح اطلاعات یا بررسی دستی الزامی شود. هر دو پلاک و تمام تلاشهای استعلام برای ممیزی نگهداری شوند، اما پلاک قبلی هیچگاه نباید روی پلاک فعلی نوشته شود.
|
در route پلاک، هماهنگکننده دقیقاً یک استعلام بیمه انجام میدهد: `currentPlate` همراه با بیمهگذار نهایی همان نوع بیمه. در route شماره شاسی نیز `vehicle.vin` با همان بیمهگذار نهایی ارسال میشود. `previousPlate` هیچگاه استعلام نمیشود، `previousPolicyholderNationalCode` به ارائهدهنده استعلام ارسال نمیشود و VIN برای انتخاب نتیجه پلاک قبلی به کار نمیرود.
|
||||||
|
|
||||||
## ترتیب پیشنهادی فرم
|
## ترتیب پیشنهادی فرم
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
- شخص نهایی هر نقش را برگرداند؛
|
- شخص نهایی هر نقش را برگرداند؛
|
||||||
- هویت درست را به استعلام مرتبط بدهد: گواهینامه ← راننده، مالکیت و تطبیق شبا ← مالک خودرو، بیمه شخص ثالث با پلاک/VIN ← بیمهگذار شخص ثالث، بیمه بدنه با پلاک/VIN ← بیمهگذار بدنه؛
|
- هویت درست را به استعلام مرتبط بدهد: گواهینامه ← راننده، مالکیت و تطبیق شبا ← مالک خودرو، بیمه شخص ثالث با پلاک/VIN ← بیمهگذار شخص ثالث، بیمه بدنه با پلاک/VIN ← بیمهگذار بدنه؛
|
||||||
- شبا را در استعلام شخص مطالبهکننده خسارت (`SECOND` زیاندیده در `THIRD_PARTY` و طرف اول در `CAR_BODY`) الزامی کند و با کد ملی مالک خودرو اعتبارسنجی کند؛ در استعلام `FIRST` مقصر پرونده ثالث شبا دریافت نمیشود؛
|
- شبا را در استعلام شخص مطالبهکننده خسارت (`SECOND` زیاندیده در `THIRD_PARTY` و طرف اول در `CAR_BODY`) الزامی کند و با کد ملی مالک خودرو اعتبارسنجی کند؛ در استعلام `FIRST` مقصر پرونده ثالث شبا دریافت نمیشود؛
|
||||||
- بر اساس یک قاعده مشخص، پلاک فعلی یا قبلی را انتخاب و نتیجه پلاک قبلی را با VIN/شماره شاسی تطبیق دهد؛
|
- فقط پلاک فعلی یا VIN ارسالشده را با بیمهگذار نهایی همان نوع بیمه استعلام کند؛ متادیتای انتقال قبلی نباید مسیریابی استعلام را تغییر دهد؛
|
||||||
- استعلام هویت را برای هر شخص یکتا فقط یک بار اجرا کند؛
|
- استعلام هویت را برای هر شخص یکتا فقط یک بار اجرا کند؛
|
||||||
- اشخاص نرمالشده و نقشهای آنها را در `Party` مربوط ذخیره کند.
|
- اشخاص نرمالشده و نقشهای آنها را در `Party` مربوط ذخیره کند.
|
||||||
- `participants` و `participantRoles` ذخیرهشده را بدون حذف اطلاعات در جزئیات پرونده پنلهای کارشناسی و پرونده خسارت متصل نمایش دهد تا اطلاعات راننده و سایر نقشها برای بررسی در دسترس بماند.
|
- `participants` و `participantRoles` ذخیرهشده را بدون حذف اطلاعات در جزئیات پرونده پنلهای کارشناسی و پرونده خسارت متصل نمایش دهد تا اطلاعات راننده و سایر نقشها برای بررسی در دسترس بماند.
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ Participant roles and vehicle identifiers are separate concerns. When a vehicle
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`registrationState` is `CURRENT` by default or `RECENTLY_TRANSFERRED` for this exceptional path. `previousPlate`, `previousPolicyholderNationalCode`, and `vin` are required when `registrationState=RECENTLY_TRANSFERRED`; the previous-plate fields are forbidden for the normal `CURRENT` path. The current plate remains the vehicle's primary identifier. The inquiry orchestrator queries the current plate with the current policyholder's national code first, then automatically tries the previous plate with `previousPolicyholderNationalCode` when the current result is missing, stale, or does not find the relevant policy.
|
`registrationState` is `CURRENT` by default or `RECENTLY_TRANSFERRED` for this exceptional path. `previousPlate`, `previousPolicyholderNationalCode`, and `vin` are required when `registrationState=RECENTLY_TRANSFERRED`; the previous-plate fields are forbidden for the normal `CURRENT` path. These transfer fields are retained only as case metadata, and the current plate remains the vehicle's primary identifier.
|
||||||
|
|
||||||
Before accepting a previous-plate result, the backend must correlate it to the same VIN/chassis. A mismatch must stop automatic selection and require correction or manual review. Both identifiers and every attempted inquiry should be retained for audit, but a previous plate must never overwrite the current plate.
|
For a plate route, the inquiry orchestrator performs exactly one policy lookup: `currentPlate` with the resolved policyholder for that policy type. For a VIN route, it uses `vehicle.vin` with the same resolved policyholder. It never queries `previousPlate`, never sends `previousPolicyholderNationalCode` to an inquiry provider, and does not use VIN to select a previous-plate result.
|
||||||
|
|
||||||
## Suggested UI sequence
|
## Suggested UI sequence
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ Create one shared participant resolver used by every inquiry route. Its interfac
|
|||||||
- return the resolved person for each role;
|
- return the resolved person for each role;
|
||||||
- route the correct identity to each inquiry: driver licence → Driver, ownership and Sheba validation → Vehicle Owner, third-party policy by plate/VIN → Third-party Policyholder, car-body policy by plate/VIN → Car-body Policyholder;
|
- route the correct identity to each inquiry: driver licence → Driver, ownership and Sheba validation → Vehicle Owner, third-party policy by plate/VIN → Third-party Policyholder, car-body policy by plate/VIN → Car-body Policyholder;
|
||||||
- require Sheba in the claimant inquiry (`THIRD_PARTY` damaged/SECOND party and `CAR_BODY` first party) and validate it against the resolved Vehicle Owner; the `THIRD_PARTY` guilty/FIRST inquiry does not collect Sheba;
|
- require Sheba in the claimant inquiry (`THIRD_PARTY` damaged/SECOND party and `CAR_BODY` first party) and validate it against the resolved Vehicle Owner; the `THIRD_PARTY` guilty/FIRST inquiry does not collect Sheba;
|
||||||
- choose the current or previous plate deterministically and verify previous-plate results against VIN/chassis;
|
- query only the submitted current plate or VIN with the resolved policyholder for that policy type; previous-transfer metadata must not affect inquiry routing;
|
||||||
- run personal identity inquiry once per distinct person;
|
- run personal identity inquiry once per distinct person;
|
||||||
- persist normalized participants and role assignments on the relevant `Party`.
|
- persist normalized participants and role assignments on the relevant `Party`.
|
||||||
- expose the persisted `participants` and `participantRoles` unchanged in expert-facing blame and linked-claim details so driver and other role data remain available for review.
|
- expose the persisted `participants` and `participantRoles` unchanged in expert-facing blame and linked-claim details so driver and other role data remain available for review.
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
VehicleRegistrationState,
|
VehicleRegistrationState,
|
||||||
} from "src/common/dto/inquiry-participants.dto";
|
} from "src/common/dto/inquiry-participants.dto";
|
||||||
import {
|
import {
|
||||||
assertPreviousPlateInquiryMatchesVin,
|
|
||||||
isMappedPolicyCurrent,
|
isMappedPolicyCurrent,
|
||||||
normalizeInquirySubmission,
|
normalizeInquirySubmission,
|
||||||
participantForRole,
|
participantForRole,
|
||||||
@@ -13,9 +12,8 @@ import {
|
|||||||
resolveInquiryParticipants,
|
resolveInquiryParticipants,
|
||||||
resolveInquirySubjects,
|
resolveInquirySubjects,
|
||||||
resolveInquiryVehicle,
|
resolveInquiryVehicle,
|
||||||
runPlateInquiryWithFallback,
|
runCurrentPlateInquiry,
|
||||||
sanitizeStoredInquiryParticipants,
|
sanitizeStoredInquiryParticipants,
|
||||||
vehiclePlateCandidates,
|
|
||||||
} from "./inquiry-participant-resolver";
|
} from "./inquiry-participant-resolver";
|
||||||
|
|
||||||
describe("inquiry participant resolver", () => {
|
describe("inquiry participant resolver", () => {
|
||||||
@@ -309,42 +307,6 @@ describe("inquiry participant resolver", () => {
|
|||||||
).toBe("0022222222");
|
).toBe("0022222222");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("orders the current plate before the previous-plate fallback", () => {
|
|
||||||
const currentPlate = {
|
|
||||||
leftDigits: "44",
|
|
||||||
centerAlphabet: "ب",
|
|
||||||
centerDigits: "111",
|
|
||||||
ir: "22",
|
|
||||||
};
|
|
||||||
const previousPlate = {
|
|
||||||
leftDigits: "55",
|
|
||||||
centerAlphabet: "ج",
|
|
||||||
centerDigits: "222",
|
|
||||||
ir: "33",
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(
|
|
||||||
vehiclePlateCandidates({
|
|
||||||
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
|
||||||
currentPlate,
|
|
||||||
previousPlate,
|
|
||||||
vin: "NAAM01E15HK123456",
|
|
||||||
previousPolicyholderNationalCode: "0098765432",
|
|
||||||
}),
|
|
||||||
).toEqual([
|
|
||||||
{ kind: "CURRENT", plate: currentPlate },
|
|
||||||
{ kind: "PREVIOUS", plate: previousPlate },
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("rejects a previous-plate result for another chassis", () => {
|
|
||||||
expect(() =>
|
|
||||||
assertPreviousPlateInquiryMatchesVin("NAAM01E15HK123456", {
|
|
||||||
VinNumberField: "DIFFERENTVIN00001",
|
|
||||||
}),
|
|
||||||
).toThrow(BadRequestException);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("requires the driver's licence status in the new contract", () => {
|
it("requires the driver's licence status in the new contract", () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||||||
@@ -357,58 +319,36 @@ describe("inquiry participant resolver", () => {
|
|||||||
).toThrow(BadRequestException);
|
).toThrow(BadRequestException);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("falls back to the previous plate and accepts only a matching VIN", async () => {
|
it("runs only one inquiry for the current plate", async () => {
|
||||||
const currentPlate = {
|
const currentPlate = {
|
||||||
leftDigits: "44",
|
leftDigits: "44",
|
||||||
centerAlphabet: "ب",
|
centerAlphabet: "ب",
|
||||||
centerDigits: "111",
|
centerDigits: "111",
|
||||||
ir: "22",
|
ir: "22",
|
||||||
};
|
};
|
||||||
const previousPlate = {
|
const query = jest.fn().mockResolvedValue({
|
||||||
leftDigits: "55",
|
mapped: { CompanyName: "پارسیان" },
|
||||||
centerAlphabet: "ج",
|
|
||||||
centerDigits: "222",
|
|
||||||
ir: "33",
|
|
||||||
};
|
|
||||||
const query = jest
|
|
||||||
.fn()
|
|
||||||
.mockRejectedValueOnce(new Error("not found"))
|
|
||||||
.mockResolvedValueOnce({
|
|
||||||
mapped: { VinNumberField: "NAAM01E15HK123456", CompanyName: "پارسیان" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await runPlateInquiryWithFallback<{
|
|
||||||
mapped: { VinNumberField?: string; CompanyName?: string };
|
|
||||||
}>({
|
|
||||||
vehicle: {
|
|
||||||
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
|
||||||
currentPlate,
|
|
||||||
previousPlate,
|
|
||||||
vin: "NAAM01E15HK123456",
|
|
||||||
previousPolicyholderNationalCode: "0098765432",
|
|
||||||
},
|
|
||||||
fallbackCurrentPlate: currentPlate,
|
|
||||||
query,
|
|
||||||
isUsable: (value) => !!value.mapped.CompanyName,
|
|
||||||
mappedValue: (value) => value.mapped,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(query).toHaveBeenCalledTimes(2);
|
const result = await runCurrentPlateInquiry<{
|
||||||
expect(query).toHaveBeenNthCalledWith(1, currentPlate, "CURRENT");
|
mapped: { CompanyName?: string };
|
||||||
expect(query).toHaveBeenNthCalledWith(2, previousPlate, "PREVIOUS");
|
}>({
|
||||||
expect(result.plateKind).toBe("PREVIOUS");
|
currentPlate,
|
||||||
|
vin: "NAAM01E15HK123456",
|
||||||
|
query,
|
||||||
|
isUsable: (value) => !!value.mapped.CompanyName,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(query).toHaveBeenCalledTimes(1);
|
||||||
|
expect(query).toHaveBeenCalledWith(currentPlate);
|
||||||
|
expect(result.plateKind).toBe("CURRENT");
|
||||||
expect(result.attempts).toMatchObject([
|
expect(result.attempts).toMatchObject([
|
||||||
{ plateKind: "CURRENT", succeeded: false, error: "not found" },
|
{ plateKind: "CURRENT", succeeded: true, usable: true },
|
||||||
{ plateKind: "PREVIOUS", succeeded: true, usable: true },
|
|
||||||
]);
|
]);
|
||||||
expect(result.attempts[0]).toMatchObject({
|
expect(result.attempts[0]).toMatchObject({
|
||||||
plate: currentPlate,
|
plate: currentPlate,
|
||||||
vin: "NAAM01E15HK123456",
|
vin: "NAAM01E15HK123456",
|
||||||
});
|
});
|
||||||
expect(result.attempts[1]).toMatchObject({
|
|
||||||
plate: previousPlate,
|
|
||||||
vin: "NAAM01E15HK123456",
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects a car-body policyholder on a third-party case", () => {
|
it("rejects a car-body policyholder on a third-party case", () => {
|
||||||
@@ -510,95 +450,52 @@ describe("inquiry participant resolver", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("falls back from a stale current policy to a current previous-plate policy", async () => {
|
it("rejects a stale current policy without trying another plate", async () => {
|
||||||
const currentPlate = {
|
const currentPlate = {
|
||||||
leftDigits: "44",
|
leftDigits: "44",
|
||||||
centerAlphabet: "ب",
|
centerAlphabet: "ب",
|
||||||
centerDigits: "111",
|
centerDigits: "111",
|
||||||
ir: "22",
|
ir: "22",
|
||||||
};
|
};
|
||||||
const previousPlate = {
|
const query = jest.fn().mockResolvedValue({
|
||||||
leftDigits: "55",
|
mapped: { CompanyName: "پارسیان", EndDate: "1404/01/01" },
|
||||||
centerAlphabet: "ج",
|
});
|
||||||
centerDigits: "222",
|
|
||||||
ir: "33",
|
|
||||||
};
|
|
||||||
const query = jest
|
|
||||||
.fn()
|
|
||||||
.mockResolvedValueOnce({
|
|
||||||
mapped: { CompanyName: "پارسیان", EndDate: "1404/01/01" },
|
|
||||||
})
|
|
||||||
.mockResolvedValueOnce({
|
|
||||||
mapped: {
|
|
||||||
CompanyName: "پارسیان",
|
|
||||||
EndDate: "1406/01/01",
|
|
||||||
VinNumberField: "NAAM01E15HK123456",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await runPlateInquiryWithFallback<{
|
await expect(
|
||||||
mapped: Record<string, any>;
|
runCurrentPlateInquiry<{ mapped: Record<string, any> }>({
|
||||||
}>({
|
|
||||||
vehicle: resolveInquiryVehicle({
|
|
||||||
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
|
||||||
currentPlate,
|
currentPlate,
|
||||||
previousPlate,
|
query,
|
||||||
vin: "NAAM01E15HK123456",
|
isUsable: (value) =>
|
||||||
previousPolicyholderNationalCode: "0098765432",
|
!!value.mapped.CompanyName &&
|
||||||
|
isMappedPolicyCurrent(value.mapped, "2026-09-13"),
|
||||||
}),
|
}),
|
||||||
fallbackCurrentPlate: currentPlate,
|
).rejects.toMatchObject({
|
||||||
query,
|
attempts: [{ plateKind: "CURRENT", succeeded: true, usable: false }],
|
||||||
isUsable: (value) =>
|
|
||||||
!!value.mapped.CompanyName &&
|
|
||||||
isMappedPolicyCurrent(value.mapped, "2026-09-13"),
|
|
||||||
mappedValue: (value) => value.mapped,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.plateKind).toBe("PREVIOUS");
|
|
||||||
expect(result.attempts[0]).toMatchObject({
|
|
||||||
plateKind: "CURRENT",
|
|
||||||
succeeded: true,
|
|
||||||
usable: false,
|
|
||||||
});
|
});
|
||||||
|
expect(query).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects the result and retains audit attempts when every plate is unusable", async () => {
|
it("retains the current-plate audit attempt when the result is unusable", async () => {
|
||||||
const currentPlate = {
|
const currentPlate = {
|
||||||
leftDigits: "44",
|
leftDigits: "44",
|
||||||
centerAlphabet: "ب",
|
centerAlphabet: "ب",
|
||||||
centerDigits: "111",
|
centerDigits: "111",
|
||||||
ir: "22",
|
ir: "22",
|
||||||
};
|
};
|
||||||
const previousPlate = {
|
|
||||||
leftDigits: "55",
|
|
||||||
centerAlphabet: "ج",
|
|
||||||
centerDigits: "222",
|
|
||||||
ir: "33",
|
|
||||||
};
|
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
runPlateInquiryWithFallback({
|
runCurrentPlateInquiry({
|
||||||
vehicle: resolveInquiryVehicle({
|
currentPlate,
|
||||||
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
vin: "NAAM01E15HK123456",
|
||||||
currentPlate,
|
|
||||||
previousPlate,
|
|
||||||
vin: "NAAM01E15HK123456",
|
|
||||||
previousPolicyholderNationalCode: "0098765432",
|
|
||||||
}),
|
|
||||||
fallbackCurrentPlate: currentPlate,
|
|
||||||
query: async () => ({ mapped: { CompanyName: "پارسیان" } }),
|
query: async () => ({ mapped: { CompanyName: "پارسیان" } }),
|
||||||
isUsable: () => false,
|
isUsable: () => false,
|
||||||
mappedValue: (value) => value.mapped,
|
|
||||||
}),
|
}),
|
||||||
).rejects.toMatchObject({
|
).rejects.toMatchObject({
|
||||||
attempts: [
|
attempts: [{ plateKind: "CURRENT", succeeded: true, usable: false }],
|
||||||
{ plateKind: "CURRENT", succeeded: true, usable: false },
|
|
||||||
{ plateKind: "PREVIOUS", succeeded: true, usable: false },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not use the previous plate after a transport or provider outage", async () => {
|
it("retains one failed current-plate attempt after a provider outage", async () => {
|
||||||
const currentPlate = {
|
const currentPlate = {
|
||||||
leftDigits: "44",
|
leftDigits: "44",
|
||||||
centerAlphabet: "ب",
|
centerAlphabet: "ب",
|
||||||
@@ -612,23 +509,11 @@ describe("inquiry participant resolver", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
runPlateInquiryWithFallback({
|
runCurrentPlateInquiry({
|
||||||
vehicle: resolveInquiryVehicle({
|
currentPlate,
|
||||||
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
vin: "NAAM01E15HK123456",
|
||||||
currentPlate,
|
|
||||||
previousPlate: {
|
|
||||||
leftDigits: "55",
|
|
||||||
centerAlphabet: "ج",
|
|
||||||
centerDigits: "222",
|
|
||||||
ir: "33",
|
|
||||||
},
|
|
||||||
vin: "NAAM01E15HK123456",
|
|
||||||
previousPolicyholderNationalCode: "0098765432",
|
|
||||||
}),
|
|
||||||
fallbackCurrentPlate: currentPlate,
|
|
||||||
query,
|
query,
|
||||||
isUsable: () => false,
|
isUsable: () => false,
|
||||||
mappedValue: () => ({}),
|
|
||||||
}),
|
}),
|
||||||
).rejects.toThrow("upstream timeout");
|
).rejects.toThrow("upstream timeout");
|
||||||
expect(query).toHaveBeenCalledTimes(1);
|
expect(query).toHaveBeenCalledTimes(1);
|
||||||
|
|||||||
@@ -393,26 +393,6 @@ export function resolveInquiryVehicle(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function vehiclePlateCandidates(input?: ResolvedInquiryVehicle): Array<{
|
|
||||||
kind: "CURRENT" | "PREVIOUS";
|
|
||||||
plate: InquiryVehicleInputDto["currentPlate"];
|
|
||||||
}> {
|
|
||||||
if (!input) return [];
|
|
||||||
return [
|
|
||||||
{ kind: "CURRENT" as const, plate: input.currentPlate },
|
|
||||||
...(input.registrationState ===
|
|
||||||
VehicleRegistrationState.RECENTLY_TRANSFERRED && input.previousPlate
|
|
||||||
? [{ kind: "PREVIOUS" as const, plate: input.previousPlate }]
|
|
||||||
: []),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeVehicleSerial(value: unknown): string {
|
|
||||||
return String(value ?? "")
|
|
||||||
.toUpperCase()
|
|
||||||
.replace(/[^A-Z0-9]/g, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A dated result is usable only while the returned policy has not expired. */
|
/** A dated result is usable only while the returned policy has not expired. */
|
||||||
export function isMappedPolicyCurrent(
|
export function isMappedPolicyCurrent(
|
||||||
mapped: Record<string, any>,
|
mapped: Record<string, any>,
|
||||||
@@ -429,19 +409,6 @@ export function isMappedPolicyCurrent(
|
|||||||
return endDate != null && endDate >= todayGregorian;
|
return endDate != null && endDate >= todayGregorian;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePlateForComparison(
|
|
||||||
plate: InquiryVehicleInputDto["currentPlate"],
|
|
||||||
): string {
|
|
||||||
return [
|
|
||||||
plate?.ir,
|
|
||||||
plate?.leftDigits,
|
|
||||||
plate?.centerAlphabet,
|
|
||||||
plate?.centerDigits,
|
|
||||||
]
|
|
||||||
.map((part) => String(part ?? "").trim())
|
|
||||||
.join("|");
|
|
||||||
}
|
|
||||||
|
|
||||||
const LEGACY_INQUIRY_FIELDS = [
|
const LEGACY_INQUIRY_FIELDS = [
|
||||||
"nationalCodeOfDriver",
|
"nationalCodeOfDriver",
|
||||||
"driverBirthday",
|
"driverBirthday",
|
||||||
@@ -469,63 +436,21 @@ function assertStructuredInquiryInput(input: Record<string, any>): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertPreviousPlateInquiryMatchesVin(
|
/**
|
||||||
expectedVin: string,
|
* Run a policy inquiry only for the submitted current plate. Recent-transfer
|
||||||
mapped: Record<string, any>,
|
* data is retained as case metadata, but must never trigger an inquiry for a
|
||||||
): void {
|
* previous plate or a previous policyholder.
|
||||||
const expected = normalizeVehicleSerial(expectedVin);
|
*/
|
||||||
const candidates = [
|
export async function runCurrentPlateInquiry<T>(options: {
|
||||||
mapped?.VinNumberField,
|
currentPlate: InquiryVehicleInputDto["currentPlate"];
|
||||||
mapped?.vin,
|
vin?: string;
|
||||||
mapped?.VIN,
|
query: (plate: InquiryVehicleInputDto["currentPlate"]) => Promise<T>;
|
||||||
mapped?.ChassisNumberField,
|
|
||||||
mapped?.chassisNumber,
|
|
||||||
mapped?.ChassisNo,
|
|
||||||
mapped?.vehicle?.VIN,
|
|
||||||
mapped?.vehicle?.ChassisNo,
|
|
||||||
]
|
|
||||||
.map(normalizeVehicleSerial)
|
|
||||||
.filter(Boolean);
|
|
||||||
if (!expected || !candidates.includes(expected)) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"نتیجه استعلام پلاک قبلی با شماره شاسی (VIN) واردشده مطابقت ندارد و پرونده نیازمند بررسی دستی است.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isPolicyNotFoundError(error: unknown): boolean {
|
|
||||||
const candidate = error as Record<string, any> | null;
|
|
||||||
const status = candidate?.status ?? candidate?.response?.status;
|
|
||||||
if (Number(status) === 404) return true;
|
|
||||||
const code = String(
|
|
||||||
candidate?.code ?? candidate?.response?.data?.code ?? "",
|
|
||||||
).toUpperCase();
|
|
||||||
if (["NOT_FOUND", "POLICY_NOT_FOUND", "NO_POLICY"].includes(code)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const message = String(
|
|
||||||
candidate?.message ?? candidate?.response?.data?.message ?? error ?? "",
|
|
||||||
);
|
|
||||||
return /\bnot[ -]?found\b|\bno (?:relevant )?policy\b|یافت نشد|فاقد بیمه(?:نامه)?/i.test(
|
|
||||||
message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function runPlateInquiryWithFallback<T>(options: {
|
|
||||||
vehicle?: ResolvedInquiryVehicle;
|
|
||||||
fallbackCurrentPlate: InquiryVehicleInputDto["currentPlate"];
|
|
||||||
query: (
|
|
||||||
plate: InquiryVehicleInputDto["currentPlate"],
|
|
||||||
plateKind: "CURRENT" | "PREVIOUS",
|
|
||||||
) => Promise<T>;
|
|
||||||
isUsable: (value: T) => boolean;
|
isUsable: (value: T) => boolean;
|
||||||
mappedValue: (value: T) => Record<string, any>;
|
|
||||||
shouldFallbackOnError?: (error: unknown) => boolean;
|
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
value: T;
|
value: T;
|
||||||
plateKind: "CURRENT" | "PREVIOUS";
|
plateKind: "CURRENT";
|
||||||
attempts: Array<{
|
attempts: Array<{
|
||||||
plateKind: "CURRENT" | "PREVIOUS";
|
plateKind: "CURRENT";
|
||||||
plate: InquiryVehicleInputDto["currentPlate"];
|
plate: InquiryVehicleInputDto["currentPlate"];
|
||||||
vin?: string;
|
vin?: string;
|
||||||
succeeded: boolean;
|
succeeded: boolean;
|
||||||
@@ -533,12 +458,8 @@ export async function runPlateInquiryWithFallback<T>(options: {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}>;
|
}>;
|
||||||
}> {
|
}> {
|
||||||
const candidates = options.vehicle
|
|
||||||
? vehiclePlateCandidates(options.vehicle)
|
|
||||||
: [{ kind: "CURRENT" as const, plate: options.fallbackCurrentPlate }];
|
|
||||||
let lastError: unknown;
|
|
||||||
const attempts: Array<{
|
const attempts: Array<{
|
||||||
plateKind: "CURRENT" | "PREVIOUS";
|
plateKind: "CURRENT";
|
||||||
plate: InquiryVehicleInputDto["currentPlate"];
|
plate: InquiryVehicleInputDto["currentPlate"];
|
||||||
vin?: string;
|
vin?: string;
|
||||||
succeeded: boolean;
|
succeeded: boolean;
|
||||||
@@ -546,80 +467,50 @@ export async function runPlateInquiryWithFallback<T>(options: {
|
|||||||
error?: string;
|
error?: string;
|
||||||
}> = [];
|
}> = [];
|
||||||
|
|
||||||
for (let index = 0; index < candidates.length; index += 1) {
|
try {
|
||||||
const candidate = candidates[index];
|
const value = await options.query(options.currentPlate);
|
||||||
const isLast = index === candidates.length - 1;
|
const usable = options.isUsable(value);
|
||||||
try {
|
if (!usable) {
|
||||||
const value = await options.query(candidate.plate, candidate.kind);
|
|
||||||
const usable = options.isUsable(value);
|
|
||||||
if (!usable) {
|
|
||||||
attempts.push({
|
|
||||||
plateKind: candidate.kind,
|
|
||||||
plate: candidate.plate,
|
|
||||||
...(options.vehicle?.vin ? { vin: options.vehicle.vin } : {}),
|
|
||||||
succeeded: true,
|
|
||||||
usable: false,
|
|
||||||
});
|
|
||||||
if (!isLast) continue;
|
|
||||||
const error = new BadRequestException(
|
|
||||||
"بیمهنامه معتبر و فعالی مطابق مشخصات خودرو و کد ملی واردشده یافت نشد.",
|
|
||||||
) as BadRequestException & { attempts?: typeof attempts };
|
|
||||||
error.attempts = attempts;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
if (candidate.kind === "PREVIOUS" && usable) {
|
|
||||||
assertPreviousPlateInquiryMatchesVin(
|
|
||||||
options.vehicle!.vin!,
|
|
||||||
options.mappedValue(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
attempts.push({
|
attempts.push({
|
||||||
plateKind: candidate.kind,
|
plateKind: "CURRENT",
|
||||||
plate: candidate.plate,
|
plate: options.currentPlate,
|
||||||
...(options.vehicle?.vin ? { vin: options.vehicle.vin } : {}),
|
...(options.vin ? { vin: options.vin } : {}),
|
||||||
succeeded: true,
|
succeeded: true,
|
||||||
usable: true,
|
usable: false,
|
||||||
});
|
});
|
||||||
return { value, plateKind: candidate.kind, attempts };
|
const error = new BadRequestException(
|
||||||
} catch (error) {
|
"بیمهنامه معتبر و فعالی مطابق مشخصات خودرو و کد ملی واردشده یافت نشد.",
|
||||||
lastError = error;
|
) as BadRequestException & { attempts?: typeof attempts };
|
||||||
const alreadyRecorded =
|
error.attempts = attempts;
|
||||||
typeof error === "object" &&
|
throw error;
|
||||||
error != null &&
|
|
||||||
Array.isArray((error as { attempts?: unknown }).attempts);
|
|
||||||
if (!alreadyRecorded) {
|
|
||||||
attempts.push({
|
|
||||||
plateKind: candidate.kind,
|
|
||||||
plate: candidate.plate,
|
|
||||||
...(options.vehicle?.vin ? { vin: options.vehicle.vin } : {}),
|
|
||||||
succeeded: false,
|
|
||||||
error: error instanceof Error ? error.message : String(error),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!isLast &&
|
|
||||||
!(options.shouldFallbackOnError ?? isPolicyNotFoundError)(error)
|
|
||||||
) {
|
|
||||||
if (typeof error === "object" && error != null) {
|
|
||||||
(error as { attempts?: typeof attempts }).attempts = attempts;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
if (isLast) {
|
|
||||||
if (typeof error === "object" && error != null) {
|
|
||||||
(error as { attempts?: typeof attempts }).attempts = attempts;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
attempts.push({
|
||||||
|
plateKind: "CURRENT",
|
||||||
|
plate: options.currentPlate,
|
||||||
|
...(options.vin ? { vin: options.vin } : {}),
|
||||||
|
succeeded: true,
|
||||||
|
usable: true,
|
||||||
|
});
|
||||||
|
return { value, plateKind: "CURRENT", attempts };
|
||||||
|
} catch (error) {
|
||||||
|
const alreadyRecorded =
|
||||||
|
typeof error === "object" &&
|
||||||
|
error != null &&
|
||||||
|
Array.isArray((error as { attempts?: unknown }).attempts);
|
||||||
|
if (!alreadyRecorded) {
|
||||||
|
attempts.push({
|
||||||
|
plateKind: "CURRENT",
|
||||||
|
plate: options.currentPlate,
|
||||||
|
...(options.vin ? { vin: options.vin } : {}),
|
||||||
|
succeeded: false,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof error === "object" && error != null) {
|
||||||
|
(error as { attempts?: typeof attempts }).attempts = attempts;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw (
|
|
||||||
lastError ??
|
|
||||||
new BadRequestException(
|
|
||||||
"برای هیچیک از پلاکهای ثبتشده نتیجه معتبری یافت نشد.",
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeInquirySubmission<T extends Record<string, any>>(
|
export function normalizeInquirySubmission<T extends Record<string, any>>(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||||||
import { RequestManagementService } from "./request-management.service";
|
import { RequestManagementService } from "./request-management.service";
|
||||||
|
|
||||||
describe("RequestManagementService previous policyholder routing", () => {
|
describe("RequestManagementService policyholder inquiry routing", () => {
|
||||||
const currentPlate = {
|
const currentPlate = {
|
||||||
leftDigits: "44",
|
leftDigits: "44",
|
||||||
centerAlphabet: "ب",
|
centerAlphabet: "ب",
|
||||||
@@ -46,7 +46,7 @@ describe("RequestManagementService previous policyholder routing", () => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
it("uses the previous code only for the previous-plate third-party lookup", async () => {
|
it("does not fall back from the current plate for third-party insurance", async () => {
|
||||||
const service = Object.create(RequestManagementService.prototype) as any;
|
const service = Object.create(RequestManagementService.prototype) as any;
|
||||||
service.sandHubService = {
|
service.sandHubService = {
|
||||||
getTejaratBlockInquiry: jest
|
getTejaratBlockInquiry: jest
|
||||||
@@ -54,10 +54,7 @@ describe("RequestManagementService previous policyholder routing", () => {
|
|||||||
.mockResolvedValueOnce({ raw: {}, mapped: {} })
|
.mockResolvedValueOnce({ raw: {}, mapped: {} })
|
||||||
.mockResolvedValueOnce({
|
.mockResolvedValueOnce({
|
||||||
raw: {},
|
raw: {},
|
||||||
mapped: {
|
mapped: { CompanyName: "پارسیان", VinNumberField: vehicle.vin },
|
||||||
CompanyName: "پارسیان",
|
|
||||||
VinNumberField: vehicle.vin,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
const submission = service.normalizeInquiryInput(
|
const submission = service.normalizeInquiryInput(
|
||||||
@@ -65,32 +62,23 @@ describe("RequestManagementService previous policyholder routing", () => {
|
|||||||
participantInput(BlameRequestType.THIRD_PARTY),
|
participantInput(BlameRequestType.THIRD_PARTY),
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await service.getThirdPartyPlateInquiry(submission);
|
await expect(service.getThirdPartyPlateInquiry(submission)).rejects.toThrow(
|
||||||
|
"بیمهنامه معتبر و فعالی مطابق مشخصات خودرو و کد ملی واردشده یافت نشد.",
|
||||||
|
);
|
||||||
|
|
||||||
expect(result.plateKind).toBe("PREVIOUS");
|
expect(service.sandHubService.getTejaratBlockInquiry).toHaveBeenCalledTimes(
|
||||||
expect(
|
|
||||||
service.sandHubService.getTejaratBlockInquiry,
|
|
||||||
).toHaveBeenNthCalledWith(
|
|
||||||
1,
|
1,
|
||||||
|
);
|
||||||
|
expect(service.sandHubService.getTejaratBlockInquiry).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
plate: currentPlate,
|
plate: currentPlate,
|
||||||
nationalCodeOfInsurer: "0022222222",
|
nationalCodeOfInsurer: "0022222222",
|
||||||
}),
|
}),
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
expect(
|
|
||||||
service.sandHubService.getTejaratBlockInquiry,
|
|
||||||
).toHaveBeenNthCalledWith(
|
|
||||||
2,
|
|
||||||
expect.objectContaining({
|
|
||||||
plate: previousPlate,
|
|
||||||
nationalCodeOfInsurer: "0098765432",
|
|
||||||
}),
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses the previous code only for the previous-plate car-body lookup", async () => {
|
it("does not fall back from the current plate for car-body insurance", async () => {
|
||||||
const service = Object.create(RequestManagementService.prototype) as any;
|
const service = Object.create(RequestManagementService.prototype) as any;
|
||||||
service.sandHubService = {
|
service.sandHubService = {
|
||||||
getCarBodyInquiry: jest
|
getCarBodyInquiry: jest
|
||||||
@@ -98,10 +86,7 @@ describe("RequestManagementService previous policyholder routing", () => {
|
|||||||
.mockResolvedValueOnce({ raw: {}, mapped: {} })
|
.mockResolvedValueOnce({ raw: {}, mapped: {} })
|
||||||
.mockResolvedValueOnce({
|
.mockResolvedValueOnce({
|
||||||
raw: {},
|
raw: {},
|
||||||
mapped: {
|
mapped: { policyNumber: "BODY-1", VinNumberField: vehicle.vin },
|
||||||
policyNumber: "BODY-1",
|
|
||||||
VinNumberField: vehicle.vin,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
const submission = service.normalizeInquiryInput(
|
const submission = service.normalizeInquiryInput(
|
||||||
@@ -109,22 +94,45 @@ describe("RequestManagementService previous policyholder routing", () => {
|
|||||||
participantInput(BlameRequestType.CAR_BODY),
|
participantInput(BlameRequestType.CAR_BODY),
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await service.getCarBodyPlateInquiry(submission);
|
await expect(service.getCarBodyPlateInquiry(submission)).rejects.toThrow(
|
||||||
|
"بیمهنامه معتبر و فعالی مطابق مشخصات خودرو و کد ملی واردشده یافت نشد.",
|
||||||
|
);
|
||||||
|
|
||||||
expect(result.plateKind).toBe("PREVIOUS");
|
expect(service.sandHubService.getCarBodyInquiry).toHaveBeenCalledTimes(1);
|
||||||
expect(service.sandHubService.getCarBodyInquiry).toHaveBeenNthCalledWith(
|
expect(service.sandHubService.getCarBodyInquiry).toHaveBeenCalledWith(
|
||||||
1,
|
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
plate: currentPlate,
|
plate: currentPlate,
|
||||||
nationalCodeOfInsurer: "0033333333",
|
nationalCodeOfInsurer: "0033333333",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
expect(service.sandHubService.getCarBodyInquiry).toHaveBeenNthCalledWith(
|
});
|
||||||
2,
|
|
||||||
expect.objectContaining({
|
it("queries VIN with the third-party policyholder", async () => {
|
||||||
plate: previousPlate,
|
const service = Object.create(RequestManagementService.prototype) as any;
|
||||||
nationalCodeOfInsurer: "0098765432",
|
service.sandHubService = {
|
||||||
|
getPolicyByChassisInquiry: jest.fn().mockResolvedValue({
|
||||||
|
raw: {},
|
||||||
|
mapped: { CompanyName: "پارسیان" },
|
||||||
}),
|
}),
|
||||||
|
};
|
||||||
|
const submission = service.normalizeInquiryInput(
|
||||||
|
BlameRequestType.THIRD_PARTY,
|
||||||
|
participantInput(BlameRequestType.THIRD_PARTY),
|
||||||
|
);
|
||||||
|
|
||||||
|
await service.getThirdPartyVinInquiry(submission);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
service.sandHubService.getPolicyByChassisInquiry,
|
||||||
|
).toHaveBeenCalledTimes(1);
|
||||||
|
expect(
|
||||||
|
service.sandHubService.getPolicyByChassisInquiry,
|
||||||
|
).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
nationalCode: "0022222222",
|
||||||
|
chassis: vehicle.vin,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -140,7 +140,7 @@ import {
|
|||||||
NormalizedInquirySubmission,
|
NormalizedInquirySubmission,
|
||||||
normalizeInquirySubmission,
|
normalizeInquirySubmission,
|
||||||
resolveInquirySubjects,
|
resolveInquirySubjects,
|
||||||
runPlateInquiryWithFallback,
|
runCurrentPlateInquiry,
|
||||||
sanitizeStoredInquiryParticipants,
|
sanitizeStoredInquiryParticipants,
|
||||||
} from "./inquiry-participant-resolver";
|
} from "./inquiry-participant-resolver";
|
||||||
import {
|
import {
|
||||||
@@ -386,17 +386,14 @@ export class RequestManagementService {
|
|||||||
options?: Record<string, any>,
|
options?: Record<string, any>,
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const subjects = resolveInquirySubjects(submission);
|
const subjects = resolveInquirySubjects(submission);
|
||||||
const result = await runPlateInquiryWithFallback({
|
const result = await runCurrentPlateInquiry({
|
||||||
vehicle: submission.vehicle,
|
currentPlate: submission.vehicle?.currentPlate ?? submission.dto.plate,
|
||||||
fallbackCurrentPlate: submission.dto.plate,
|
vin: submission.vehicle?.vin,
|
||||||
query: (plate, plateKind) =>
|
query: (plate) =>
|
||||||
this.sandHubService.getTejaratBlockInquiry(
|
this.sandHubService.getTejaratBlockInquiry(
|
||||||
{
|
{
|
||||||
plate: plate as any,
|
plate: plate as any,
|
||||||
nationalCodeOfInsurer:
|
nationalCodeOfInsurer: subjects.thirdPartyPolicyNationalCode,
|
||||||
plateKind === "PREVIOUS"
|
|
||||||
? submission.vehicle!.previousPolicyholderNationalCode!
|
|
||||||
: subjects.thirdPartyPolicyNationalCode,
|
|
||||||
},
|
},
|
||||||
options,
|
options,
|
||||||
),
|
),
|
||||||
@@ -404,7 +401,6 @@ export class RequestManagementService {
|
|||||||
!value?.mapped?.Error &&
|
!value?.mapped?.Error &&
|
||||||
!!value?.mapped?.CompanyName &&
|
!!value?.mapped?.CompanyName &&
|
||||||
isMappedPolicyCurrent(value.mapped),
|
isMappedPolicyCurrent(value.mapped),
|
||||||
mappedValue: (value) => value?.mapped ?? {},
|
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
...result.value,
|
...result.value,
|
||||||
@@ -423,15 +419,12 @@ export class RequestManagementService {
|
|||||||
"اطلاعات بیمهگذار برای استعلام بیمه بدنه الزامی است.",
|
"اطلاعات بیمهگذار برای استعلام بیمه بدنه الزامی است.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const result = await runPlateInquiryWithFallback({
|
const result = await runCurrentPlateInquiry({
|
||||||
vehicle: submission.vehicle,
|
currentPlate: submission.vehicle?.currentPlate ?? submission.dto.plate,
|
||||||
fallbackCurrentPlate: submission.dto.plate,
|
vin: submission.vehicle?.vin,
|
||||||
query: (plate, plateKind) =>
|
query: (plate) =>
|
||||||
this.sandHubService.getCarBodyInquiry({
|
this.sandHubService.getCarBodyInquiry({
|
||||||
nationalCodeOfInsurer:
|
nationalCodeOfInsurer: policyholderNationalCode,
|
||||||
plateKind === "PREVIOUS"
|
|
||||||
? submission.vehicle!.previousPolicyholderNationalCode!
|
|
||||||
: policyholderNationalCode,
|
|
||||||
plate: plate as any,
|
plate: plate as any,
|
||||||
}),
|
}),
|
||||||
isUsable: (value) =>
|
isUsable: (value) =>
|
||||||
@@ -442,7 +435,6 @@ export class RequestManagementService {
|
|||||||
value.mapped.CompanyName ||
|
value.mapped.CompanyName ||
|
||||||
value.mapped.companyId
|
value.mapped.companyId
|
||||||
),
|
),
|
||||||
mappedValue: (value) => value?.mapped ?? {},
|
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
...result.value,
|
...result.value,
|
||||||
|
|||||||
Reference in New Issue
Block a user