This commit is contained in:
SepehrYahyaee
2026-04-22 10:29:01 +03:30
parent 04ba46ed2a
commit 194b71cdb2
5 changed files with 197 additions and 26 deletions

View File

@@ -79,6 +79,7 @@ import {
buildMutualAgreementExpertDecision,
MUTUAL_AGREEMENT_EXPERT_DECISION_FIELDS,
} from "src/helpers/blame-party-agreement-decision";
import { buildFileLink } from "src/helpers/urlCreator";
@Injectable()
export class RequestManagementService {
@@ -4117,6 +4118,133 @@ export class RequestManagementService {
return none;
}
/** Party payload for user blame detail: no national codes, licenses, phone, birthdays, or vehicle inquiry blobs. */
private async sanitizePartyForBlameUserView(
party: any,
): Promise<Record<string, unknown>> {
if (!party || typeof party !== "object") {
return {};
}
const person = party.person;
const safePerson = person
? {
userId:
person.userId != null ? String(person.userId) : undefined,
fullName: person.fullName,
clientId:
person.clientId != null ? String(person.clientId) : undefined,
}
: undefined;
const vehicle = party.vehicle
? {
plateId: party.vehicle.plateId,
name: party.vehicle.name,
model: party.vehicle.model,
type: party.vehicle.type,
isNew: party.vehicle.isNew,
}
: undefined;
const evidenceRaw = party.evidence as Record<string, unknown> | undefined;
const evidence: Record<string, unknown> | undefined = evidenceRaw
? { ...evidenceRaw }
: undefined;
if (evidence) {
if (party.role === PartyRole.FIRST && evidence.videoId) {
const videoDoc = await this.blameVideoDbService.findById(
String(evidence.videoId),
);
if (videoDoc?.path) {
evidence.videoUrl = buildFileLink(videoDoc.path);
}
}
delete evidence.videoId;
const voiceIds = evidence.voices;
if (Array.isArray(voiceIds)) {
const voiceUrls: string[] = [];
for (const voiceId of voiceIds) {
const voiceDoc = await this.blameVoiceDbService.findById(
String(voiceId),
);
if (voiceDoc?.path) {
voiceUrls.push(buildFileLink(voiceDoc.path));
}
}
evidence.voiceUrls = voiceUrls;
}
delete evidence.voices;
}
return {
role: party.role,
person: safePerson,
carBodyFirstForm: party.carBodyFirstForm,
location: party.location,
vehicle,
insurance: party.insurance,
statement: party.statement,
evidence,
confirmation: party.confirmation,
};
}
private async resolveBlameExpertDisplayNameFromPlain(
expert: Record<string, unknown>,
): Promise<string | undefined> {
const decision = expert.decision as Record<string, unknown> | undefined;
const decided = decision?.decidedByExpertId;
const assigned = expert.assignedExpertId;
const raw = decided ?? assigned;
if (raw == null || raw === "") return undefined;
const sid = String(raw);
if (!Types.ObjectId.isValid(sid)) return undefined;
const doc = await this.expertDbService.findOne({
_id: new Types.ObjectId(sid),
});
if (!doc) return undefined;
return `${doc.firstName || ""} ${doc.lastName || ""}`.trim() || undefined;
}
/** Expert subdocument for user view: string ids, trimmed snapshot (name only), plus `expertName`. */
private async buildExpertForBlameUserView(
expertRaw: any,
): Promise<Record<string, unknown> | undefined> {
if (!expertRaw || typeof expertRaw !== "object") return undefined;
const out = JSON.parse(JSON.stringify(expertRaw)) as Record<string, unknown>;
const decision = out.decision as Record<string, unknown> | undefined;
let expertName: string | undefined;
if (decision) {
if (decision.guiltyPartyId != null) {
decision.guiltyPartyId = String(decision.guiltyPartyId);
}
if (decision.decidedByExpertId != null) {
decision.decidedByExpertId = String(decision.decidedByExpertId);
}
const snap = decision.expertProfileSnapshot as
| Record<string, unknown>
| undefined;
if (snap) {
expertName =
`${snap.firstName ?? ""} ${snap.lastName ?? ""}`.trim() || undefined;
decision.expertProfileSnapshot = {
firstName: snap.firstName,
lastName: snap.lastName,
};
}
}
if (out.assignedExpertId != null) {
out.assignedExpertId = String(out.assignedExpertId);
}
if (!expertName) {
expertName = await this.resolveBlameExpertDisplayNameFromPlain(out);
}
return { ...out, expertName };
}
/**
* V2: Get one blame request by id. Access allowed if current user is a party (by userId or phone)
* or the initiating field expert. For expert-initiated LINK, party access by phone is sufficient.
@@ -4165,7 +4293,36 @@ export class RequestManagementService {
typeof (req as any).toObject === "function"
? (req as any).toObject({ versionKey: false })
: { ...(req as any) };
return { ...plain, claimCreation };
const allParties = Array.isArray(plain.parties) ? plain.parties : [];
const visibleParties =
isFieldExpertOwner || isRegistrarOwner
? allParties
: allParties.filter((p: any) => {
const pid = p?.person?.userId ? String(p.person.userId) : null;
const phone = p?.person?.phoneNumber;
return (
(pid && pid === String(user?.sub)) ||
(phone && phone === user?.username)
);
});
const parties = await Promise.all(
visibleParties.map((p: any) => this.sanitizePartyForBlameUserView(p)),
);
const expert = await this.buildExpertForBlameUserView(plain.expert);
return {
requestNo: plain.requestNo,
publicId: plain.publicId,
type: plain.type,
status: plain.status,
blameStatus: plain.blameStatus,
workflow: plain.workflow,
parties,
expert,
carBodyInsuranceDetail: plain.carBodyInsuranceDetail,
claimCreation,
};
}
/**