Merge pull request 'main' (#340) from s.yahyaee/yara724-api:main into main

Reviewed-on: Yara724/api#340
This commit is contained in:
2026-09-27 10:22:06 +03:30
17 changed files with 343 additions and 15 deletions

View File

@@ -483,6 +483,7 @@ describe("buildInsurerFileReport", () => {
nationalCode: "0012345678",
birthday: "1370/01/01",
hasDrivingLicense: true,
driverLicenseDate: "1400/01/01",
licenseNumber: "123456789",
licenseType: "BASE_2",
},

View File

@@ -732,7 +732,10 @@ function buildDriverSection(
},
{
label: PR.licenseDate,
value: licenseDate ?? asString(person.driverLicense),
value:
asString(person.driverLicenseDate) ||
licenseDate ||
asString(person.driverLicense),
},
{ label: PR.phone, value: asString(person.phoneNumber) },
{ label: PR.nationalCode, value: asString(person.nationalCodeOfDriver) },

View File

@@ -170,6 +170,7 @@ import { RoleEnum } from "src/Types&Enums/role.enum";
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
import { InquiryParticipantRole } from "src/common/dto/inquiry-participants.dto";
import { participantForStoredPartyRole } from "src/request-management/inquiry-participant-resolver";
import type { InquiryParticipantRoleAssignments } from "src/request-management/inquiry-participant-resolver";
import { UserRatingDto } from "./dto/user-rating.dto";
import {
canFinalizeExpertResend,
@@ -5905,7 +5906,7 @@ export class ClaimRequestManagementService {
role?: PartyRole;
person?: { userId?: Types.ObjectId; nationalCodeOfInsurer?: string };
participants?: Array<Record<string, any>>;
participantRoles?: Record<string, string | undefined>;
participantRoles?: Partial<InquiryParticipantRoleAssignments>;
statement?: { admitsGuilt?: boolean };
}>,
guiltyPartyId?: Types.ObjectId | string | null,
@@ -12560,6 +12561,7 @@ export class ClaimRequestManagementService {
currentStep: c.workflow?.currentStep || "",
createdAt: c.createdAt,
blameRequestId: c.blameRequestId?.toString(),
partyIdentities: c.parties ?? c.snapshot?.parties,
blameType: blameForItem?.type ?? undefined,
creationMethod: blameForItem?.creationMethod ?? undefined,
unifiedFileStatus: resolveUnifiedFileStatus({

View File

@@ -8,6 +8,7 @@ import {
type FanavaranCarPolicyProduct,
} from "src/lookups/fanavaran-last-car-policy";
import { resolvePolicyholderFromParty } from "./fanavaran-party-roles";
import type { InquiryParticipantRoleAssignments } from "src/request-management/inquiry-participant-resolver";
export type FanavaranClaimProduct = FanavaranCarPolicyProduct;
@@ -103,7 +104,7 @@ type BlamePartyForCarBodyPolicy = {
role?: string;
person?: { nationalCodeOfInsurer?: unknown };
participants?: Array<Record<string, any>>;
participantRoles?: Record<string, string | undefined>;
participantRoles?: Partial<InquiryParticipantRoleAssignments>;
insurance?: {
carBodyInsurance?: {
policyId?: unknown;

View File

@@ -1,4 +1,5 @@
import { InquiryParticipantRole } from "src/common/dto/inquiry-participants.dto";
import type { InquiryParticipantRoleAssignments } from "src/request-management/inquiry-participant-resolver";
import { participantForStoredPartyRole } from "src/request-management/inquiry-participant-resolver";
import {
normalizeNationalCode,
@@ -24,7 +25,7 @@ export type FanavaranRoleIdentity = {
type PartyLike = {
participants?: Array<Record<string, any>>;
participantRoles?: Record<string, string | undefined>;
participantRoles?: Partial<InquiryParticipantRoleAssignments>;
person?: Record<string, any> | null;
};

View File

@@ -81,6 +81,14 @@ export class InquiryParticipantInputDto {
@IsOptional()
@IsString()
licenseType?: string;
@ApiPropertyOptional({
description:
"Driver licence issue date. Required only for a driver who has a licence; Jalali YYYY/MM/DD is recommended.",
example: "1400/01/15",
})
@IsOptional()
driverLicenseDate?: string | number;
}
export class InquiryVehicleInputDto {
@@ -168,6 +176,9 @@ export class InquiryParticipantFieldsDto {
@ApiHideProperty()
driverLicense?: string;
@ApiHideProperty()
driverLicenseDate?: string;
@ApiHideProperty()
licenseType?: string;

View File

@@ -86,6 +86,26 @@ export class ListQueryV2Dto {
@MaxLength(64)
search?: string;
@ApiPropertyOptional({
description: "Filter cases by a phone number found on any party.",
example: "09121234567",
maxLength: 32,
})
@IsOptional()
@IsString()
@MaxLength(32)
phoneNumber?: string;
@ApiPropertyOptional({
description: "Filter cases by a national code found on any party.",
example: "0012345678",
maxLength: 32,
})
@IsOptional()
@IsString()
@MaxLength(32)
nationalCode?: string;
@ApiPropertyOptional({
enum: UNIFIED_FILE_STATUS_KEYS,
description:

View File

@@ -778,6 +778,7 @@ export class ExpertClaimService {
creationMethod?: string;
carBodyFirstForm?: { car?: boolean; object?: boolean };
blameStatus?: string;
partyIdentities?: Array<Record<string, unknown>>;
} {
if (!blame?.type) return {};
const blameRequestType = blame.type as BlameRequestType;
@@ -786,12 +787,26 @@ export class ExpertClaimService {
creationMethod?: string;
carBodyFirstForm?: { car?: boolean; object?: boolean };
blameStatus?: string;
partyIdentities?: Array<Record<string, unknown>>;
} = { blameRequestType };
if (blame.creationMethod) out.creationMethod = blame.creationMethod;
out.blameStatus = blame.blameStatus;
if (blameRequestType !== BlameRequestType.CAR_BODY) return out;
const parties = blame.parties;
if (Array.isArray(parties) && parties.length > 0) {
out.partyIdentities = parties.map((party: any) => ({
phoneNumber: party?.person?.phoneNumber,
nationalCode: party?.person?.nationalCode,
nationalCodeOfInsurer: party?.person?.nationalCodeOfInsurer,
nationalCodeOfDriver: party?.person?.nationalCodeOfDriver,
inquiryParticipants: Array.isArray(party?.inquiryParticipants)
? party.inquiryParticipants.map((participant: any) => ({
phoneNumber: participant?.phoneNumber,
nationalCode: participant?.nationalCode,
}))
: undefined,
}));
}
if (blameRequestType !== BlameRequestType.CAR_BODY) return out;
if (!Array.isArray(parties) || parties.length === 0) return out;
const first =
@@ -4393,6 +4408,8 @@ export class ExpertClaimService {
? { carName: v.carName, carModel: v.carModel, carType: v.carType }
: undefined,
...fileCtx,
partyIdentities:
fileCtx.partyIdentities ?? c.parties ?? c.snapshot?.parties,
blameRequestId: c.blameRequestId?.toString(),
createdAt: c.createdAt,
awaitingFactorValidation,
@@ -4486,6 +4503,8 @@ export class ExpertClaimService {
? { carName: v.carName, carModel: v.carModel, carType: v.carType }
: undefined,
...fileCtx,
partyIdentities:
fileCtx.partyIdentities ?? c.parties ?? c.snapshot?.parties,
blameRequestId: c.blameRequestId?.toString(),
createdAt: c.createdAt,
awaitingFactorValidation: claimIsAwaitingExpertFactorValidationV2(c),
@@ -4625,6 +4644,8 @@ export class ExpertClaimService {
? { carName: v.carName, carModel: v.carModel, carType: v.carType }
: undefined,
...fileCtx,
partyIdentities:
fileCtx.partyIdentities ?? c.parties ?? c.snapshot?.parties,
blameRequestId: c.blameRequestId?.toString(),
createdAt: c.createdAt,
awaitingFactorValidation: claimIsAwaitingExpertFactorValidationV2(c),
@@ -4720,6 +4741,8 @@ export class ExpertClaimService {
? { carName: v.carName, carModel: v.carModel, carType: v.carType }
: undefined,
...fileCtx,
partyIdentities:
fileCtx.partyIdentities ?? c.parties ?? c.snapshot?.parties,
blameRequestId: c.blameRequestId?.toString(),
createdAt: c.createdAt,
awaitingFactorValidation: claimIsAwaitingExpertFactorValidationV2(c),

View File

@@ -26,6 +26,45 @@ describe("applyListQueryV2", () => {
expect(r.list[0].publicId).toBe("A200");
});
it("filters nested party identities and normalizes Persian digits", () => {
const identityRows = [
{
publicId: "PHONE",
createdAt: "2026-01-01",
status: "OPEN",
parties: [
{
person: {
phoneNumber: "0912-123-4567",
nationalCodeOfDriver: "۰۰۱۲۳۴۵۶۷۸",
},
},
],
},
{
publicId: "OTHER",
createdAt: "2026-01-02",
status: "OPEN",
parties: [{ person: { phoneNumber: "09351234567", nationalCode: "1111111111" } }],
},
];
const identityGetters = {
publicId: (r: (typeof identityRows)[0]) => r.publicId,
createdAt: (r: (typeof identityRows)[0]) => r.createdAt,
status: (r: (typeof identityRows)[0]) => r.status,
};
const byPhone = applyListQueryV2(identityRows, identityGetters, {
phoneNumber: "09121234567",
});
expect(byPhone.list.map((row) => row.publicId)).toEqual(["PHONE"]);
const byNationalCode = applyListQueryV2(identityRows, identityGetters, {
nationalCode: "0012345678",
});
expect(byNationalCode.list.map((row) => row.publicId)).toEqual(["PHONE"]);
});
it("sorts by publicId ascending", () => {
const r = applyListQueryV2(rows, getters, {
sortBy: "publicId",

View File

@@ -53,6 +53,48 @@ function normalizeSearch(raw?: string): string | undefined {
return t || undefined;
}
function normalizeIdentityValue(raw?: unknown): string {
return String(raw ?? "")
.trim()
.replace(/[۰-۹]/g, (digit) => String("۰۱۲۳۴۵۶۷۸۹".indexOf(digit)))
.replace(/[٠-٩]/g, (digit) => String("٠١٢٣٤٥٦٧٨٩".indexOf(digit)))
.replace(/\D/g, "");
}
function isPhoneKey(key: string): boolean {
return /phone|mobile|tel/i.test(key);
}
function isNationalCodeKey(key: string): boolean {
return /national[_-]?(code|id)|code[_-]?mell?i|mell?i[_-]?code/i.test(key);
}
function collectIdentityValues(
value: unknown,
matchKey: (key: string) => boolean,
): string[] {
const values: string[] = [];
const seen = new WeakSet<object>();
const visit = (current: unknown, depth: number) => {
if (current == null || depth > 8 || typeof current !== "object") return;
const object = current as Record<string, unknown>;
if (seen.has(object)) return;
seen.add(object);
for (const [key, child] of Object.entries(object)) {
if (
matchKey(key) &&
(typeof child === "string" || typeof child === "number")
) {
const normalized = normalizeIdentityValue(child);
if (normalized) values.push(normalized);
}
if (child && typeof child === "object") visit(child, depth + 1);
}
};
visit(value, 0);
return values;
}
function toTime(v: Date | string | number | undefined): number {
if (v == null) return 0;
const n = v instanceof Date ? v.getTime() : new Date(v as string | number).getTime();
@@ -75,12 +117,28 @@ export function applyListQueryV2<T>(
totalPages?: number;
} {
const search = normalizeSearch(query.search);
const phoneNumber = normalizeIdentityValue(query.phoneNumber);
const nationalCode = normalizeIdentityValue(query.nationalCode);
let filtered = rows;
if (query.fileType && getters.fileType) {
filtered = filtered.filter(
(row) => getters.fileType!(row) === query.fileType,
);
}
if (phoneNumber) {
filtered = filtered.filter((row) =>
collectIdentityValues(row, isPhoneKey).some((value) =>
value.includes(phoneNumber),
),
);
}
if (nationalCode) {
filtered = filtered.filter((row) =>
collectIdentityValues(row, isNationalCodeKey).some((value) =>
value.includes(nationalCode),
),
);
}
if (search) {
filtered = filtered.filter((row) => {
const chunks: string[] = [];

View File

@@ -1,5 +1,5 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsNotEmpty, IsOptional, IsString } from "class-validator";
import { IsOptional, IsString } from "class-validator";
import { Types } from "mongoose";
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum";
@@ -51,8 +51,13 @@ export class FirstPartyFileDto {
}
export class DescriptionDto {
@ApiProperty({ description: "Accident description (required for all types)" })
desc: string;
@ApiPropertyOptional({
description:
"Accident description. Optional for THIRD_PARTY; CAR_BODY requires at least 100 non-whitespace characters.",
})
@IsOptional()
@IsString()
desc?: string;
@ApiPropertyOptional({
description: "CAR_BODY only. Ignored for THIRD_PARTY.",
@@ -94,10 +99,13 @@ export class DescriptionDto {
* front-end can display them consistently regardless of blame type.
*/
export class DescriptionV4Dto {
@ApiProperty({ description: "Accident description" })
@ApiPropertyOptional({
description:
"Accident description. Optional for THIRD_PARTY; CAR_BODY requires at least 100 non-whitespace characters.",
})
@IsOptional()
@IsString()
@IsNotEmpty()
desc: string;
desc?: string;
@ApiPropertyOptional({
description:

View File

@@ -23,6 +23,7 @@ export class Person {
@Prop() nationalCodeOfDriver?: string;
@Prop() insurerLicense?: string;
@Prop() driverLicense?: string;
@Prop() driverLicenseDate?: string;
/** Driving licence type code from the Fanavaran lookup (GET /lookups/driving-licence-types). */
@Prop() licenseType?: string;
@@ -66,6 +67,7 @@ export class InquiryParticipant {
@Prop({ type: Boolean }) hasDrivingLicense?: boolean;
@Prop() licenseNumber?: string;
@Prop() licenseType?: string;
@Prop() driverLicenseDate?: string;
}
export const InquiryParticipantSchema =
SchemaFactory.createForClass(InquiryParticipant);

View File

@@ -53,6 +53,7 @@ export class CarDetail {
insurerBirthday?: string;
driverBirthday?: string | null;
driverLicenseDate?: string;
}
export class InsuranceDetail {

View File

@@ -16,6 +16,7 @@ describe("inquiry participant persistence", () => {
nationalCode: "0022324224",
birthday: "1378/04/26",
hasDrivingLicense: true,
driverLicenseDate: "1400/01/01",
licenseNumber: "1001011111",
licenseType: "10",
},
@@ -60,6 +61,9 @@ describe("inquiry participant persistence", () => {
expect(request.validateSync()).toBeUndefined();
expect(request.parties[0].person.insurerBirthday).toBe("1378/06/27");
expect(request.parties[0].person.driverBirthday).toBe("1378/04/26");
expect(request.parties[0].participants?.[0].driverLicenseDate).toBe(
"1400/01/01",
);
});
it("recasts populated party subdocuments during an inquiry-audit update", () => {
@@ -89,6 +93,7 @@ describe("inquiry participant persistence", () => {
nationalCode: "0022324224",
birthday: "1378/04/26",
hasDrivingLicense: true,
driverLicenseDate: "1400/01/01",
licenseNumber: "1001011111",
licenseType: "10",
},

View File

@@ -14,15 +14,60 @@ import {
resolveInquiryVehicle,
runCurrentPlateInquiry,
sanitizeStoredInquiryParticipants,
validateDriverLicenseDate,
} from "./inquiry-participant-resolver";
describe("inquiry participant resolver", () => {
it("accepts a driver licence date within the ten-year window", () => {
expect(validateDriverLicenseDate("2016-09-26", "2026-09-26")).toBe(
"2016-09-26",
);
});
it("rejects future and older-than-ten-year driver licence dates", () => {
expect(() =>
validateDriverLicenseDate("2026-09-27", "2026-09-26"),
).toThrow("نمی‌تواند در آینده");
expect(() =>
validateDriverLicenseDate("2016-09-25", "2026-09-26"),
).toThrow("بیشتر از ۱۰ سال قبل");
});
it("requires the date only for a licensed driver", () => {
expect(() =>
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
driver: {
nationalCode: "0012345678",
birthday: "1370/01/01",
hasDrivingLicense: true,
licenseNumber: "123456789",
licenseType: "1",
},
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
thirdPartyPolicyholder: { sameAs: InquiryParticipantRole.DRIVER },
}),
).toThrow("تاریخ صدور گواهینامه");
expect(() =>
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
driver: {
nationalCode: "0012345678",
birthday: "1370/01/01",
hasDrivingLicense: false,
},
vehicleOwner: { sameAs: InquiryParticipantRole.DRIVER },
thirdPartyPolicyholder: { sameAs: InquiryParticipantRole.DRIVER },
}),
).not.toThrow();
});
it("links several roles to one explicitly shared person", () => {
const resolved = resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
driver: {
nationalCode: "0012345678",
birthday: "1370/01/01",
hasDrivingLicense: true,
driverLicenseDate: "1400/01/01",
licenseNumber: "123456789",
licenseType: "1",
},
@@ -231,6 +276,7 @@ describe("inquiry participant resolver", () => {
nationalCode: "0012345678",
birthday: "1370/01/01",
hasDrivingLicense: true,
driverLicenseDate: "1400/01/01",
licenseNumber: "D-1",
licenseType: "1",
},

View File

@@ -18,6 +18,7 @@ export interface ResolvedInquiryParticipant {
hasDrivingLicense?: boolean;
licenseNumber?: string;
licenseType?: string;
driverLicenseDate?: string;
}
export interface InquiryParticipantRoleAssignments {
@@ -38,6 +39,7 @@ export interface NormalizedInquirySubmission<T extends Record<string, any>> {
nationalCodeOfDriver: string;
driverBirthday: string;
driverLicense?: string;
driverLicenseDate?: string;
licenseType?: string;
userNoCertificate?: boolean;
nationalCodeOfInsurer: string;
@@ -215,6 +217,29 @@ function requiredIdentity(
"شماره و نوع گواهینامه برای راننده دارای گواهینامه الزامی است.",
);
}
if (
role !== InquiryParticipantRole.DRIVER &&
Object.prototype.hasOwnProperty.call(input, "driverLicenseDate")
) {
throw new BadRequestException(
"تاریخ صدور گواهینامه فقط برای راننده قابل ثبت است.",
);
}
const driverLicenseDate =
role === InquiryParticipantRole.DRIVER && input.hasDrivingLicense === true
? validateDriverLicenseDate(input.driverLicenseDate)
: undefined;
if (
role === InquiryParticipantRole.DRIVER &&
input.hasDrivingLicense === false &&
Object.prototype.hasOwnProperty.call(input, "driverLicenseDate") &&
input.driverLicenseDate != null &&
String(input.driverLicenseDate).trim() !== ""
) {
throw new BadRequestException(
"تاریخ صدور گواهینامه برای راننده فاقد گواهینامه قابل ثبت نیست.",
);
}
return {
participantId: role,
nationalCode,
@@ -225,9 +250,38 @@ function requiredIdentity(
: {}),
...(input.licenseNumber ? { licenseNumber: input.licenseNumber } : {}),
...(input.licenseType ? { licenseType: input.licenseType } : {}),
...(driverLicenseDate ? { driverLicenseDate } : {}),
};
}
/** Validate a driver licence issue date against the current Iran date. */
export function validateDriverLicenseDate(
value: unknown,
todayGregorian: string = gregorianDateInIran(new Date()),
): string {
const raw = String(value ?? "").trim();
const parsed = jalaliToGregorianDate(raw);
if (!parsed) {
throw new BadRequestException("تاریخ صدور گواهینامه راننده نامعتبر است.");
}
const today = jalaliToGregorianDate(todayGregorian) ?? todayGregorian;
const todayDate = new Date(`${today}T00:00:00Z`);
const minimumDate = new Date(todayDate);
minimumDate.setUTCFullYear(minimumDate.getUTCFullYear() - 10);
const minimum = minimumDate.toISOString().slice(0, 10);
if (parsed > today) {
throw new BadRequestException(
"تاریخ صدور گواهینامه راننده نمی‌تواند در آینده باشد.",
);
}
if (parsed < minimum) {
throw new BadRequestException(
"تاریخ صدور گواهینامه راننده نباید بیشتر از ۱۰ سال قبل باشد.",
);
}
return raw;
}
export function resolveInquiryParticipants(
caseType: BlameRequestType,
input: Partial<InquiryParticipantFieldsDto> & Record<string, any>,
@@ -413,6 +467,7 @@ const LEGACY_INQUIRY_FIELDS = [
"nationalCodeOfDriver",
"driverBirthday",
"driverLicense",
"driverLicenseDate",
"licenseType",
"nationalCodeOfInsurer",
"insurerBirthday",
@@ -555,6 +610,7 @@ export function normalizeInquirySubmission<T extends Record<string, any>>(
nationalCodeOfDriver: driver.nationalCode,
driverBirthday: driver.birthday,
driverLicense: driver.licenseNumber,
driverLicenseDate: driver.driverLicenseDate,
licenseType: driver.licenseType,
userNoCertificate:
driver.hasDrivingLicense == null

View File

@@ -211,6 +211,19 @@ export class RequestManagementService {
}
/** Keep eligibility rejections distinct from an unavailable CAR_BODY provider. */
private assertCarBodyDescriptionMinimum(
requestType: BlameRequestType | string | undefined,
description: unknown,
): void {
if (requestType !== BlameRequestType.CAR_BODY) return;
const length = String(description ?? "").trim().length;
if (length < 100) {
throw new BadRequestException(
"CAR_BODY description must contain at least 100 non-whitespace characters.",
);
}
}
private throwCarBodyInquiryFailure(
err: unknown,
context: "carBodyPlate" | "carBodyVin" = "carBodyPlate",
@@ -2021,6 +2034,7 @@ export class RequestManagementService {
party.person.nationalCodeOfDriver = body.nationalCodeOfDriver;
party.person.insurerLicense = body.insurerLicense;
party.person.driverLicense = body.driverLicense;
party.person.driverLicenseDate = body.driverLicenseDate;
party.person.driverIsInsurer = body.driverIsInsurer;
party.person.isNewCar = body.isNewCar;
party.person.userNoCertificate = body.userNoCertificate;
@@ -2448,6 +2462,7 @@ export class RequestManagementService {
party.person.nationalCodeOfDriver = body.nationalCodeOfDriver;
party.person.insurerLicense = body.insurerLicense;
party.person.driverLicense = body.driverLicense;
party.person.driverLicenseDate = body.driverLicenseDate;
party.person.driverIsInsurer = body.driverIsInsurer;
party.person.isNewCar = body.isNewCar;
party.person.userNoCertificate = body.userNoCertificate;
@@ -2817,6 +2832,8 @@ export class RequestManagementService {
this.isBlameOnBehalfActor(req, user),
);
this.assertCarBodyDescriptionMinimum(req.type, body.desc);
// Set userId for party if not already set (important for second party)
if (!party.person) party.person = {} as any;
if (
@@ -4120,6 +4137,8 @@ export class RequestManagementService {
throw new NotFoundException("Request not found");
}
this.assertCarBodyDescriptionMinimum(request.type, body.desc);
// --- First Party Flow ---
if (request.firstPartyDetails.firstPartyId == user.sub) {
// Build base update payload with proper MongoDB operators
@@ -6611,11 +6630,26 @@ export class RequestManagementService {
);
const obj = req.toObject();
const partyIdentities = Array.isArray(req.parties)
? req.parties.map((entry: any) => ({
phoneNumber: entry?.person?.phoneNumber,
nationalCode: entry?.person?.nationalCode,
nationalCodeOfInsurer: entry?.person?.nationalCodeOfInsurer,
nationalCodeOfDriver: entry?.person?.nationalCodeOfDriver,
inquiryParticipants: Array.isArray(entry?.inquiryParticipants)
? entry.inquiryParticipants.map((participant: any) => ({
phoneNumber: participant?.phoneNumber,
nationalCode: participant?.nationalCode,
}))
: undefined,
}))
: [];
delete obj.parties;
return {
...obj,
partyIdentities,
userSide: party?.role ?? null,
initiatedByMe: isInitiator,
unifiedFileStatus: resolveUnifiedFileStatus({
@@ -7091,9 +7125,10 @@ export class RequestManagementService {
if (!formData.carBodyForm) {
throw new BadRequestException("carBodyForm is required.");
}
if (!formData?.expertDescription?.desc) {
throw new BadRequestException("expertDescription.desc is required.");
}
this.assertCarBodyDescriptionMinimum(
req.type,
formData?.expertDescription?.desc,
);
const firstPartyUserId = await this.getOrCreateUserByPhoneNumber(
formData.firstPartyPhoneNumber,
@@ -7272,6 +7307,7 @@ export class RequestManagementService {
nationalCodeOfDriver: firstPartyPlate.nationalCodeOfDriver,
insurerLicense: firstPartyPlate.insurerLicense,
driverLicense: firstPartyPlate.driverLicense,
driverLicenseDate: firstPartyPlate.driverLicenseDate,
driverIsInsurer: firstPartyPlate.driverIsInsurer,
isNewCar: firstPartyPlate.isNewCar,
userNoCertificate: firstPartyPlate.userNoCertificate,
@@ -7560,6 +7596,7 @@ export class RequestManagementService {
nationalCodeOfDriver: plateDto.nationalCodeOfDriver,
insurerLicense: plateDto.insurerLicense,
driverLicense: plateDto.driverLicense,
driverLicenseDate: plateDto.driverLicenseDate,
driverIsInsurer: plateDto.driverIsInsurer,
isNewCar: plateDto.isNewCar,
userNoCertificate: plateDto.userNoCertificate,
@@ -8286,6 +8323,7 @@ export class RequestManagementService {
carName: sandHubReport?.MapTypNam || sandHubReport?.CarName,
insurerBirthday: firstPartyPlate.insurerBirthday,
driverBirthday: firstPartyPlate.driverBirthday,
driverLicenseDate: firstPartyPlate.driverLicenseDate,
carType: `${sandHubReport?.UsageField || ""} / ${sandHubReport?.MapUsageName || "-"}`,
isNewCar: firstPartyPlate.isNewCar,
};
@@ -8632,6 +8670,11 @@ export class RequestManagementService {
);
}
this.assertCarBodyDescriptionMinimum(
request.type,
formData.firstPartyDescription?.desc,
);
this.assertAccidentDateTimeNotInFuture({
accidentDate: formData.firstPartyDescription?.accidentDate,
accidentTime: formData.firstPartyDescription?.accidentTime,
@@ -8772,6 +8815,7 @@ export class RequestManagementService {
carName: sandHubReport?.MapTypNam || sandHubReport?.CarName,
insurerBirthday: firstPartyPlate.insurerBirthday,
driverBirthday: firstPartyPlate.driverBirthday,
driverLicenseDate: firstPartyPlate.driverLicenseDate,
carType: `${sandHubReport?.UsageField || ""} / ${sandHubReport?.MapUsageName || "-"}`,
isNewCar: firstPartyPlate.isNewCar,
};
@@ -10107,6 +10151,8 @@ export class RequestManagementService {
party.person.insurerLicense = partyData.insurerLicense;
if (partyData.driverLicense)
party.person.driverLicense = partyData.driverLicense;
if ((partyData as any).driverLicenseDate)
party.person.driverLicenseDate = (partyData as any).driverLicenseDate;
if ((partyData as any).licenseType)
(party.person as any).licenseType = (partyData as any).licenseType;
@@ -11015,6 +11061,8 @@ export class RequestManagementService {
party.person.insurerLicense = partyData.insurerLicense;
if (partyData.driverLicense)
party.person.driverLicense = partyData.driverLicense;
if ((partyData as any).driverLicenseDate)
party.person.driverLicenseDate = (partyData as any).driverLicenseDate;
if ((partyData as any).licenseType)
(party.person as any).licenseType = (partyData as any).licenseType;
@@ -11829,6 +11877,7 @@ export class RequestManagementService {
await this.verifyExpertAccessForBlameV2(req, actor);
const role = this.resolvePartyRoleV3(req, partyRole);
this.assertBlameV3PartyDetailPhase(req, role);
this.assertCarBodyDescriptionMinimum(req.type, body.desc);
// accidentDate and accidentTime are only required for the first (guilty)
// party — the second (damaged) party can skip them.
@@ -13198,6 +13247,7 @@ export class RequestManagementService {
driverBirthday: p.person?.driverBirthday,
insurerLicense: p.person?.insurerLicense,
driverLicense: p.person?.driverLicense,
driverLicenseDate: p.person?.driverLicenseDate,
userId: p.person?.userId ? String(p.person.userId) : undefined,
clientId: p.person?.clientId ? String(p.person.clientId) : undefined,
licenseType: p.person?.licenseType ?? undefined,
@@ -13440,6 +13490,7 @@ export class RequestManagementService {
driverBirthday: p.person?.driverBirthday,
insurerLicense: p.person?.insurerLicense,
driverLicense: p.person?.driverLicense,
driverLicenseDate: p.person?.driverLicenseDate,
userId: p.person?.userId ? String(p.person.userId) : undefined,
clientId: p.person?.clientId ? String(p.person.clientId) : undefined,
licenseType: p.person?.licenseType ?? undefined,