forked from Yara724/api
Compare commits
3 Commits
5a84080bf1
...
374b15933d
| Author | SHA1 | Date | |
|---|---|---|---|
| 374b15933d | |||
| 08a4d754c1 | |||
| c5b2d7b520 |
@@ -98,6 +98,37 @@ export class ClaimDetailV2ResponseDto {
|
|||||||
damageExpertReplyFinal?: unknown;
|
damageExpertReplyFinal?: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description:
|
||||||
|
'Car walk-around video from `claim-video-capture` (resolved from `media.videoCaptureId`).',
|
||||||
|
})
|
||||||
|
videoCapture?: {
|
||||||
|
id: string;
|
||||||
|
url?: string;
|
||||||
|
path?: string;
|
||||||
|
fileName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description:
|
||||||
|
'Linked blame case (`blameCases`), same shape as expert-blame detail: parties with video/voice URLs, workflow, expert, formatted dates.',
|
||||||
|
})
|
||||||
|
blameCase?: Record<string, unknown>;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description:
|
||||||
|
'Same as `blameCase.expert.decision` when present: guilty party id, description, accident fields, decidedAt.',
|
||||||
|
})
|
||||||
|
blameExpertDecision?: Record<string, unknown>;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Claim payout / banking metadata from the claim file (e.g. Sheba).',
|
||||||
|
})
|
||||||
|
money?: {
|
||||||
|
sheba?: string;
|
||||||
|
nationalCodeOfInsurer?: string;
|
||||||
|
};
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { DamageImageDbService } from "src/claim-request-management/entites/db-se
|
|||||||
import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service";
|
import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service";
|
||||||
import { ClientDbService } from "src/client/entities/db-service/client.db.service";
|
import { ClientDbService } from "src/client/entities/db-service/client.db.service";
|
||||||
import { buildFileLink } from "src/helpers/urlCreator";
|
import { buildFileLink } from "src/helpers/urlCreator";
|
||||||
|
import { toJalaliDateAndTime } from "src/helpers/date-jalali";
|
||||||
import { BlameDocumentDbService } from "src/request-management/entities/db-service/blame-document.db.service";
|
import { BlameDocumentDbService } from "src/request-management/entities/db-service/blame-document.db.service";
|
||||||
import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service";
|
import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service";
|
||||||
import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service";
|
import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service";
|
||||||
@@ -53,6 +54,10 @@ import { BranchDbService } from "src/client/entities/db-service/branch.db.servic
|
|||||||
import { ClaimSubmitResendV2Dto } from "./dto/expert-claim-v2.dto";
|
import { ClaimSubmitResendV2Dto } from "./dto/expert-claim-v2.dto";
|
||||||
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
||||||
import { FactorValidationV2Dto } from "./dto/factor-validation.dto";
|
import { FactorValidationV2Dto } from "./dto/factor-validation.dto";
|
||||||
|
import {
|
||||||
|
buildMutualAgreementExpertDecision,
|
||||||
|
enrichBlamePartiesForAgreementView,
|
||||||
|
} from "src/helpers/blame-party-agreement-decision";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExpertClaimService {
|
export class ExpertClaimService {
|
||||||
@@ -2410,6 +2415,119 @@ export class ExpertClaimService {
|
|||||||
return { list, total: list.length };
|
return { list, total: list.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load linked blame case for damage-expert claim detail (same enrichment as expert-blame `findOneV2`:
|
||||||
|
* party evidence `videoUrl` / `voiceUrls`, Jalali date strings).
|
||||||
|
*/
|
||||||
|
private async buildBlameCaseSnapshotForClaimDetail(
|
||||||
|
blameRequestId: string,
|
||||||
|
): Promise<Record<string, unknown> | null> {
|
||||||
|
const doc = await this.blameRequestDbService.findByIdWithoutHistory(
|
||||||
|
blameRequestId,
|
||||||
|
);
|
||||||
|
if (!doc) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const docRec = doc as Record<string, unknown>;
|
||||||
|
const built = buildMutualAgreementExpertDecision(docRec);
|
||||||
|
const existingDecision = (docRec.expert as Record<string, unknown> | undefined)
|
||||||
|
?.decision as Record<string, unknown> | undefined;
|
||||||
|
if (built && !existingDecision?.guiltyPartyId) {
|
||||||
|
const prevExpert =
|
||||||
|
typeof docRec.expert === "object" && docRec.expert !== null
|
||||||
|
? { ...(docRec.expert as Record<string, unknown>) }
|
||||||
|
: {};
|
||||||
|
const expert = { ...prevExpert, decision: built };
|
||||||
|
await this.blameRequestDbService.findByIdAndUpdate(blameRequestId, {
|
||||||
|
$set: { expert },
|
||||||
|
});
|
||||||
|
docRec.expert = expert;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parties = (doc.parties ?? []) as Array<{
|
||||||
|
evidence?: {
|
||||||
|
videoId?: string | number;
|
||||||
|
voices?: (string | number)[];
|
||||||
|
videoUrl?: string;
|
||||||
|
voiceUrls?: string[];
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
for (const party of parties) {
|
||||||
|
if (!party.evidence) continue;
|
||||||
|
const evidence = party.evidence as Record<string, unknown>;
|
||||||
|
|
||||||
|
if (evidence.videoId) {
|
||||||
|
const videoDoc = await this.blameVideoDbService.findById(
|
||||||
|
String(evidence.videoId),
|
||||||
|
);
|
||||||
|
if (videoDoc?.path) {
|
||||||
|
evidence.videoUrl = buildFileLink(videoDoc.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evidence.voices && Array.isArray(evidence.voices)) {
|
||||||
|
const voiceUrls: string[] = [];
|
||||||
|
for (const voiceId of evidence.voices) {
|
||||||
|
const voiceDoc = await this.blameVoiceDbService.findById(
|
||||||
|
String(voiceId),
|
||||||
|
);
|
||||||
|
if (voiceDoc?.path) {
|
||||||
|
voiceUrls.push(buildFileLink(voiceDoc.path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
evidence.voiceUrls = voiceUrls;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdAt = doc.createdAt
|
||||||
|
? new Date(doc.createdAt as string | number)
|
||||||
|
: new Date();
|
||||||
|
const updatedAt = doc.updatedAt
|
||||||
|
? new Date(doc.updatedAt as string | number)
|
||||||
|
: new Date();
|
||||||
|
const [createdDate, createdTime] = toJalaliDateAndTime(createdAt);
|
||||||
|
const [updatedDate, updatedTime] = toJalaliDateAndTime(updatedAt);
|
||||||
|
(doc as Record<string, unknown>).createdAtFormatted =
|
||||||
|
`${createdDate} ${createdTime}`;
|
||||||
|
(doc as Record<string, unknown>).updatedAtFormatted =
|
||||||
|
`${updatedDate} ${updatedTime}`;
|
||||||
|
|
||||||
|
docRec.parties = enrichBlamePartiesForAgreementView(docRec);
|
||||||
|
|
||||||
|
return docRec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** JSON-safe `expert.decision` for API responses (ObjectIds → string). */
|
||||||
|
private serializeBlameExpertDecisionForClaimDetail(
|
||||||
|
decision: unknown,
|
||||||
|
): Record<string, unknown> | undefined {
|
||||||
|
if (!decision || typeof decision !== "object") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const d = decision as Record<string, unknown>;
|
||||||
|
const guilty = d.guiltyPartyId;
|
||||||
|
const decidedBy = d.decidedByExpertId;
|
||||||
|
const decidedAt = d.decidedAt;
|
||||||
|
return {
|
||||||
|
...d,
|
||||||
|
guiltyPartyId:
|
||||||
|
guilty != null && typeof (guilty as { toString?: () => string }).toString === "function"
|
||||||
|
? String(guilty)
|
||||||
|
: guilty,
|
||||||
|
decidedByExpertId:
|
||||||
|
decidedBy != null &&
|
||||||
|
typeof (decidedBy as { toString?: () => string }).toString === "function"
|
||||||
|
? String(decidedBy)
|
||||||
|
: decidedBy,
|
||||||
|
decidedAt:
|
||||||
|
decidedAt instanceof Date
|
||||||
|
? decidedAt.toISOString()
|
||||||
|
: decidedAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V2: Get claim detail for damage expert
|
* V2: Get claim detail for damage expert
|
||||||
*
|
*
|
||||||
@@ -2499,6 +2617,9 @@ export class ExpertClaimService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Combine both branches of the conflict, preserving necessary logic from both ---
|
||||||
|
|
||||||
|
// 1. Vehicle payload logic from "upstream"
|
||||||
let vehiclePayload = claim.vehicle as any;
|
let vehiclePayload = claim.vehicle as any;
|
||||||
const hasClaimVehicle =
|
const hasClaimVehicle =
|
||||||
vehiclePayload &&
|
vehiclePayload &&
|
||||||
@@ -2508,23 +2629,73 @@ export class ExpertClaimService {
|
|||||||
vehiclePayload.plate);
|
vehiclePayload.plate);
|
||||||
|
|
||||||
let blameLean: any = null;
|
let blameLean: any = null;
|
||||||
if (claim.blameRequestId) {
|
|
||||||
const rows = (await this.blameRequestDbService.find(
|
// 2. Video capture and blame case db queries from "stashed"
|
||||||
|
const videoCaptureIdRaw = (claim.media as any)?.videoCaptureId;
|
||||||
|
const blameRequestIdStr = claim.blameRequestId?.toString();
|
||||||
|
|
||||||
|
const [
|
||||||
|
videoCaptureRow,
|
||||||
|
blameCase,
|
||||||
|
blameLeanRows
|
||||||
|
] = await Promise.all([
|
||||||
|
videoCaptureIdRaw
|
||||||
|
? this.claimVideoCaptureDbService.findById(String(videoCaptureIdRaw))
|
||||||
|
: Promise.resolve(null),
|
||||||
|
blameRequestIdStr
|
||||||
|
? this.buildBlameCaseSnapshotForClaimDetail(blameRequestIdStr)
|
||||||
|
: Promise.resolve(null),
|
||||||
|
claim.blameRequestId
|
||||||
|
? this.blameRequestDbService.find(
|
||||||
{ _id: new Types.ObjectId(claim.blameRequestId.toString()) },
|
{ _id: new Types.ObjectId(claim.blameRequestId.toString()) },
|
||||||
{ lean: true, select: "type parties expert.decision" },
|
{ lean: true, select: "type parties expert.decision" },
|
||||||
)) as any[];
|
)
|
||||||
blameLean = rows[0] ?? null;
|
: Promise.resolve([]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (Array.isArray(blameLeanRows) && blameLeanRows.length) {
|
||||||
|
blameLean = blameLeanRows[0] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If claim vehicle not found and blameLean exists, replace vehiclePayload
|
||||||
if (!hasClaimVehicle && blameLean) {
|
if (!hasClaimVehicle && blameLean) {
|
||||||
const fromBlame = this.damagedPartyVehicleFromBlame(blameLean, claim);
|
const fromBlame = this.damagedPartyVehicleFromBlame(blameLean, claim);
|
||||||
if (fromBlame) vehiclePayload = fromBlame;
|
if (fromBlame) vehiclePayload = fromBlame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. Blame file context logic from "upstream"
|
||||||
const blameFileContext = blameLean
|
const blameFileContext = blameLean
|
||||||
? this.blameFileContextForExpert(blameLean)
|
? this.blameFileContextForExpert(blameLean)
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
|
// 4. videoCapture logic from "stashed"
|
||||||
|
let videoCapture: ClaimDetailV2ResponseDto['videoCapture'] = undefined;
|
||||||
|
if (videoCaptureRow) {
|
||||||
|
const vc = videoCaptureRow as any;
|
||||||
|
videoCapture = {
|
||||||
|
id: String(vc._id ?? videoCaptureIdRaw),
|
||||||
|
fileName: vc.fileName,
|
||||||
|
path: vc.path,
|
||||||
|
url: vc.path ? buildFileLink(vc.path) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const blameExpertDecision = blameCase
|
||||||
|
? this.serializeBlameExpertDecisionForClaimDetail(
|
||||||
|
(blameCase.expert as Record<string, unknown> | undefined)?.decision,
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const money = (claim as { money?: { sheba?: string; nationalCodeOfInsurer?: string } })
|
||||||
|
.money;
|
||||||
|
const moneyPayload =
|
||||||
|
money && (money.sheba != null || money.nationalCodeOfInsurer != null)
|
||||||
|
? {
|
||||||
|
sheba: money.sheba,
|
||||||
|
nationalCodeOfInsurer: money.nationalCodeOfInsurer,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
claimRequestId: claim._id.toString(),
|
claimRequestId: claim._id.toString(),
|
||||||
publicId: claim.publicId,
|
publicId: claim.publicId,
|
||||||
@@ -2550,6 +2721,7 @@ export class ExpertClaimService {
|
|||||||
...blameFileContext,
|
...blameFileContext,
|
||||||
blameRequestId: claim.blameRequestId?.toString(),
|
blameRequestId: claim.blameRequestId?.toString(),
|
||||||
blameRequestNo: claim.blameRequestNo,
|
blameRequestNo: claim.blameRequestNo,
|
||||||
|
money: moneyPayload,
|
||||||
selectedParts: claim.damage?.selectedParts,
|
selectedParts: claim.damage?.selectedParts,
|
||||||
otherParts: claim.damage?.otherParts,
|
otherParts: claim.damage?.otherParts,
|
||||||
requiredDocuments:
|
requiredDocuments:
|
||||||
@@ -2565,6 +2737,9 @@ export class ExpertClaimService {
|
|||||||
damageExpertReplyFinal: (claim as any).evaluation?.damageExpertReplyFinal,
|
damageExpertReplyFinal: (claim as any).evaluation?.damageExpertReplyFinal,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
videoCapture,
|
||||||
|
blameCase: blameCase ?? undefined,
|
||||||
|
blameExpertDecision,
|
||||||
createdAt: (claim as any).createdAt,
|
createdAt: (claim as any).createdAt,
|
||||||
updatedAt: (claim as any).updatedAt,
|
updatedAt: (claim as any).updatedAt,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export class ExpertClaimV2Controller {
|
|||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Get claim request detail for damage expert",
|
summary: "Get claim request detail for damage expert",
|
||||||
description:
|
description:
|
||||||
"Returns full claim details including captured images, required documents status, and damage selections. Allowed when status is WAITING_FOR_DAMAGE_EXPERT (if locked, only the locking expert) or when awaiting factor validation (WAITING_FOR_INSURER_APPROVAL + UNDER_REVIEW + EXPERT_COST_EVALUATION); in the latter case `evaluation` includes the active expert reply for factors.",
|
"Returns full claim details including captured images, required documents, damage selections, `videoCapture` (from claim-video-capture via media.videoCaptureId), and `blameCase` (linked blameCases document with party video/voice URLs like expert-blame detail). Allowed when status is WAITING_FOR_DAMAGE_EXPERT (if locked, only the locking expert) or when awaiting factor validation.",
|
||||||
})
|
})
|
||||||
@ApiParam({ name: "claimRequestId" })
|
@ApiParam({ name: "claimRequestId" })
|
||||||
async getClaimDetailV2(
|
async getClaimDetailV2(
|
||||||
|
|||||||
149
src/helpers/blame-party-agreement-decision.ts
Normal file
149
src/helpers/blame-party-agreement-decision.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import { Types } from "mongoose";
|
||||||
|
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||||||
|
import { BlameStatus } from "src/Types&Enums/blame-request-management/blameStatus.enum";
|
||||||
|
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
|
||||||
|
|
||||||
|
/** Default accident taxonomy when guilt is established by mutual party agreement (no field-expert decision). */
|
||||||
|
export const MUTUAL_AGREEMENT_EXPERT_DECISION_FIELDS = {
|
||||||
|
accidentWay: { id: 8, label: "جلو به عقب" },
|
||||||
|
accidentReason: {
|
||||||
|
id: 1,
|
||||||
|
fanavaran: 1,
|
||||||
|
label:
|
||||||
|
"عدم رعايت فاصله مناسب با وسيله نقليه و يا عدم توجه به جلو",
|
||||||
|
},
|
||||||
|
accidentType: {
|
||||||
|
id: 1,
|
||||||
|
label: "برخورد یک وسیله نقلیه با یک وسیله نقلیه",
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type MutualAgreementDecisionPayload = {
|
||||||
|
guiltyPartyId: Types.ObjectId;
|
||||||
|
description: string;
|
||||||
|
decidedAt: Date;
|
||||||
|
decidedByExpertId?: Types.ObjectId;
|
||||||
|
fields: typeof MUTUAL_AGREEMENT_EXPERT_DECISION_FIELDS;
|
||||||
|
};
|
||||||
|
|
||||||
|
function partyPhone(p: { person?: { phoneNumber?: string } } | undefined): string {
|
||||||
|
return (p?.person?.phoneNumber || "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function partyUserId(
|
||||||
|
p: { person?: { userId?: Types.ObjectId } } | undefined,
|
||||||
|
): Types.ObjectId | null {
|
||||||
|
const id = p?.person?.userId;
|
||||||
|
if (!id) return null;
|
||||||
|
return id instanceof Types.ObjectId ? id : new Types.ObjectId(String(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the first party chose AGREED (guilty or damaged) and the second party exists,
|
||||||
|
* derive the guilty party and build the synthetic expert.decision payload.
|
||||||
|
* Does not set decidedByExpertId — this is not a field-expert adjudication.
|
||||||
|
*/
|
||||||
|
export function buildMutualAgreementExpertDecision(
|
||||||
|
doc: Record<string, unknown>,
|
||||||
|
): MutualAgreementDecisionPayload | null {
|
||||||
|
if (doc.type !== BlameRequestType.THIRD_PARTY) return null;
|
||||||
|
if (doc.blameStatus !== BlameStatus.AGREED) return null;
|
||||||
|
|
||||||
|
const parties = (doc.parties ?? []) as Array<{
|
||||||
|
role?: string;
|
||||||
|
person?: { userId?: Types.ObjectId; phoneNumber?: string };
|
||||||
|
statement?: {
|
||||||
|
admitsGuilt?: boolean;
|
||||||
|
claimsDamage?: boolean;
|
||||||
|
acceptsExpertOpinion?: boolean;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const first = parties.find((p) => p.role === PartyRole.FIRST);
|
||||||
|
const second = parties.find((p) => p.role === PartyRole.SECOND);
|
||||||
|
if (!first || !second) return null;
|
||||||
|
|
||||||
|
const st = first.statement;
|
||||||
|
if (!st) return null;
|
||||||
|
if (st.acceptsExpertOpinion) return null;
|
||||||
|
|
||||||
|
const admits = !!st.admitsGuilt;
|
||||||
|
const claims = !!st.claimsDamage;
|
||||||
|
if (admits === claims) return null;
|
||||||
|
|
||||||
|
let guiltyParty: typeof first;
|
||||||
|
let damagedParty: typeof first;
|
||||||
|
if (admits) {
|
||||||
|
guiltyParty = first;
|
||||||
|
damagedParty = second;
|
||||||
|
} else {
|
||||||
|
guiltyParty = second;
|
||||||
|
damagedParty = first;
|
||||||
|
}
|
||||||
|
|
||||||
|
const guiltyId = partyUserId(guiltyParty);
|
||||||
|
if (!guiltyId) return null;
|
||||||
|
|
||||||
|
const gPhone = partyPhone(guiltyParty);
|
||||||
|
const dPhone = partyPhone(damagedParty);
|
||||||
|
if (!gPhone || !dPhone) return null;
|
||||||
|
|
||||||
|
const description = `با توافق طرفین مقصر و زیان دیده مشخص شد. کاربر ${gPhone} مقصر و کاربر ${dPhone} زیان دیده می باشد.`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
guiltyPartyId: guiltyId,
|
||||||
|
description,
|
||||||
|
decidedAt: new Date(),
|
||||||
|
fields: MUTUAL_AGREEMENT_EXPERT_DECISION_FIELDS,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge complementary statement flags onto the second party for expert/claim UIs when
|
||||||
|
* only the first party submitted a formal confession (V2 remote flow).
|
||||||
|
*/
|
||||||
|
export function enrichBlamePartiesForAgreementView(
|
||||||
|
doc: Record<string, unknown>,
|
||||||
|
): Array<Record<string, unknown>> {
|
||||||
|
const parties = (doc.parties ?? []) as Array<Record<string, unknown>>;
|
||||||
|
if (!Array.isArray(parties) || parties.length === 0) return parties;
|
||||||
|
|
||||||
|
const first = parties.find((p) => p.role === PartyRole.FIRST) as
|
||||||
|
| {
|
||||||
|
statement?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
const secondIdx = parties.findIndex((p) => p.role === PartyRole.SECOND);
|
||||||
|
if (!first?.statement || secondIdx === -1) return parties;
|
||||||
|
|
||||||
|
if (doc.blameStatus !== BlameStatus.AGREED) return parties;
|
||||||
|
|
||||||
|
const st = first.statement as {
|
||||||
|
admitsGuilt?: boolean;
|
||||||
|
claimsDamage?: boolean;
|
||||||
|
acceptsExpertOpinion?: boolean;
|
||||||
|
};
|
||||||
|
if (st.acceptsExpertOpinion) return parties;
|
||||||
|
|
||||||
|
const admits = !!st.admitsGuilt;
|
||||||
|
const claims = !!st.claimsDamage;
|
||||||
|
if (admits === claims) return parties;
|
||||||
|
|
||||||
|
const second = parties[secondIdx] as {
|
||||||
|
statement?: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
const prev = (second.statement ?? {}) as Record<string, unknown>;
|
||||||
|
const mergedStatement = {
|
||||||
|
...prev,
|
||||||
|
admitsGuilt: admits ? false : true,
|
||||||
|
claimsDamage: admits ? true : false,
|
||||||
|
acceptsExpertOpinion: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const next = [...parties];
|
||||||
|
next[secondIdx] = {
|
||||||
|
...second,
|
||||||
|
statement: mergedStatement,
|
||||||
|
};
|
||||||
|
return next;
|
||||||
|
}
|
||||||
@@ -75,6 +75,10 @@ import { CaseStatus } from "src/Types&Enums/blame-request-management/caseStatus.
|
|||||||
import { UploadContext } from "./entities/schema/blame-voice.schema";
|
import { UploadContext } from "./entities/schema/blame-voice.schema";
|
||||||
import { HashService } from "src/utils/hash/hash.service";
|
import { HashService } from "src/utils/hash/hash.service";
|
||||||
import { UserAuthService } from "src/auth/auth-services/user.auth.service";
|
import { UserAuthService } from "src/auth/auth-services/user.auth.service";
|
||||||
|
import {
|
||||||
|
buildMutualAgreementExpertDecision,
|
||||||
|
MUTUAL_AGREEMENT_EXPERT_DECISION_FIELDS,
|
||||||
|
} from "src/helpers/blame-party-agreement-decision";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RequestManagementService {
|
export class RequestManagementService {
|
||||||
@@ -1157,6 +1161,22 @@ export class RequestManagementService {
|
|||||||
|
|
||||||
await this.advanceWorkflowToNext(req, stepKey);
|
await this.advanceWorkflowToNext(req, stepKey);
|
||||||
|
|
||||||
|
if (
|
||||||
|
stepKey === WorkflowStep.SECOND_DESCRIPTION &&
|
||||||
|
req.type === BlameRequestType.THIRD_PARTY &&
|
||||||
|
!(req as any).expert?.decision?.guiltyPartyId
|
||||||
|
) {
|
||||||
|
const built = buildMutualAgreementExpertDecision(
|
||||||
|
(req as any).toObject
|
||||||
|
? (req as any).toObject()
|
||||||
|
: { ...req },
|
||||||
|
);
|
||||||
|
if (built) {
|
||||||
|
if (!(req as any).expert) (req as any).expert = {};
|
||||||
|
(req as any).expert.decision = built as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!Array.isArray(req.history)) req.history = [];
|
if (!Array.isArray(req.history)) req.history = [];
|
||||||
req.history.push({
|
req.history.push({
|
||||||
type: `${stepKey}_SUBMITTED`,
|
type: `${stepKey}_SUBMITTED`,
|
||||||
@@ -4435,8 +4455,18 @@ export class RequestManagementService {
|
|||||||
formData.guiltyPartyPhoneNumber === formData.firstPartyPhoneNumber
|
formData.guiltyPartyPhoneNumber === formData.firstPartyPhoneNumber
|
||||||
? String(firstPartyUserId)
|
? String(firstPartyUserId)
|
||||||
: String(secondPartyUserId);
|
: String(secondPartyUserId);
|
||||||
|
const damagedPhone =
|
||||||
|
formData.guiltyPartyPhoneNumber === formData.firstPartyPhoneNumber
|
||||||
|
? formData.secondParty.phoneNumber
|
||||||
|
: formData.firstPartyPhoneNumber;
|
||||||
if (!req.expert) req.expert = {} as any;
|
if (!req.expert) req.expert = {} as any;
|
||||||
req.expert.decision = { guiltyPartyId: new Types.ObjectId(guiltyPartyId) } as any;
|
req.expert.decision = {
|
||||||
|
guiltyPartyId: new Types.ObjectId(guiltyPartyId),
|
||||||
|
description: `با توافق طرفین مقصر و زیان دیده مشخص شد. کاربر ${formData.guiltyPartyPhoneNumber} مقصر و کاربر ${damagedPhone} زیان دیده می باشد.`,
|
||||||
|
decidedAt: new Date(),
|
||||||
|
decidedByExpertId: new Types.ObjectId(expert.sub),
|
||||||
|
fields: { ...MUTUAL_AGREEMENT_EXPERT_DECISION_FIELDS },
|
||||||
|
} as any;
|
||||||
req.workflow = {
|
req.workflow = {
|
||||||
currentStep: WorkflowStep.FIRST_LOCATION as any,
|
currentStep: WorkflowStep.FIRST_LOCATION as any,
|
||||||
nextStep: WorkflowStep.SECOND_LOCATION as any,
|
nextStep: WorkflowStep.SECOND_LOCATION as any,
|
||||||
|
|||||||
Reference in New Issue
Block a user