forked from Yara724/api
Require structured inquiry participant inputs
This commit is contained in:
@@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
## interface پیادهسازیشده
|
## interface پیادهسازیشده
|
||||||
|
|
||||||
فیلدهای نقشها و آبجکت اختیاری `vehicle` که در ادامه آمدهاند، مستقیماً در body تمام درخواستهای استعلام فعلی پذیرفته میشوند. این تغییر شامل فرم اولیه کاربر و mirror کارشناس/ثبتکننده در V2، جریان کارشناس V3، جریانهای پروندهساز V4/V5، مرکز تماس V6 و مسیرهای تکدرخواستی حضوری است. routeهای پلاک و VIN از قوانین مشترک اشخاص استفاده میکنند.
|
فیلدهای نقشها و آبجکت الزامی `vehicle` که در ادامه آمدهاند، مستقیماً در body تمام درخواستهای استعلام فعلی پذیرفته میشوند. این تغییر شامل فرم اولیه کاربر و mirror کارشناس/ثبتکننده در V2، جریان کارشناس V3، جریانهای پروندهساز V4/V5، مرکز تماس V6 و مسیرهای تکدرخواستی حضوری است. routeهای پلاک و VIN از قوانین مشترک اشخاص استفاده میکنند. فیلدهای تخت راننده/بیمهگذار و شماره تلفن، ورودی استعلام نیستند.
|
||||||
|
|
||||||
در دوره مهاجرت، فیلدهای قدیمی و تخت همچنان پذیرفته میشوند. پاسخها و جزئیات پرونده برای نقشهای عملیاتی، در صورت وجود داده، فیلدهای نرمالشده `participants`، `participantRoles`، `vehicle.registrationState` و `vehicle.previousPlateId` را نمایش میدهند.
|
پاسخها و جزئیات پرونده برای نقشهای عملیاتی، در صورت وجود داده، فیلدهای نرمالشده `participants`، `participantRoles`، `vehicle.registrationState` و `vehicle.previousPlateId` را نمایش میدهند.
|
||||||
|
|
||||||
## مسئله
|
## مسئله
|
||||||
|
|
||||||
@@ -106,14 +106,9 @@
|
|||||||
|
|
||||||
جریانهای V2 کاربر/کارشناس، V3، V4، V5 و V6 باید adapter همین قوانین مشترک باشند و منطق نسبت اشخاص را جداگانه پیادهسازی نکنند.
|
جریانهای V2 کاربر/کارشناس، V3، V4، V5 و V6 باید adapter همین قوانین مشترک باشند و منطق نسبت اشخاص را جداگانه پیادهسازی نکنند.
|
||||||
|
|
||||||
## سازگاری و انتشار تدریجی
|
## مرز قرارداد
|
||||||
|
|
||||||
1. در دوره گذار، ساختار جدید در کنار فیلدهای قدیمی پذیرفته شود.
|
ورودی استعلام فقط شامل آبجکتهای ساختاریافته اشخاص و خودرو است. بکاند فیلدهای تختی مانند `nationalCodeOfDriver`، `nationalCodeOfInsurer`، `driverIsInsurer`، `plate`/`vin` سطح بالا و `phoneNumber` را با خطای اعتبارسنجی رد میکند. احراز هویت تلفنی و جریانهای تماس با طرفین از جمعآوری هویت برای استعلام جدا هستند.
|
||||||
2. `nationalCodeOfDriver` به راننده و `nationalCodeOfInsurer` به بیمهگذار شخص ثالث نگاشت شود. اگر `driverIsInsurer=true` است، هر دو نقش به یک شخص متصل شوند.
|
|
||||||
3. `plate` یا `plateId` قدیمی به پلاک فعلی نگاشت شود؛ پلاک قبلی فقط وقتی ثبت شود که صریحاً از کاربر دریافت شده باشد.
|
|
||||||
4. برای دادههای قدیمی، مالک خودرو یا بیمهگذار بدنه حدس زده نشود؛ مگر اینکه نتیجه استعلام ذخیرهشده آن را قطعی کند، نقش «نامشخص» باقی بماند.
|
|
||||||
5. پاسخ جزئیات و گزارش، اشخاص را بر اساس نقش نمایش دهد و در زمان مهاجرت فیلدهای قدیمی را نیز حفظ کند.
|
|
||||||
6. پس از مهاجرت همه فرانتاندها، قرارداد جدید برای پروندههای تازه الزامی و فیلدهای قدیمی deprecated شوند.
|
|
||||||
|
|
||||||
## تصمیم پیشنهادی
|
## تصمیم پیشنهادی
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ Persian version: [inquiry-participants-proposal.fa.md](./inquiry-participants-pr
|
|||||||
|
|
||||||
## Implemented interface
|
## Implemented interface
|
||||||
|
|
||||||
The role fields and optional `vehicle` object shown below are accepted directly in every existing inquiry request body. This covers V2 user and expert/registrar mirror initial forms, V3 expert flow, V4/V5 FileMaker flows, V6 call-center flow, and the one-shot in-person completion paths. Plate and VIN routes share the same participant rules.
|
The role fields and required `vehicle` object shown below are accepted directly in every existing inquiry request body. This covers V2 user and expert/registrar mirror initial forms, V3 expert flow, V4/V5 FileMaker flows, V6 call-center flow, and the one-shot in-person completion paths. Plate and VIN routes share the same participant rules. Flat driver/insurer fields and phone numbers are not inquiry inputs.
|
||||||
|
|
||||||
Existing flat fields remain accepted during migration. Responses and file-detail views for operational actors expose normalized `participants`, `participantRoles`, `vehicle.registrationState`, and `vehicle.previousPlateId` where available.
|
Responses and file-detail views for operational actors expose normalized `participants`, `participantRoles`, `vehicle.registrationState`, and `vehicle.previousPlateId` where available.
|
||||||
|
|
||||||
## Problem
|
## Problem
|
||||||
|
|
||||||
@@ -106,14 +106,9 @@ Create one shared participant resolver used by every inquiry route. Its interfac
|
|||||||
|
|
||||||
V2 user/expert routes, V3, V4, V5, and V6 should be adapters over this shared rule set rather than implementing their own relationship logic.
|
V2 user/expert routes, V3, V4, V5, and V6 should be adapters over this shared rule set rather than implementing their own relationship logic.
|
||||||
|
|
||||||
## Compatibility and rollout
|
## Contract boundary
|
||||||
|
|
||||||
1. Accept the new participant object alongside the legacy fields temporarily.
|
The structured participant and vehicle objects are the only accepted inquiry input. The backend rejects flat fields such as `nationalCodeOfDriver`, `nationalCodeOfInsurer`, `driverIsInsurer`, top-level `plate`/`vin`, and `phoneNumber` with a validation error. Phone-based authentication and party contact flows remain separate from inquiry identity collection.
|
||||||
2. Map legacy `nationalCodeOfDriver` to Driver and `nationalCodeOfInsurer` to Third-party Policyholder. When `driverIsInsurer=true`, bind both roles to the same participant.
|
|
||||||
3. Map the legacy `plate`/`plateId` to Current Plate; leave Previous Plate absent unless it was explicitly collected.
|
|
||||||
4. Do not guess Vehicle Owner or Car-body Policyholder for old records. Mark unresolved roles as unknown unless stored inquiry evidence identifies them.
|
|
||||||
5. Update report/detail responses to expose people by role while retaining legacy fields during migration.
|
|
||||||
6. After all frontends use the new step, require the new contract for newly created cases and deprecate the legacy fields.
|
|
||||||
|
|
||||||
## Decision
|
## Decision
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import {
|
||||||
|
ApiHideProperty,
|
||||||
|
ApiProperty,
|
||||||
|
ApiPropertyOptional,
|
||||||
|
} from "@nestjs/swagger";
|
||||||
import { Type } from "class-transformer";
|
import { Type } from "class-transformer";
|
||||||
import {
|
import {
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
@@ -69,11 +73,6 @@ export class InquiryParticipantInputDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
fullName?: string;
|
fullName?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional()
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
phoneNumber?: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: "Required for a driver who has a licence.",
|
description: "Required for a driver who has a licence.",
|
||||||
})
|
})
|
||||||
@@ -117,27 +116,31 @@ export class InquiryVehicleInputDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
@MaxLength(17)
|
@MaxLength(17)
|
||||||
vin?: string;
|
vin?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: "Whether the vehicle is newly purchased/registered.",
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsBoolean()
|
||||||
|
isNewCar?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** New role-complete contract mixed into every inquiry DTO. Legacy fields remain during rollout. */
|
/** Structured role-complete contract mixed into every inquiry DTO. */
|
||||||
export class InquiryParticipantFieldsDto {
|
export class InquiryParticipantFieldsDto {
|
||||||
@ApiPropertyOptional({ type: InquiryParticipantInputDto })
|
@ApiProperty({ type: InquiryParticipantInputDto })
|
||||||
@IsOptional()
|
|
||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@Type(() => InquiryParticipantInputDto)
|
@Type(() => InquiryParticipantInputDto)
|
||||||
driver?: InquiryParticipantInputDto;
|
driver: InquiryParticipantInputDto;
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: InquiryParticipantInputDto })
|
@ApiProperty({ type: InquiryParticipantInputDto })
|
||||||
@IsOptional()
|
|
||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@Type(() => InquiryParticipantInputDto)
|
@Type(() => InquiryParticipantInputDto)
|
||||||
vehicleOwner?: InquiryParticipantInputDto;
|
vehicleOwner: InquiryParticipantInputDto;
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: InquiryParticipantInputDto })
|
@ApiProperty({ type: InquiryParticipantInputDto })
|
||||||
@IsOptional()
|
|
||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@Type(() => InquiryParticipantInputDto)
|
@Type(() => InquiryParticipantInputDto)
|
||||||
thirdPartyPolicyholder?: InquiryParticipantInputDto;
|
thirdPartyPolicyholder: InquiryParticipantInputDto;
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: InquiryParticipantInputDto })
|
@ApiPropertyOptional({ type: InquiryParticipantInputDto })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -145,9 +148,48 @@ export class InquiryParticipantFieldsDto {
|
|||||||
@Type(() => InquiryParticipantInputDto)
|
@Type(() => InquiryParticipantInputDto)
|
||||||
carBodyPolicyholder?: InquiryParticipantInputDto;
|
carBodyPolicyholder?: InquiryParticipantInputDto;
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: InquiryVehicleInputDto })
|
@ApiProperty({ type: InquiryVehicleInputDto })
|
||||||
@IsOptional()
|
|
||||||
@ValidateNested()
|
@ValidateNested()
|
||||||
@Type(() => InquiryVehicleInputDto)
|
@Type(() => InquiryVehicleInputDto)
|
||||||
vehicle?: InquiryVehicleInputDto;
|
vehicle: InquiryVehicleInputDto;
|
||||||
|
|
||||||
|
/** Internal normalized projections; rejected as request input by the resolver. */
|
||||||
|
@ApiHideProperty()
|
||||||
|
nationalCodeOfDriver?: string;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
driverBirthday?: any;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
driverLicense?: string;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
licenseType?: string;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
nationalCodeOfInsurer?: string;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
insurerBirthday?: any;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
insurerLicense?: string;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
driverIsInsurer?: boolean;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
userNoCertificate?: boolean;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
plate?: any;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
plateId?: string;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
vin?: string;
|
||||||
|
|
||||||
|
@ApiHideProperty()
|
||||||
|
isNewCar?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +1,9 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
import { PlatesDto } from "src/plates/dto/plate.dto";
|
import { PlatesDto } from "src/plates/dto/plate.dto";
|
||||||
import { Plates } from "src/Types&Enums/plate.interface";
|
import { Plates } from "src/Types&Enums/plate.interface";
|
||||||
import { InquiryParticipantFieldsDto } from "src/common/dto/inquiry-participants.dto";
|
import { InquiryParticipantFieldsDto } from "src/common/dto/inquiry-participants.dto";
|
||||||
|
|
||||||
export class AddPlateDto extends InquiryParticipantFieldsDto {
|
export class AddPlateDto extends InquiryParticipantFieldsDto {}
|
||||||
plateId: string;
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: String,
|
|
||||||
description: "Legacy third-party policyholder national code",
|
|
||||||
})
|
|
||||||
nationalCodeOfInsurer: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: String,
|
|
||||||
description: "Legacy driver national code",
|
|
||||||
})
|
|
||||||
nationalCodeOfDriver: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: String })
|
|
||||||
insurerLicense: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: String })
|
|
||||||
driverLicense: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: PlatesDto,
|
|
||||||
description:
|
|
||||||
"Legacy/current plate; may be supplied as vehicle.currentPlate",
|
|
||||||
})
|
|
||||||
plate: Plates;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: Boolean,
|
|
||||||
description: "Legacy role relationship",
|
|
||||||
})
|
|
||||||
driverIsInsurer: boolean;
|
|
||||||
|
|
||||||
@ApiProperty({ type: Boolean, required: true, default: false })
|
|
||||||
isNewCar: boolean;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: Boolean,
|
|
||||||
description: "Legacy; use driver.hasDrivingLicense",
|
|
||||||
})
|
|
||||||
userNoCertificate: boolean;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: Number,
|
|
||||||
description: "Legacy third-party policyholder birthday",
|
|
||||||
})
|
|
||||||
insurerBirthday: number;
|
|
||||||
@ApiProperty({
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
driverBirthday: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AddPlateProfileDto {
|
export class AddPlateProfileDto {
|
||||||
plateId: string;
|
plateId: string;
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import {
|
import { IsNotEmpty, IsOptional, IsString } from "class-validator";
|
||||||
IsNotEmpty,
|
|
||||||
IsOptional,
|
|
||||||
IsString,
|
|
||||||
MaxLength,
|
|
||||||
ValidateIf,
|
|
||||||
} from "class-validator";
|
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
|
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
|
||||||
import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum";
|
import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum";
|
||||||
@@ -229,70 +223,8 @@ export class BlameConfessionDtoV2 {
|
|||||||
imGuilty?: boolean;
|
imGuilty?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** V2 initial-form step submitted with the shared structured vehicle contract. */
|
||||||
* V2 initial-form step submitted with a VIN/chassis number instead of a plate.
|
export class InitialFormVinDto extends InquiryParticipantFieldsDto {}
|
||||||
* All identity and license fields from {@link AddPlateDto} are preserved; only
|
|
||||||
* `plate` is replaced by `vin` (the 17-character chassis / VIN string).
|
|
||||||
*/
|
|
||||||
export class InitialFormVinDto extends InquiryParticipantFieldsDto {
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
description: "17-character VIN / chassis number (شماره شاسی)",
|
|
||||||
example: "NAAM01E15HK123456",
|
|
||||||
maxLength: 17,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@MaxLength(17)
|
|
||||||
@ValidateIf((dto) => !dto.vehicle?.vin)
|
|
||||||
vin: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: String,
|
|
||||||
description: "Legacy third-party policyholder national code",
|
|
||||||
})
|
|
||||||
@ValidateIf((dto) => !dto.thirdPartyPolicyholder)
|
|
||||||
nationalCodeOfInsurer: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: String,
|
|
||||||
description: "Legacy driver national code",
|
|
||||||
})
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
nationalCodeOfDriver: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: String })
|
|
||||||
insurerLicense: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: String })
|
|
||||||
driverLicense: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: Boolean,
|
|
||||||
description: "Legacy role relationship",
|
|
||||||
})
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
driverIsInsurer: boolean;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: Boolean, default: false })
|
|
||||||
isNewCar: boolean;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: Boolean,
|
|
||||||
description: "Legacy; use driver.hasDrivingLicense",
|
|
||||||
})
|
|
||||||
userNoCertificate: boolean;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: Number,
|
|
||||||
description: "Legacy policyholder birthday",
|
|
||||||
})
|
|
||||||
insurerBirthday: number;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({ type: String, required: false })
|
|
||||||
driverBirthday: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// export class DocsOfThisFile {
|
// export class DocsOfThisFile {
|
||||||
// @ApiProperty()
|
// @ApiProperty()
|
||||||
|
|||||||
@@ -1,182 +1,14 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
|
||||||
import {
|
|
||||||
IsBoolean,
|
|
||||||
IsNotEmpty,
|
|
||||||
IsOptional,
|
|
||||||
IsString,
|
|
||||||
MaxLength,
|
|
||||||
ValidateIf,
|
|
||||||
ValidateNested,
|
|
||||||
} from "class-validator";
|
|
||||||
import { Type } from "class-transformer";
|
|
||||||
import { InquiryParticipantFieldsDto } from "src/common/dto/inquiry-participants.dto";
|
import { InquiryParticipantFieldsDto } from "src/common/dto/inquiry-participants.dto";
|
||||||
|
|
||||||
class PlateV6Dto {
|
|
||||||
@ApiProperty({ example: "44", description: "Left two digits" })
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
leftDigits: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: "ب", description: "Center alphabet letter" })
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
centerAlphabet: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: "111", description: "Center three digits" })
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
centerDigits: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: "22",
|
|
||||||
description: "Right two digits (Iran region code)",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
ir: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inquiry body for the V6 call-center flow.
|
* Inquiry body for the V6 call-center flow.
|
||||||
* Same as V3 but without `sheba` — the user adds their own IBAN later via the link.
|
* Same as V3 but without `sheba` — the user adds their own IBAN later via the link.
|
||||||
*/
|
*/
|
||||||
export class RunCallCenterInquiryV6Dto extends InquiryParticipantFieldsDto {
|
export class RunCallCenterInquiryV6Dto extends InquiryParticipantFieldsDto {}
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: PlateV6Dto,
|
|
||||||
description: "Plate segments — Tejarat block / third-party inquiry.",
|
|
||||||
})
|
|
||||||
@ValidateNested()
|
|
||||||
@Type(() => PlateV6Dto)
|
|
||||||
@ValidateIf((dto) => !dto.vehicle?.currentPlate)
|
|
||||||
plate: PlateV6Dto;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1234567890",
|
|
||||||
description: "Legacy third-party policyholder national code",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ValidateIf((dto) => !dto.thirdPartyPolicyholder)
|
|
||||||
nationalCodeOfInsurer: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1234567890",
|
|
||||||
description: "Legacy driver national code",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
nationalCodeOfDriver: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: true,
|
|
||||||
description: "Legacy role relationship",
|
|
||||||
})
|
|
||||||
@IsBoolean()
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
driverIsInsurer: boolean;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: 13780624,
|
|
||||||
description: "Legacy policyholder birth date (Jalali)",
|
|
||||||
})
|
|
||||||
insurerBirthday: number | string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: 13780624,
|
|
||||||
description:
|
|
||||||
"Driver birth date (Jalali). Required when driverIsInsurer is false.",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
driverBirthday?: number | string | null;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "123456789",
|
|
||||||
description: "Driver license (required when driverIsInsurer is false).",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
driverLicense?: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "123456789",
|
|
||||||
description: "Insurer license (required when driverIsInsurer is true).",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
insurerLicense?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* VIN / chassis variant of the V6 call-center inquiry.
|
* VIN / chassis variant of the V6 call-center inquiry.
|
||||||
* Identical to `RunCallCenterInquiryV6Dto` but replaces `plate` with `vin`.
|
* Identical to `RunCallCenterInquiryV6Dto` but replaces `plate` with `vin`.
|
||||||
* Sheba (IBAN) is intentionally absent — the user provides it themselves via the link.
|
* Sheba (IBAN) is intentionally absent — the user provides it themselves via the link.
|
||||||
*/
|
*/
|
||||||
export class RunCallCenterInquiryVinV6Dto extends InquiryParticipantFieldsDto {
|
export class RunCallCenterInquiryVinV6Dto extends InquiryParticipantFieldsDto {}
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "NAAM01E15HK123456",
|
|
||||||
description: "17-character VIN / chassis number (شماره شاسی)",
|
|
||||||
maxLength: 17,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@MaxLength(17)
|
|
||||||
@ValidateIf((dto) => !dto.vehicle?.vin)
|
|
||||||
vin: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1234567890",
|
|
||||||
description: "Legacy third-party policyholder national code",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ValidateIf((dto) => !dto.thirdPartyPolicyholder)
|
|
||||||
nationalCodeOfInsurer: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1234567890",
|
|
||||||
description: "Legacy driver national code",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
nationalCodeOfDriver: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: true,
|
|
||||||
description: "Legacy role relationship",
|
|
||||||
})
|
|
||||||
@IsBoolean()
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
driverIsInsurer: boolean;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: 13780624,
|
|
||||||
description: "Legacy policyholder birth date (Jalali)",
|
|
||||||
})
|
|
||||||
insurerBirthday: number | string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: 13780624,
|
|
||||||
description:
|
|
||||||
"Driver birth date (Jalali). Required when driverIsInsurer is false.",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
driverBirthday?: number | string | null;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "123456789",
|
|
||||||
description: "Driver license (required when driverIsInsurer is false).",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
driverLicense?: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "123456789",
|
|
||||||
description: "Insurer license (required when driverIsInsurer is true).",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
insurerLicense?: string;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,95 +1,12 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import {
|
import { IsOptional, IsString } from "class-validator";
|
||||||
IsBoolean,
|
|
||||||
IsNotEmpty,
|
|
||||||
IsOptional,
|
|
||||||
IsString,
|
|
||||||
MaxLength,
|
|
||||||
ValidateIf,
|
|
||||||
ValidateNested,
|
|
||||||
} from "class-validator";
|
|
||||||
import { Type } from "class-transformer";
|
|
||||||
import { InquiryParticipantFieldsDto } from "src/common/dto/inquiry-participants.dto";
|
import { InquiryParticipantFieldsDto } from "src/common/dto/inquiry-participants.dto";
|
||||||
|
|
||||||
class PlateV3Dto {
|
|
||||||
@ApiProperty({ example: "44", description: "Left two digits" })
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
leftDigits: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: "ب", description: "Center alphabet letter" })
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
centerAlphabet: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: "111", description: "Center three digits" })
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
centerDigits: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: "22",
|
|
||||||
description: "Right two digits (Iran region code)",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
ir: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Body for `POST run-inquiries/:requestId`.
|
* Body for `POST run-inquiries/:requestId`.
|
||||||
* First call = guilty party (+ auto claim). Second call = damaged party (THIRD_PARTY only).
|
* First call = guilty party (+ auto claim). Second call = damaged party (THIRD_PARTY only).
|
||||||
*/
|
*/
|
||||||
export class RunInquiriesV3Dto extends InquiryParticipantFieldsDto {
|
export class RunInquiriesV3Dto extends InquiryParticipantFieldsDto {
|
||||||
@ApiPropertyOptional({
|
|
||||||
type: PlateV3Dto,
|
|
||||||
description: "Plate segments — Tejarat block / third-party inquiry.",
|
|
||||||
})
|
|
||||||
@ValidateNested()
|
|
||||||
@Type(() => PlateV3Dto)
|
|
||||||
@ValidateIf((dto) => !dto.vehicle?.currentPlate)
|
|
||||||
plate: PlateV3Dto;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1234567890",
|
|
||||||
description: "Legacy third-party policyholder national code",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ValidateIf((dto) => !dto.thirdPartyPolicyholder)
|
|
||||||
nationalCodeOfInsurer: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1234567890",
|
|
||||||
description: "Legacy driver national code",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
nationalCodeOfDriver: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: true,
|
|
||||||
description: "Legacy role relationship",
|
|
||||||
})
|
|
||||||
@IsBoolean()
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
driverIsInsurer: boolean;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: 13780624,
|
|
||||||
description: "Legacy policyholder birth date (Jalali)",
|
|
||||||
})
|
|
||||||
insurerBirthday: number | string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: 13780624,
|
|
||||||
description:
|
|
||||||
"Driver birth date (Jalali). Required when driverIsInsurer is false.",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
driverBirthday?: number | string | null;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
example: "IR123456789012345678901234",
|
example: "IR123456789012345678901234",
|
||||||
description:
|
description:
|
||||||
@@ -98,32 +15,6 @@ export class RunInquiriesV3Dto extends InquiryParticipantFieldsDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
sheba?: string;
|
sheba?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "123456789",
|
|
||||||
description: "Driver license (required when driverIsInsurer is false).",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
driverLicense?: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "123456789",
|
|
||||||
description: "Insurer license (required when driverIsInsurer is true).",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
insurerLicense?: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1",
|
|
||||||
description:
|
|
||||||
"Driving licence type code from GET /lookups/driving-licence-types. " +
|
|
||||||
"Optional — used in V4 FileMaker flow.",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
licenseType?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,57 +23,6 @@ export class RunInquiriesV3Dto extends InquiryParticipantFieldsDto {
|
|||||||
* First call = guilty party (+ auto claim). Second call = damaged party (THIRD_PARTY only).
|
* First call = guilty party (+ auto claim). Second call = damaged party (THIRD_PARTY only).
|
||||||
*/
|
*/
|
||||||
export class RunInquiriesVinV3Dto extends InquiryParticipantFieldsDto {
|
export class RunInquiriesVinV3Dto extends InquiryParticipantFieldsDto {
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "NAAM01E15HK123456",
|
|
||||||
description: "17-character VIN / chassis number (شماره شاسی)",
|
|
||||||
maxLength: 17,
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@MaxLength(17)
|
|
||||||
@ValidateIf((dto) => !dto.vehicle?.vin)
|
|
||||||
vin: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1234567890",
|
|
||||||
description: "Legacy third-party policyholder national code",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ValidateIf((dto) => !dto.thirdPartyPolicyholder)
|
|
||||||
nationalCodeOfInsurer: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1234567890",
|
|
||||||
description: "Legacy driver national code",
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
nationalCodeOfDriver: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: true,
|
|
||||||
description: "Legacy role relationship",
|
|
||||||
})
|
|
||||||
@IsBoolean()
|
|
||||||
@ValidateIf((dto) => !dto.driver)
|
|
||||||
driverIsInsurer: boolean;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: 13780624,
|
|
||||||
description: "Legacy policyholder birth date (Jalali)",
|
|
||||||
})
|
|
||||||
insurerBirthday: number | string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: 13780624,
|
|
||||||
description:
|
|
||||||
"Driver birth date (Jalali). Required when driverIsInsurer is false.",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
driverBirthday?: number | string | null;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
example: "IR123456789012345678901234",
|
example: "IR123456789012345678901234",
|
||||||
description:
|
description:
|
||||||
@@ -191,30 +31,4 @@ export class RunInquiriesVinV3Dto extends InquiryParticipantFieldsDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
sheba?: string;
|
sheba?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "123456789",
|
|
||||||
description: "Driver license (required when driverIsInsurer is false).",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
driverLicense?: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "123456789",
|
|
||||||
description: "Insurer license (required when driverIsInsurer is true).",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
insurerLicense?: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
example: "1",
|
|
||||||
description:
|
|
||||||
"Driving licence type code from GET /lookups/driving-licence-types. " +
|
|
||||||
"Optional — used in V4 FileMaker flow.",
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
licenseType?: string;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,22 +66,34 @@ describe("inquiry participant resolver", () => {
|
|||||||
).toBe(VehicleRegistrationState.CURRENT);
|
).toBe(VehicleRegistrationState.CURRENT);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps legacy driver/policyholder payloads working during rollout", () => {
|
it("rejects the removed flat inquiry contract", () => {
|
||||||
const resolved = resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
expect(() =>
|
||||||
nationalCodeOfDriver: "0012345678",
|
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||||||
driverBirthday: "1370/01/01",
|
nationalCodeOfDriver: "0012345678",
|
||||||
driverLicense: "123456789",
|
driverBirthday: "1370/01/01",
|
||||||
nationalCodeOfInsurer: "0098765432",
|
driverLicense: "123456789",
|
||||||
insurerBirthday: "1360/02/02",
|
nationalCodeOfInsurer: "0098765432",
|
||||||
driverIsInsurer: false,
|
insurerBirthday: "1360/02/02",
|
||||||
} as any);
|
driverIsInsurer: false,
|
||||||
|
} as any),
|
||||||
|
).toThrow(BadRequestException);
|
||||||
|
});
|
||||||
|
|
||||||
expect(resolved.legacy).toBe(true);
|
it("rejects phone numbers in participant inquiry identity", () => {
|
||||||
expect(resolved.roles.driver).toBe("DRIVER");
|
expect(() =>
|
||||||
expect(resolved.roles.thirdPartyPolicyholder).toBe(
|
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||||||
"THIRD_PARTY_POLICYHOLDER",
|
driver: {
|
||||||
);
|
nationalCode: "0012345678",
|
||||||
expect(resolved.roles.vehicleOwner).toBeUndefined();
|
birthday: "1370/01/01",
|
||||||
|
hasDrivingLicense: false,
|
||||||
|
phoneNumber: "09120000000",
|
||||||
|
},
|
||||||
|
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
|
||||||
|
thirdPartyPolicyholder: {
|
||||||
|
sameAs: InquiryParticipantRole.DRIVER,
|
||||||
|
},
|
||||||
|
} as any),
|
||||||
|
).toThrow(BadRequestException);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps third-party and car-body policyholders distinct", () => {
|
it("keeps third-party and car-body policyholders distinct", () => {
|
||||||
@@ -116,11 +128,13 @@ describe("inquiry participant resolver", () => {
|
|||||||
|
|
||||||
it("projects role-complete input onto the existing inquiry fields", () => {
|
it("projects role-complete input onto the existing inquiry fields", () => {
|
||||||
const normalized = normalizeInquirySubmission(BlameRequestType.CAR_BODY, {
|
const normalized = normalizeInquirySubmission(BlameRequestType.CAR_BODY, {
|
||||||
plate: {
|
vehicle: {
|
||||||
leftDigits: "44",
|
currentPlate: {
|
||||||
centerAlphabet: "ب",
|
leftDigits: "44",
|
||||||
centerDigits: "111",
|
centerAlphabet: "ب",
|
||||||
ir: "22",
|
centerDigits: "111",
|
||||||
|
ir: "22",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
driver: {
|
driver: {
|
||||||
nationalCode: "0012345678",
|
nationalCode: "0012345678",
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export interface ResolvedInquiryParticipant {
|
|||||||
nationalCode: string;
|
nationalCode: string;
|
||||||
birthday: string;
|
birthday: string;
|
||||||
fullName?: string;
|
fullName?: string;
|
||||||
phoneNumber?: string;
|
|
||||||
hasDrivingLicense?: boolean;
|
hasDrivingLicense?: boolean;
|
||||||
licenseNumber?: string;
|
licenseNumber?: string;
|
||||||
licenseType?: string;
|
licenseType?: string;
|
||||||
@@ -90,6 +89,11 @@ function requiredIdentity(
|
|||||||
role: InquiryParticipantRole,
|
role: InquiryParticipantRole,
|
||||||
input: InquiryParticipantInputDto,
|
input: InquiryParticipantInputDto,
|
||||||
): ResolvedInquiryParticipant {
|
): ResolvedInquiryParticipant {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(input, "phoneNumber")) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`${role} does not accept phoneNumber; phone numbers are collected separately from inquiry identity.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
const nationalCode = String(input.nationalCode ?? "").trim();
|
const nationalCode = String(input.nationalCode ?? "").trim();
|
||||||
const birthday = String(input.birthday ?? "").trim();
|
const birthday = String(input.birthday ?? "").trim();
|
||||||
if (!nationalCode || !birthday) {
|
if (!nationalCode || !birthday) {
|
||||||
@@ -118,7 +122,6 @@ function requiredIdentity(
|
|||||||
nationalCode,
|
nationalCode,
|
||||||
birthday,
|
birthday,
|
||||||
...(input.fullName ? { fullName: input.fullName } : {}),
|
...(input.fullName ? { fullName: input.fullName } : {}),
|
||||||
...(input.phoneNumber ? { phoneNumber: input.phoneNumber } : {}),
|
|
||||||
...(input.hasDrivingLicense != null
|
...(input.hasDrivingLicense != null
|
||||||
? { hasDrivingLicense: input.hasDrivingLicense }
|
? { hasDrivingLicense: input.hasDrivingLicense }
|
||||||
: {}),
|
: {}),
|
||||||
@@ -129,52 +132,17 @@ function requiredIdentity(
|
|||||||
|
|
||||||
export function resolveInquiryParticipants(
|
export function resolveInquiryParticipants(
|
||||||
caseType: BlameRequestType,
|
caseType: BlameRequestType,
|
||||||
input: InquiryParticipantFieldsDto & Record<string, any>,
|
input: Partial<InquiryParticipantFieldsDto> & Record<string, any>,
|
||||||
options: InquiryParticipantResolutionOptions = {},
|
options: InquiryParticipantResolutionOptions = {},
|
||||||
): ResolvedInquiryParticipants {
|
): ResolvedInquiryParticipants {
|
||||||
|
assertStructuredInquiryInput(input);
|
||||||
const hasRoleCompleteInput = Object.values(ROLE_FIELDS).some(
|
const hasRoleCompleteInput = Object.values(ROLE_FIELDS).some(
|
||||||
(field) => input[field] != null,
|
(field) => input[field] != null,
|
||||||
);
|
);
|
||||||
if (!hasRoleCompleteInput) {
|
if (!hasRoleCompleteInput) {
|
||||||
const driverId = InquiryParticipantRole.DRIVER;
|
throw new BadRequestException(
|
||||||
const policyholderId = input.driverIsInsurer
|
"driver, vehicleOwner, and thirdPartyPolicyholder are required in the structured inquiry format.",
|
||||||
? driverId
|
);
|
||||||
: InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER;
|
|
||||||
const participants: ResolvedInquiryParticipant[] = [
|
|
||||||
{
|
|
||||||
participantId: driverId,
|
|
||||||
nationalCode: String(input.nationalCodeOfDriver ?? ""),
|
|
||||||
birthday: String(
|
|
||||||
input.driverBirthday ??
|
|
||||||
(input.driverIsInsurer ? input.insurerBirthday : "") ??
|
|
||||||
"",
|
|
||||||
),
|
|
||||||
...(input.driverLicense
|
|
||||||
? { licenseNumber: String(input.driverLicense) }
|
|
||||||
: {}),
|
|
||||||
...(input.licenseType
|
|
||||||
? { licenseType: String(input.licenseType) }
|
|
||||||
: {}),
|
|
||||||
...(input.userNoCertificate != null
|
|
||||||
? { hasDrivingLicense: !input.userNoCertificate }
|
|
||||||
: {}),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
if (policyholderId !== driverId) {
|
|
||||||
participants.push({
|
|
||||||
participantId: policyholderId,
|
|
||||||
nationalCode: String(input.nationalCodeOfInsurer ?? ""),
|
|
||||||
birthday: String(input.insurerBirthday ?? ""),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
participants,
|
|
||||||
roles: {
|
|
||||||
driver: driverId,
|
|
||||||
thirdPartyPolicyholder: policyholderId,
|
|
||||||
},
|
|
||||||
legacy: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
caseType === BlameRequestType.THIRD_PARTY &&
|
caseType === BlameRequestType.THIRD_PARTY &&
|
||||||
@@ -367,6 +335,33 @@ function normalizePlateForComparison(
|
|||||||
.join("|");
|
.join("|");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LEGACY_INQUIRY_FIELDS = [
|
||||||
|
"nationalCodeOfDriver",
|
||||||
|
"driverBirthday",
|
||||||
|
"driverLicense",
|
||||||
|
"licenseType",
|
||||||
|
"nationalCodeOfInsurer",
|
||||||
|
"insurerBirthday",
|
||||||
|
"insurerLicense",
|
||||||
|
"driverIsInsurer",
|
||||||
|
"userNoCertificate",
|
||||||
|
"plate",
|
||||||
|
"plateId",
|
||||||
|
"vin",
|
||||||
|
"isNewCar",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
function assertStructuredInquiryInput(input: Record<string, any>): void {
|
||||||
|
const legacyFields = LEGACY_INQUIRY_FIELDS.filter((field) =>
|
||||||
|
Object.prototype.hasOwnProperty.call(input, field),
|
||||||
|
);
|
||||||
|
if (legacyFields.length > 0) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`Legacy inquiry fields are not accepted: ${legacyFields.join(", ")}. Use driver, vehicleOwner, thirdPartyPolicyholder, carBodyPolicyholder, and vehicle.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function assertPreviousPlateInquiryMatchesVin(
|
export function assertPreviousPlateInquiryMatchesVin(
|
||||||
expectedVin: string,
|
expectedVin: string,
|
||||||
mapped: Record<string, any>,
|
mapped: Record<string, any>,
|
||||||
@@ -517,7 +512,11 @@ export function normalizeInquirySubmission<T extends Record<string, any>>(
|
|||||||
input: T,
|
input: T,
|
||||||
options: InquiryParticipantResolutionOptions = {},
|
options: InquiryParticipantResolutionOptions = {},
|
||||||
): NormalizedInquirySubmission<T> {
|
): NormalizedInquirySubmission<T> {
|
||||||
const participants = resolveInquiryParticipants(caseType, input, options);
|
const participants = resolveInquiryParticipants(
|
||||||
|
caseType,
|
||||||
|
input as any,
|
||||||
|
options,
|
||||||
|
);
|
||||||
const driver = participantForRole(
|
const driver = participantForRole(
|
||||||
participants,
|
participants,
|
||||||
InquiryParticipantRole.DRIVER,
|
InquiryParticipantRole.DRIVER,
|
||||||
@@ -539,43 +538,32 @@ export function normalizeInquirySubmission<T extends Record<string, any>>(
|
|||||||
participants,
|
participants,
|
||||||
InquiryParticipantRole.CAR_BODY_POLICYHOLDER,
|
InquiryParticipantRole.CAR_BODY_POLICYHOLDER,
|
||||||
);
|
);
|
||||||
const vehicle = input.vehicle
|
const vehicle = resolveInquiryVehicle(
|
||||||
? resolveInquiryVehicle(input.vehicle as InquiryVehicleInputDto)
|
input.vehicle as InquiryVehicleInputDto,
|
||||||
: undefined;
|
);
|
||||||
if (
|
|
||||||
vehicle &&
|
|
||||||
input.plate &&
|
|
||||||
normalizePlateForComparison(vehicle.currentPlate) !==
|
|
||||||
normalizePlateForComparison(input.plate)
|
|
||||||
) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"plate and vehicle.currentPlate must identify the same current plate.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const sameDriverAndPolicyholder =
|
const sameDriverAndPolicyholder =
|
||||||
participants.roles.driver === participants.roles.thirdPartyPolicyholder;
|
participants.roles.driver === participants.roles.thirdPartyPolicyholder;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dto: {
|
dto: {
|
||||||
...input,
|
...input,
|
||||||
...(vehicle?.currentPlate && !input.plate
|
plate: vehicle.currentPlate,
|
||||||
? { plate: vehicle.currentPlate }
|
vin: vehicle.vin,
|
||||||
: {}),
|
|
||||||
...(vehicle?.vin && !input.vin ? { vin: vehicle.vin } : {}),
|
|
||||||
nationalCodeOfDriver: driver.nationalCode,
|
nationalCodeOfDriver: driver.nationalCode,
|
||||||
driverBirthday: driver.birthday,
|
driverBirthday: driver.birthday,
|
||||||
driverLicense: driver.licenseNumber,
|
driverLicense: driver.licenseNumber,
|
||||||
licenseType: driver.licenseType,
|
licenseType: driver.licenseType,
|
||||||
userNoCertificate:
|
userNoCertificate:
|
||||||
driver.hasDrivingLicense == null
|
driver.hasDrivingLicense == null
|
||||||
? input.userNoCertificate
|
? undefined
|
||||||
: !driver.hasDrivingLicense,
|
: !driver.hasDrivingLicense,
|
||||||
nationalCodeOfInsurer: thirdPartyPolicyholder.nationalCode,
|
nationalCodeOfInsurer: thirdPartyPolicyholder.nationalCode,
|
||||||
insurerBirthday: thirdPartyPolicyholder.birthday,
|
insurerBirthday: thirdPartyPolicyholder.birthday,
|
||||||
driverIsInsurer: sameDriverAndPolicyholder,
|
driverIsInsurer: sameDriverAndPolicyholder,
|
||||||
insurerLicense: sameDriverAndPolicyholder
|
insurerLicense: sameDriverAndPolicyholder
|
||||||
? driver.licenseNumber
|
? driver.licenseNumber
|
||||||
: input.insurerLicense,
|
: undefined,
|
||||||
|
isNewCar: vehicle.isNewCar,
|
||||||
} as NormalizedInquirySubmission<T>["dto"],
|
} as NormalizedInquirySubmission<T>["dto"],
|
||||||
participants,
|
participants,
|
||||||
driver,
|
driver,
|
||||||
|
|||||||
Reference in New Issue
Block a user