import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum"; import { ClaimStatus } from "src/Types&Enums/claim-request-management/claimStatus.enum"; import { ClaimWorkflowStep } from "src/Types&Enums/claim-request-management/claim-workflow-steps.enum"; export interface ClaimPricingPartLite { factorNeeded?: boolean; /** Numeric catalog id (legacy string values may still exist in old documents). */ partId?: number | string; } export function getActiveV2ExpertReply(claim: { evaluation?: Record }): { replyKey: "damageExpertReplyFinal" | "damageExpertReply"; reply: Record; parts: ClaimPricingPartLite[]; } | null { const fin = claim.evaluation?.damageExpertReplyFinal as | { parts?: ClaimPricingPartLite[]; submittedAt?: Date } | undefined; const ini = claim.evaluation?.damageExpertReply as | { parts?: ClaimPricingPartLite[]; submittedAt?: Date } | undefined; if (fin?.submittedAt && fin?.parts?.length) { return { replyKey: "damageExpertReplyFinal", reply: fin as Record, parts: fin.parts }; } if (ini?.submittedAt && ini?.parts?.length) { return { replyKey: "damageExpertReply", reply: ini as Record, parts: ini.parts }; } return null; } export function classifyV2ExpertPricingParts(parts: ClaimPricingPartLite[]) { const factorNeeded = parts.filter((p) => p.factorNeeded === true); const priced = parts.filter((p) => p.factorNeeded !== true); return { pricedPartsCount: priced.length, factorNeededPartsCount: factorNeeded.length, mixedFactorAndPrice: factorNeeded.length > 0 && priced.length > 0, allFactorLines: factorNeeded.length > 0 && priced.length === 0, noFactors: factorNeeded.length === 0, }; } const OWNER_POST_EXPERT_INSURER_SIGNATURE_STATUSES: ReadonlySet = new Set([ ClaimCaseStatus.WAITING_FOR_INSURER_APPROVAL, ClaimCaseStatus.INSURER_REVIEW_AWAITING_OWNER_SIGN, ClaimCaseStatus.INSURER_REVIEW_MIXED_FACTORS_PENDING, ]); const OWNER_FACTOR_UPLOAD_PHASE_STATUSES: ReadonlySet = new Set([ ClaimCaseStatus.WAITING_FOR_INSURER_APPROVAL, ClaimCaseStatus.OWNER_REPAIR_FACTOR_UPLOAD_PENDING, ClaimCaseStatus.INSURER_REVIEW_MIXED_FACTORS_PENDING, ]); const V2_POST_EXPERT_OWNER_PIPELINE_STATUSES: ReadonlySet = new Set([ ClaimCaseStatus.WAITING_FOR_INSURER_APPROVAL, ClaimCaseStatus.INSURER_REVIEW_AWAITING_OWNER_SIGN, ClaimCaseStatus.INSURER_REVIEW_MIXED_FACTORS_PENDING, ClaimCaseStatus.OWNER_REPAIR_FACTOR_UPLOAD_PENDING, ClaimCaseStatus.EXPERT_VALIDATING_REPAIR_FACTORS, ]); /** Owner multipart sign at `INSURER_REVIEW` (priced-line gate or final acceptance). */ export function claimCaseStatusAllowsOwnerInsurerSignStep( status: ClaimCaseStatus | string | undefined, ): boolean { return OWNER_POST_EXPERT_INSURER_SIGNATURE_STATUSES.has(String(status ?? "")); } /** Repair-factor upload during `NEEDS_REVISION` @ `OWNER_UPLOAD_FACTOR_DOCUMENTS`. */ export function claimCaseStatusAllowsOwnerFactorUploadDuringRevision( status: ClaimCaseStatus | string | undefined, ): boolean { return OWNER_FACTOR_UPLOAD_PHASE_STATUSES.has(String(status ?? "")); } /** Owner-facing post-expert flow: signatures, factor uploads, or waiting on expert factor validation. */ export function claimCaseStatusIsV2OwnerPostExpertPipeline( status: ClaimCaseStatus | string | undefined, ): boolean { return V2_POST_EXPERT_OWNER_PIPELINE_STATUSES.has(String(status ?? "")); } export function claimIsAwaitingExpertFactorValidationV2(claim: { status?: ClaimCaseStatus | string; claimStatus?: ClaimStatus; workflow?: { currentStep?: string }; }): boolean { return ( claim.claimStatus === ClaimStatus.UNDER_REVIEW && claim.workflow?.currentStep === ClaimWorkflowStep.EXPERT_COST_EVALUATION && (claim.status === ClaimCaseStatus.EXPERT_VALIDATING_REPAIR_FACTORS || claim.status === ClaimCaseStatus.WAITING_FOR_INSURER_APPROVAL) ); } export function claimCaseStatusAfterExpertReplyV2(parts: ClaimPricingPartLite[]): ClaimCaseStatus { const { mixedFactorAndPrice } = classifyV2ExpertPricingParts(parts); const needsFactorUpload = parts.some((p) => p.factorNeeded === true); if (!needsFactorUpload) { return ClaimCaseStatus.INSURER_REVIEW_AWAITING_OWNER_SIGN; } if (mixedFactorAndPrice) { return ClaimCaseStatus.INSURER_REVIEW_MIXED_FACTORS_PENDING; } return ClaimCaseStatus.OWNER_REPAIR_FACTOR_UPLOAD_PENDING; } export function claimIsV2ExpertFactorUploadStep(claim: { workflow?: { currentStep?: string } }) { return claim.workflow?.currentStep === ClaimWorkflowStep.OWNER_UPLOAD_FACTOR_DOCUMENTS; } export function claimIsV2PendingFactorExpertValidation(claim: { status?: ClaimCaseStatus; claimStatus?: ClaimStatus; workflow?: { currentStep?: string }; }) { return claimIsAwaitingExpertFactorValidationV2(claim); } export function objectionDisallowedDueToOutstandingFactorWorkflow(claim: { workflow?: { currentStep?: string }; }) { const step = claim.workflow?.currentStep; return ( step === ClaimWorkflowStep.OWNER_UPLOAD_FACTOR_DOCUMENTS || step === ClaimWorkflowStep.EXPERT_COST_EVALUATION ); }