forked from Yara724/api
Compare commits
9 Commits
7f672541ae
...
767317cce8
| Author | SHA1 | Date | |
|---|---|---|---|
| 767317cce8 | |||
|
|
65870c8d66 | ||
| 70d05a7624 | |||
|
|
192d4e72de | ||
| 305a2965bf | |||
|
|
626b0ded34 | ||
| c4f3558cda | |||
|
|
c8ccd943f2 | ||
| e761c4b6b2 |
@@ -8198,8 +8198,12 @@ export class ClaimRequestManagementService {
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Validate outer parts haven't been selected yet
|
||||
// 4. Validate outer parts haven't been selected yet.
|
||||
// Skip this guard when FileMaker rejected the claim and the FileReviewer
|
||||
// is updating (overwriting) the previous selection.
|
||||
const isFileMakerRejectedRerun = !!(claimCase as any).fileMakerRejectedPendingReview;
|
||||
if (
|
||||
!isFileMakerRejectedRerun &&
|
||||
claimCase.damage?.selectedParts &&
|
||||
claimCase.damage.selectedParts.length > 0
|
||||
) {
|
||||
@@ -8266,7 +8270,9 @@ export class ClaimRequestManagementService {
|
||||
}));
|
||||
const selectedPartIds = selectedItems.map((p) => p.id);
|
||||
|
||||
// 6. Update claim case with selected parts and move to next step
|
||||
// 6. Update claim case with selected parts and move to next step.
|
||||
// Also clear fileMakerRejectedPendingReview now that the reviewer has
|
||||
// actively re-submitted outer parts.
|
||||
const updatedClaim = await this.claimCaseDbService.findByIdAndUpdate(
|
||||
claimRequestId,
|
||||
{
|
||||
@@ -8276,7 +8282,10 @@ export class ClaimRequestManagementService {
|
||||
status: ClaimCaseStatus.SELECTING_OTHER_PARTS,
|
||||
"workflow.currentStep": ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
||||
"workflow.nextStep": ClaimWorkflowStep.CAPTURE_PART_DAMAGES,
|
||||
$unset: { "damage.selectedOuterParts": "" },
|
||||
$unset: {
|
||||
"damage.selectedOuterParts": "",
|
||||
"fileMakerRejectedPendingReview": "",
|
||||
},
|
||||
$push: {
|
||||
"workflow.completedSteps": ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
||||
history: {
|
||||
@@ -8401,8 +8410,11 @@ export class ClaimRequestManagementService {
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Validate other parts and bank info haven't been submitted yet
|
||||
// 4. Validate other parts and bank info haven't been submitted yet.
|
||||
// Skip when FileMaker rejected the claim — the FileReviewer can update bank info.
|
||||
const isFileMakerRejectedRerun = !!(claimCase as any).fileMakerRejectedPendingReview;
|
||||
if (
|
||||
!isFileMakerRejectedRerun &&
|
||||
claimCase.money?.sheba &&
|
||||
claimCase.money?.nationalCodeOfInsurer &&
|
||||
claimCase.workflow?.currentStep !== ClaimWorkflowStep.USER_EXPERT_RESEND
|
||||
|
||||
@@ -93,6 +93,28 @@ export class FileMakerBlameV4Controller {
|
||||
);
|
||||
}
|
||||
|
||||
@Get("my-files")
|
||||
@ApiOperation({
|
||||
summary: "List all blame files created by this FileMaker",
|
||||
description: "Returns all V4 FileMaker blame files initiated by the authenticated FileMaker.",
|
||||
})
|
||||
async getMyFiles(@CurrentUser() fileMaker: any) {
|
||||
return this.requestManagementService.getMyFileMakerFiles(fileMaker);
|
||||
}
|
||||
|
||||
@Get("my-files/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
summary: "Get a single blame file created by this FileMaker",
|
||||
description: "Returns full detail — parties, workflow, linked claim ID — for one V4 FileMaker blame file.",
|
||||
})
|
||||
async getMyFileDetail(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileMaker: any,
|
||||
) {
|
||||
return this.requestManagementService.getMyFileMakerFileDetail(fileMaker, requestId);
|
||||
}
|
||||
|
||||
@Get("claim-id/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
|
||||
@@ -92,6 +92,28 @@ export class FileMakerBlameV5Controller {
|
||||
);
|
||||
}
|
||||
|
||||
@Get("my-files")
|
||||
@ApiOperation({
|
||||
summary: "List all blame files created by this FileMaker",
|
||||
description: "Returns all V5 FileMaker blame files initiated by the authenticated FileMaker.",
|
||||
})
|
||||
async getMyFiles(@CurrentUser() fileMaker: any) {
|
||||
return this.requestManagementService.getMyFileMakerFiles(fileMaker);
|
||||
}
|
||||
|
||||
@Get("my-files/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
summary: "Get a single blame file created by this FileMaker",
|
||||
description: "Returns full detail — parties, workflow, linked claim ID — for one V5 FileMaker blame file.",
|
||||
})
|
||||
async getMyFileDetail(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileMaker: any,
|
||||
) {
|
||||
return this.requestManagementService.getMyFileMakerFileDetail(fileMaker, requestId);
|
||||
}
|
||||
|
||||
@Get("claim-id/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
|
||||
@@ -84,6 +84,28 @@ export class FileReviewerBlameV4Controller {
|
||||
|
||||
// ─── Linked claim lookup ──────────────────────────────────────────────────────
|
||||
|
||||
@Get("my-files")
|
||||
@ApiOperation({
|
||||
summary: "List all blame files assigned to this FileReviewer",
|
||||
description: "Returns all V4 FileMaker blame files that have been assigned to the authenticated FileReviewer.",
|
||||
})
|
||||
async getMyFiles(@CurrentUser() fileReviewer: any) {
|
||||
return this.requestManagementService.getMyFileReviewerFiles(fileReviewer);
|
||||
}
|
||||
|
||||
@Get("my-files/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
summary: "Get a single blame file assigned to this FileReviewer",
|
||||
description: "Returns full detail — parties, workflow, expert fields, linked claim ID — for one V4 blame file.",
|
||||
})
|
||||
async getMyFileDetail(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
) {
|
||||
return this.requestManagementService.getMyFileReviewerFileDetail(fileReviewer, requestId);
|
||||
}
|
||||
|
||||
@Get("claim-id/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
|
||||
@@ -82,6 +82,28 @@ export class FileReviewerBlameV5Controller {
|
||||
|
||||
// ─── Linked claim lookup ──────────────────────────────────────────────────────
|
||||
|
||||
@Get("my-files")
|
||||
@ApiOperation({
|
||||
summary: "List all blame files assigned to this FileReviewer",
|
||||
description: "Returns all V5 FileMaker blame files that have been assigned to the authenticated FileReviewer.",
|
||||
})
|
||||
async getMyFiles(@CurrentUser() fileReviewer: any) {
|
||||
return this.requestManagementService.getMyFileReviewerFiles(fileReviewer);
|
||||
}
|
||||
|
||||
@Get("my-files/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
summary: "Get a single blame file assigned to this FileReviewer",
|
||||
description: "Returns full detail — parties, workflow, expert fields, linked claim ID — for one V5 blame file.",
|
||||
})
|
||||
async getMyFileDetail(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
) {
|
||||
return this.requestManagementService.getMyFileReviewerFileDetail(fileReviewer, requestId);
|
||||
}
|
||||
|
||||
@Get("claim-id/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
|
||||
@@ -272,6 +272,31 @@ export class RequestManagementService {
|
||||
req: any,
|
||||
submittedStepKey: WorkflowStep,
|
||||
) {
|
||||
// V6 call-center initiated THIRD_PARTY: skip FIRST_INITIAL_FORM because the
|
||||
// call-center agent already ran the inquiry during run-inquiry.
|
||||
// After FIRST_VIDEO, jump straight to FIRST_LOCATION.
|
||||
if (
|
||||
req.callCenterInitiated &&
|
||||
req.type === BlameRequestType.THIRD_PARTY &&
|
||||
submittedStepKey === WorkflowStep.FIRST_VIDEO
|
||||
) {
|
||||
const completed = Array.isArray(req.workflow.completedSteps)
|
||||
? req.workflow.completedSteps
|
||||
: [];
|
||||
if (!completed.includes(submittedStepKey)) completed.push(submittedStepKey);
|
||||
// Also mark FIRST_INITIAL_FORM as completed so the step chain is consistent
|
||||
if (!completed.includes(WorkflowStep.FIRST_INITIAL_FORM)) {
|
||||
completed.push(WorkflowStep.FIRST_INITIAL_FORM);
|
||||
}
|
||||
req.workflow.completedSteps = completed;
|
||||
req.workflow.currentStep = WorkflowStep.FIRST_LOCATION;
|
||||
req.workflow.nextStep = WorkflowStep.FIRST_VOICE;
|
||||
this.logger.debug(
|
||||
"[WORKFLOW] V6 call-center: skipped FIRST_INITIAL_FORM after FIRST_VIDEO, advanced to FIRST_LOCATION",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// CAR_BODY: after first-party description, move to signature step.
|
||||
if (
|
||||
req.type === BlameRequestType.CAR_BODY &&
|
||||
@@ -326,9 +351,15 @@ export class RequestManagementService {
|
||||
req.creationMethod === CreationMethod.IN_PERSON &&
|
||||
(req.expertInitiated || req.registrarInitiated);
|
||||
|
||||
// V6 call-center THIRD_PARTY: guilt is pre-decided (first party is always guilty),
|
||||
// so skip the WAITING_FOR_GUILT_DECISION waiting state just like IN_PERSON.
|
||||
const isCallCenterThirdParty =
|
||||
req.type === BlameRequestType.THIRD_PARTY &&
|
||||
!!req.callCenterInitiated;
|
||||
|
||||
const resolveStep = (step: WorkflowStep | undefined): WorkflowStep | undefined => {
|
||||
if (
|
||||
isInPersonThirdParty &&
|
||||
(isInPersonThirdParty || isCallCenterThirdParty) &&
|
||||
step === WorkflowStep.WAITING_FOR_GUILT_DECISION
|
||||
) {
|
||||
return WorkflowStep.WAITING_FOR_SIGNATURES;
|
||||
@@ -1045,26 +1076,38 @@ export class RequestManagementService {
|
||||
if (!completed.includes(step3Key)) {
|
||||
completed.push(step3Key);
|
||||
}
|
||||
req.workflow.completedSteps = completed;
|
||||
|
||||
const nextStepKey = step3.nextPossibleSteps?.[0] as
|
||||
| WorkflowStep
|
||||
| undefined;
|
||||
if (!nextStepKey) {
|
||||
throw new InternalServerErrorException(
|
||||
"Workflow stepNumber=3 has no nextPossibleSteps configured",
|
||||
);
|
||||
}
|
||||
const nextStepDoc = await this.getWorkflowStep({ stepKey: nextStepKey });
|
||||
const afterNext = nextStepDoc.nextPossibleSteps?.[0];
|
||||
if (!afterNext) {
|
||||
throw new InternalServerErrorException(
|
||||
`Workflow step ${nextStepKey} has no nextPossibleSteps configured`,
|
||||
);
|
||||
}
|
||||
// V6 call-center initiated THIRD_PARTY: inquiry already done by agent — skip
|
||||
// FIRST_INITIAL_FORM and go straight to FIRST_LOCATION.
|
||||
if (req.callCenterInitiated && req.type === BlameRequestType.THIRD_PARTY) {
|
||||
if (!completed.includes(WorkflowStep.FIRST_INITIAL_FORM)) {
|
||||
completed.push(WorkflowStep.FIRST_INITIAL_FORM);
|
||||
}
|
||||
req.workflow.completedSteps = completed;
|
||||
req.workflow.currentStep = WorkflowStep.FIRST_LOCATION;
|
||||
req.workflow.nextStep = WorkflowStep.FIRST_VOICE;
|
||||
} else {
|
||||
req.workflow.completedSteps = completed;
|
||||
|
||||
req.workflow.currentStep = nextStepKey;
|
||||
req.workflow.nextStep = afterNext as WorkflowStep;
|
||||
const nextStepKey = step3.nextPossibleSteps?.[0] as
|
||||
| WorkflowStep
|
||||
| undefined;
|
||||
if (!nextStepKey) {
|
||||
throw new InternalServerErrorException(
|
||||
"Workflow stepNumber=3 has no nextPossibleSteps configured",
|
||||
);
|
||||
}
|
||||
const nextStepDoc = await this.getWorkflowStep({ stepKey: nextStepKey });
|
||||
const afterNext = nextStepDoc.nextPossibleSteps?.[0];
|
||||
if (!afterNext) {
|
||||
throw new InternalServerErrorException(
|
||||
`Workflow step ${nextStepKey} has no nextPossibleSteps configured`,
|
||||
);
|
||||
}
|
||||
|
||||
req.workflow.currentStep = nextStepKey;
|
||||
req.workflow.nextStep = afterNext as WorkflowStep;
|
||||
}
|
||||
|
||||
// History
|
||||
if (!Array.isArray(req.history)) req.history = [];
|
||||
@@ -2147,6 +2190,19 @@ export class RequestManagementService {
|
||||
req.type === BlameRequestType.THIRD_PARTY;
|
||||
let closedByMutualAgreement = false;
|
||||
|
||||
// V6 call-center initiated THIRD_PARTY: first party is always the guilty party.
|
||||
// If blameStatus is still UNKNOWN here (pre-fix file or race condition), force it to AGREED now.
|
||||
if (isSecondThirdParty && req.callCenterInitiated && req.blameStatus !== BlameStatus.AGREED) {
|
||||
const firstIdx = this.getPartyIndex(req, PartyRole.FIRST);
|
||||
if (firstIdx !== -1) {
|
||||
if (!req.parties[firstIdx].statement) req.parties[firstIdx].statement = {} as any;
|
||||
req.parties[firstIdx].statement.admitsGuilt = true;
|
||||
req.parties[firstIdx].statement.claimsDamage = false;
|
||||
req.parties[firstIdx].statement.acceptsExpertOpinion = false;
|
||||
}
|
||||
req.blameStatus = BlameStatus.AGREED;
|
||||
}
|
||||
|
||||
// THIRD_PARTY + AGREED: guilt/damage already implied by first-party confession — no field expert needed.
|
||||
if (isSecondThirdParty && req.blameStatus === BlameStatus.AGREED) {
|
||||
const built = buildMutualAgreementExpertDecision(
|
||||
@@ -10559,25 +10615,33 @@ export class RequestManagementService {
|
||||
const newRejectionCount = currentRejections + 1;
|
||||
const actorName = `${fileMaker.firstName || ""} ${fileMaker.lastName || ""}`.trim();
|
||||
|
||||
// Steps that belong to the re-run assessment cycle.
|
||||
// Pulled from completedSteps so workflow ordering guards pass on re-run.
|
||||
const assessmentSteps = [
|
||||
ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
||||
ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
||||
ClaimWorkflowStep.CAPTURE_PART_DAMAGES,
|
||||
ClaimWorkflowStep.USER_SUBMISSION_COMPLETE,
|
||||
];
|
||||
|
||||
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
|
||||
$set: {
|
||||
status: ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT,
|
||||
claimStatus: ClaimStatus.NEEDS_REVISION,
|
||||
fileMakerRejectionCount: newRejectionCount,
|
||||
fileMakerRejectionReason: reason ?? null,
|
||||
"workflow.currentStep": ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
|
||||
"workflow.nextStep": ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
|
||||
// Land directly at SELECT_OUTER_PARTS: the FileReviewer sees the
|
||||
// existing parts pre-populated and can adjust or confirm them.
|
||||
"workflow.currentStep": ClaimWorkflowStep.SELECT_OUTER_PARTS,
|
||||
"workflow.nextStep": ClaimWorkflowStep.SELECT_OTHER_PARTS,
|
||||
"workflow.locked": false,
|
||||
// Flag consumed by selectOuterPartsV2 / selectOtherPartsV2 to allow
|
||||
// overwriting previously submitted data instead of throwing ConflictException.
|
||||
"fileMakerRejectedPendingReview": true,
|
||||
},
|
||||
// Clear all stale lock fields and prior-cycle evaluation data so the
|
||||
// FileReviewer starts a completely clean lock cycle:
|
||||
// - workflow lock fields left over from the completed review cycle
|
||||
// - damageExpertReply / damageExpertResend / ownerInsurerApproval /
|
||||
// ownerPricedPartsApproval — all belong to the rejected cycle and
|
||||
// must not bleed into the new one (resend fulfilledAt would block
|
||||
// submitResendDocsV2; stale reply would mislead the frontend)
|
||||
// - assignedForReviewBy is intentionally kept so the same reviewer
|
||||
// is still scoped to this file on re-lock
|
||||
// Clear stale lock/evaluation fields and media that must be re-captured.
|
||||
// Part selections (damage.selectedParts, damage.otherParts) are intentionally
|
||||
// preserved as a pre-populated starting point for the FileReviewer to adjust.
|
||||
$unset: {
|
||||
"workflow.lockedAt": "",
|
||||
"workflow.expiredAt": "",
|
||||
@@ -10587,6 +10651,15 @@ export class RequestManagementService {
|
||||
"evaluation.damageExpertResend": "",
|
||||
"evaluation.ownerInsurerApproval": "",
|
||||
"evaluation.ownerPricedPartsApproval": "",
|
||||
// Photos and walk-around video must be re-taken for the new part set.
|
||||
"media.damagedParts": "",
|
||||
"media.captures": "",
|
||||
"media.videoCaptureId": "",
|
||||
},
|
||||
// Remove assessment-cycle steps from completedSteps so workflow ordering
|
||||
// guards pass when the FileReviewer re-runs each step.
|
||||
$pull: {
|
||||
"workflow.completedSteps": { $in: assessmentSteps },
|
||||
},
|
||||
$push: {
|
||||
history: {
|
||||
@@ -10759,7 +10832,9 @@ export class RequestManagementService {
|
||||
const firstParty = req.parties[firstIdx];
|
||||
|
||||
await this.runPartyInquiriesV3Internal(req, dto, PartyRole.FIRST, firstParty);
|
||||
this.markPartyInquiriesCompleteOnBlame(req, PartyRole.FIRST, agent);
|
||||
// Do NOT call markPartyInquiriesCompleteOnBlame here: that helper is for V3/V4/V5
|
||||
// FileMaker flows and would advance the workflow to FIRST_LOCATION prematurely.
|
||||
// In V6 the workflow must stay at CREATED until send-link advances it to FIRST_VIDEO.
|
||||
if (!Array.isArray((req as any).history)) (req as any).history = [];
|
||||
(req as any).history.push({
|
||||
type: "CALL_CENTER_INQUIRY_COMPLETED",
|
||||
@@ -10981,6 +11056,193 @@ export class RequestManagementService {
|
||||
/**
|
||||
* V6: List blame files created by this call-center agent.
|
||||
*/
|
||||
// ─── FileMaker file list / detail (V4 + V5) ────────────────────────────────
|
||||
|
||||
async getMyFileMakerFiles(fileMaker: any): Promise<any[]> {
|
||||
if (fileMaker?.role !== RoleEnum.FILE_MAKER) {
|
||||
throw new ForbiddenException("Only FileMakers can use this endpoint.");
|
||||
}
|
||||
const makerId = new Types.ObjectId(fileMaker.sub);
|
||||
const files = await this.blameRequestDbService.find({
|
||||
isMadeByFileMaker: true,
|
||||
initiatedByFieldExpertId: makerId,
|
||||
});
|
||||
return (files || []).map((f: any) => ({
|
||||
_id: f._id,
|
||||
publicId: f.publicId,
|
||||
requestNo: f.requestNo,
|
||||
type: f.type,
|
||||
status: f.status,
|
||||
blameStatus: f.blameStatus,
|
||||
workflow: {
|
||||
currentStep: f.workflow?.currentStep,
|
||||
nextStep: f.workflow?.nextStep,
|
||||
completedSteps: f.workflow?.completedSteps,
|
||||
},
|
||||
requiresFileMakerApproval: f.requiresFileMakerApproval,
|
||||
createdAt: f.createdAt,
|
||||
updatedAt: f.updatedAt,
|
||||
}));
|
||||
}
|
||||
|
||||
async getMyFileMakerFileDetail(fileMaker: any, requestId: string): Promise<any> {
|
||||
if (fileMaker?.role !== RoleEnum.FILE_MAKER) {
|
||||
throw new ForbiddenException("Only FileMakers can use this endpoint.");
|
||||
}
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Blame request not found");
|
||||
if (
|
||||
!req.isMadeByFileMaker ||
|
||||
String((req as any).initiatedByFieldExpertId) !== String(fileMaker.sub)
|
||||
) {
|
||||
throw new ForbiddenException("You can only access files that you have created.");
|
||||
}
|
||||
const plain = typeof (req as any).toObject === "function"
|
||||
? (req as any).toObject({ versionKey: false })
|
||||
: { ...(req as any) };
|
||||
// Attach linked claim ID if present
|
||||
const claim = await this.claimCaseDbService.findOne({ blameRequestId: (req as any)._id });
|
||||
return {
|
||||
_id: plain._id,
|
||||
publicId: plain.publicId,
|
||||
requestNo: plain.requestNo,
|
||||
type: plain.type,
|
||||
status: plain.status,
|
||||
blameStatus: plain.blameStatus,
|
||||
workflow: plain.workflow,
|
||||
requiresFileMakerApproval: plain.requiresFileMakerApproval,
|
||||
parties: (plain.parties ?? []).map((p: any) => ({
|
||||
role: p.role,
|
||||
person: {
|
||||
phoneNumber: p.person?.phoneNumber,
|
||||
nationalCodeOfInsurer: p.person?.nationalCodeOfInsurer,
|
||||
nationalCodeOfDriver: p.person?.nationalCodeOfDriver,
|
||||
driverIsInsurer: p.person?.driverIsInsurer,
|
||||
userId: p.person?.userId ? String(p.person.userId) : undefined,
|
||||
clientId: p.person?.clientId ? String(p.person.clientId) : undefined,
|
||||
},
|
||||
insurance: p.insurance ? {
|
||||
company: p.insurance.company,
|
||||
policyNumber: p.insurance.policyNumber,
|
||||
startDate: p.insurance.startDate,
|
||||
endDate: p.insurance.endDate,
|
||||
financialCeiling: p.insurance.financialCeiling,
|
||||
} : undefined,
|
||||
vehicle: p.vehicle ? {
|
||||
plateId: p.vehicle.plateId,
|
||||
name: p.vehicle.name,
|
||||
type: p.vehicle.type,
|
||||
} : undefined,
|
||||
inquiryComplete: !!(p.person?.inquiriesCompleted),
|
||||
hasSigned: p.confirmation != null,
|
||||
})),
|
||||
linkedClaimId: claim ? String((claim as any)._id) : null,
|
||||
createdAt: plain.createdAt,
|
||||
updatedAt: plain.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── FileReviewer file list / detail (V4 + V5) ─────────────────────────────
|
||||
|
||||
async getMyFileReviewerFiles(fileReviewer: any): Promise<any[]> {
|
||||
if (fileReviewer?.role !== RoleEnum.FILE_REVIEWER) {
|
||||
throw new ForbiddenException("Only FileReviewers can use this endpoint.");
|
||||
}
|
||||
const reviewerId = new Types.ObjectId(fileReviewer.sub);
|
||||
const files = await this.blameRequestDbService.find({
|
||||
isMadeByFileMaker: true,
|
||||
assignedFileReviewerId: reviewerId,
|
||||
});
|
||||
return (files || []).map((f: any) => ({
|
||||
_id: f._id,
|
||||
publicId: f.publicId,
|
||||
requestNo: f.requestNo,
|
||||
type: f.type,
|
||||
status: f.status,
|
||||
blameStatus: f.blameStatus,
|
||||
workflow: {
|
||||
currentStep: f.workflow?.currentStep,
|
||||
nextStep: f.workflow?.nextStep,
|
||||
completedSteps: f.workflow?.completedSteps,
|
||||
},
|
||||
requiresFileMakerApproval: f.requiresFileMakerApproval,
|
||||
createdAt: f.createdAt,
|
||||
updatedAt: f.updatedAt,
|
||||
}));
|
||||
}
|
||||
|
||||
async getMyFileReviewerFileDetail(fileReviewer: any, requestId: string): Promise<any> {
|
||||
if (fileReviewer?.role !== RoleEnum.FILE_REVIEWER) {
|
||||
throw new ForbiddenException("Only FileReviewers can use this endpoint.");
|
||||
}
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Blame request not found");
|
||||
if (!req.isMadeByFileMaker) {
|
||||
throw new ForbiddenException("FileReviewer can only access V4/V5 FileMaker files.");
|
||||
}
|
||||
const assignedId = (req as any).assignedFileReviewerId
|
||||
? String((req as any).assignedFileReviewerId)
|
||||
: null;
|
||||
if (assignedId && assignedId !== String(fileReviewer.sub)) {
|
||||
throw new ForbiddenException("This file has been taken by another FileReviewer.");
|
||||
}
|
||||
const plain = typeof (req as any).toObject === "function"
|
||||
? (req as any).toObject({ versionKey: false })
|
||||
: { ...(req as any) };
|
||||
const claim = await this.claimCaseDbService.findOne({ blameRequestId: (req as any)._id });
|
||||
return {
|
||||
_id: plain._id,
|
||||
publicId: plain.publicId,
|
||||
requestNo: plain.requestNo,
|
||||
type: plain.type,
|
||||
status: plain.status,
|
||||
blameStatus: plain.blameStatus,
|
||||
workflow: plain.workflow,
|
||||
requiresFileMakerApproval: plain.requiresFileMakerApproval,
|
||||
assignedFileReviewerId: plain.assignedFileReviewerId
|
||||
? String(plain.assignedFileReviewerId)
|
||||
: null,
|
||||
parties: (plain.parties ?? []).map((p: any) => ({
|
||||
role: p.role,
|
||||
person: {
|
||||
phoneNumber: p.person?.phoneNumber,
|
||||
nationalCodeOfInsurer: p.person?.nationalCodeOfInsurer,
|
||||
nationalCodeOfDriver: p.person?.nationalCodeOfDriver,
|
||||
driverIsInsurer: p.person?.driverIsInsurer,
|
||||
userId: p.person?.userId ? String(p.person.userId) : undefined,
|
||||
clientId: p.person?.clientId ? String(p.person.clientId) : undefined,
|
||||
},
|
||||
insurance: p.insurance ? {
|
||||
company: p.insurance.company,
|
||||
policyNumber: p.insurance.policyNumber,
|
||||
startDate: p.insurance.startDate,
|
||||
endDate: p.insurance.endDate,
|
||||
financialCeiling: p.insurance.financialCeiling,
|
||||
} : undefined,
|
||||
vehicle: p.vehicle ? {
|
||||
plateId: p.vehicle.plateId,
|
||||
name: p.vehicle.name,
|
||||
type: p.vehicle.type,
|
||||
} : undefined,
|
||||
inquiryComplete: !!(p.person?.inquiriesCompleted),
|
||||
hasSigned: p.confirmation != null,
|
||||
})),
|
||||
expert: plain.expert ? {
|
||||
decision: plain.expert.decision ? {
|
||||
guiltyPartyId: plain.expert.decision.guiltyPartyId
|
||||
? String(plain.expert.decision.guiltyPartyId)
|
||||
: undefined,
|
||||
description: plain.expert.decision.description,
|
||||
decidedAt: plain.expert.decision.decidedAt,
|
||||
} : undefined,
|
||||
accidentFields: plain.expert.accidentFields,
|
||||
} : undefined,
|
||||
linkedClaimId: claim ? String((claim as any)._id) : null,
|
||||
createdAt: plain.createdAt,
|
||||
updatedAt: plain.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
async getMyCallCenterFilesV6(agent: any): Promise<any[]> {
|
||||
if (agent?.role !== RoleEnum.CALL_CENTER) {
|
||||
throw new ForbiddenException("Only call-center agents can use this endpoint.");
|
||||
|
||||
Reference in New Issue
Block a user