1
0
forked from Yara724/api
This commit is contained in:
2026-04-29 20:22:43 +03:30
parent 993d809de2
commit ebf8a9a624
10 changed files with 487 additions and 20 deletions

View File

@@ -110,7 +110,8 @@ export class ClaimSubmitResendV2Dto {
@ApiPropertyOptional({
type: [String],
enum: ClaimRequiredDocumentType,
description: "Extra document keys to upload (may include types not in the initial claim form).",
description:
"Document keys to re-upload; each value must be a ClaimRequiredDocumentType string (e.g. national_card).",
})
@IsOptional()
@IsArray()

View File

@@ -45,6 +45,7 @@ import {
initialClaimExpertReportBuckets,
} from "src/helpers/expert-panel-status-report";
import { ClaimWorkflowStep } from "src/Types&Enums/claim-request-management/claim-workflow-steps.enum";
import { ClaimRequiredDocumentType } from "src/Types&Enums/claim-request-management/required-document-type.enum";
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
import { GetClaimListV2ResponseDto, ClaimListItemV2Dto } from "./dto/claim-list-v2.dto";
@@ -229,6 +230,21 @@ export class ExpertClaimService {
});
}
/**
* Insurer tenant id stored on claim file-activity rows. Prefer `owner.userClientKey`
* so V2 events line up with `expireClaimWorkflowLockV2IfStale` and insurer `findByTenant`.
*/
private claimActivityTenantId(
claim: { owner?: { userClientKey?: unknown } } | null | undefined,
actor: { clientKey?: string },
): string {
const fromOwner = claim?.owner?.userClientKey;
if (fromOwner != null && String(fromOwner).length > 0) {
return String(fromOwner);
}
return String(actor.clientKey ?? "");
}
/** Owner mobile: linked blame party phone when available, else `users.mobile`. */
private async resolveClaimOwnerPhone(claim: any): Promise<string | undefined> {
if (!claim?.owner?.userId) return undefined;
@@ -1976,7 +1992,7 @@ export class ExpertClaimService {
await this.recordClaimExpertActivity({
expertId: String(actor.sub),
tenantId: String(actor.clientKey),
tenantId: this.claimActivityTenantId(claim, actor),
claimId: String(claimRequestId),
eventType: ExpertFileActivityType.CHECKED,
idempotencyKey: `claim:${claimRequestId}:checked:${actor.sub}`,
@@ -2076,7 +2092,7 @@ export class ExpertClaimService {
await this.recordClaimExpertActivity({
expertId: String(actor.sub),
tenantId: String(actor.clientKey),
tenantId: this.claimActivityTenantId(claim, actor),
claimId: String(claimRequestId),
eventType: ExpertFileActivityType.UNCHECKED,
idempotencyKey: `claim:${claimRequestId}:unchecked:resend:${actor.sub}`,
@@ -2228,6 +2244,14 @@ export class ExpertClaimService {
},
});
await this.recordClaimExpertActivity({
expertId: String(actor.sub),
tenantId: this.claimActivityTenantId(claim, actor),
claimId: String(claimRequestId),
eventType: ExpertFileActivityType.CHECKED,
idempotencyKey: `claim:${claimRequestId}:checked:v2:${actor.sub}`,
});
const ownerPhone = await this.resolveClaimOwnerPhone(claim);
if (ownerPhone) {
const expertLastName =
@@ -2293,10 +2317,38 @@ export class ExpertClaimService {
);
}
const docs = reply.resendDocuments ?? [];
const parts = reply.resendCarParts ?? [];
const allowedDocTypes = new Set<string>(
Object.values(ClaimRequiredDocumentType),
);
const rawDocs = reply.resendDocuments ?? [];
const docs: ClaimRequiredDocumentType[] = [];
for (const x of rawDocs) {
const s = String(x).trim();
if (!allowedDocTypes.has(s)) {
throw new BadRequestException(
`Invalid resendDocuments value: "${String(x)}". Must be a ClaimRequiredDocumentType string (e.g. national_card, damaged_driving_license_front).`,
);
}
docs.push(s as ClaimRequiredDocumentType);
}
const uniqueDocs = [...new Set(docs)];
const normalizedParts = (reply.resendCarParts ?? []).map((p) => {
const row: Record<string, unknown> = {
key: p.key,
label_fa: p.label_fa,
label_en: p.label_en,
captured: false,
};
const id = (p as { id?: number }).id;
if (typeof id === "number" && Number.isFinite(id)) {
row.id = Math.trunc(id);
}
return row;
});
const desc = String(reply.resendDescription ?? "").trim();
if (!desc && docs.length === 0 && parts.length === 0) {
if (!desc && uniqueDocs.length === 0 && normalizedParts.length === 0) {
throw new BadRequestException(
"Provide resendDescription and/or resendDocuments and/or resendCarParts.",
);
@@ -2313,8 +2365,8 @@ export class ExpertClaimService {
"workflow.nextStep": ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
"evaluation.damageExpertResend": {
resendDescription: desc || undefined,
resendDocuments: docs,
resendCarParts: parts,
resendDocuments: uniqueDocs,
resendCarParts: normalizedParts,
...(resendSnapshot && { expertProfileSnapshot: resendSnapshot }),
},
},
@@ -2334,8 +2386,8 @@ export class ExpertClaimService {
},
timestamp: new Date(),
metadata: {
documentCount: docs.length,
carPartCount: parts.length,
documentCount: uniqueDocs.length,
carPartCount: normalizedParts.length,
},
},
},
@@ -2511,7 +2563,7 @@ export class ExpertClaimService {
await this.recordClaimExpertActivity({
expertId: String(actor.sub),
tenantId: String(actor.clientKey),
tenantId: this.claimActivityTenantId(claim, actor),
claimId: String(claimRequestId),
eventType: ExpertFileActivityType.HANDLED,
idempotencyKey: `claim:${claimRequestId}:handled:${actor.sub}`,
@@ -2616,7 +2668,7 @@ export class ExpertClaimService {
await this.recordClaimExpertActivity({
expertId: String(actor.sub),
tenantId: String(actor.clientKey),
tenantId: this.claimActivityTenantId(claim, actor),
claimId: String(claimRequestId),
eventType: ExpertFileActivityType.HANDLED,
idempotencyKey: `claim:${claimRequestId}:handled:inperson:${actor.sub}`,