forked from Yara724/api
Fixed Lock for Field expert + user view of files
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
import {
|
||||
assertBlameCaseForExpertTenant,
|
||||
blameCaseAccessibleToExpert,
|
||||
blameCaseInitiatedByFieldExpert,
|
||||
requireActorClientKey,
|
||||
} from "src/helpers/tenant-scope";
|
||||
import {
|
||||
@@ -436,8 +437,9 @@ export class ExpertBlameService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Blame review inbox for FIELD_EXPERT — expert-initiated DISAGREEMENT files only.
|
||||
* IN_PERSON flows are AGREED and handled outside this panel; completed blames appear in expert-claim.
|
||||
* Blame review inbox for FIELD_EXPERT — expert-initiated DISAGREEMENT files.
|
||||
* Covers LINK flows (parties complete via link → WAITING_FOR_EXPERT) and in-progress
|
||||
* disputes while parties are still filling. IN_PERSON AGREED blames use expert-claim instead.
|
||||
*/
|
||||
private async getFieldExpertBlameListV2(
|
||||
actor: { sub: string },
|
||||
@@ -448,9 +450,25 @@ export class ExpertBlameService {
|
||||
|
||||
const visibleCases = (await this.blameRequestDbService.find(
|
||||
{
|
||||
blameStatus: BlameStatus.DISAGREEMENT,
|
||||
expertInitiated: true,
|
||||
initiatedByFieldExpertId: expertOid,
|
||||
blameStatus: BlameStatus.DISAGREEMENT,
|
||||
type: { $ne: BlameRequestType.CAR_BODY },
|
||||
$or: [
|
||||
{ status: CaseStatus.WAITING_FOR_EXPERT },
|
||||
{
|
||||
status: {
|
||||
$in: [
|
||||
CaseStatus.OPEN,
|
||||
CaseStatus.WAITING_FOR_SECOND_PARTY,
|
||||
CaseStatus.WAITING_FOR_DOCUMENT_RESEND,
|
||||
],
|
||||
},
|
||||
},
|
||||
{ "workflow.lockedBy.actorId": expertOid },
|
||||
{ "workflow.assignedForReviewBy.actorId": expertOid },
|
||||
{ "expert.decision.decidedByExpertId": expertOid },
|
||||
],
|
||||
},
|
||||
{ lean: true },
|
||||
)) as Record<string, unknown>[];
|
||||
@@ -966,6 +984,8 @@ export class ExpertBlameService {
|
||||
);
|
||||
}
|
||||
|
||||
const isFieldExpertOwner = blameCaseInitiatedByFieldExpert(doc, actor);
|
||||
|
||||
const decision = (doc.expert as any)?.decision;
|
||||
const decidedByExpertId = decision?.decidedByExpertId
|
||||
? String(decision.decidedByExpertId)
|
||||
@@ -978,6 +998,7 @@ export class ExpertBlameService {
|
||||
(doc.workflow as any)?.assignedForReviewBy?.actorId ?? "",
|
||||
);
|
||||
|
||||
if (!isFieldExpertOwner) {
|
||||
// Access gates — must satisfy at least one bucket
|
||||
const isAvailable =
|
||||
doc.status === CaseStatus.WAITING_FOR_EXPERT && !decidedByExpertId;
|
||||
@@ -1002,6 +1023,7 @@ export class ExpertBlameService {
|
||||
"You do not have permission to view this request.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Build evidence URLs
|
||||
const parties = Array.isArray(doc.parties) ? doc.parties : [];
|
||||
@@ -1158,7 +1180,12 @@ export class ExpertBlameService {
|
||||
*/
|
||||
async assignBlameCaseForReviewV2(
|
||||
requestId: string,
|
||||
actor: { sub: string; fullName?: string; clientKey?: string },
|
||||
actor: {
|
||||
sub: string;
|
||||
fullName?: string;
|
||||
clientKey?: string;
|
||||
role?: string;
|
||||
},
|
||||
): Promise<ExpertFileAssignResultDto> {
|
||||
await this.expireBlameCaseWorkflowLockV2IfStale(requestId);
|
||||
|
||||
@@ -1169,6 +1196,8 @@ export class ExpertBlameService {
|
||||
|
||||
assertBlameCaseForExpertTenant(request, actor);
|
||||
|
||||
const isFieldExpertOwner = blameCaseInitiatedByFieldExpert(request, actor);
|
||||
|
||||
if (request.type === BlameRequestType.CAR_BODY) {
|
||||
throw new BadRequestException({
|
||||
success: false,
|
||||
@@ -1177,9 +1206,17 @@ export class ExpertBlameService {
|
||||
});
|
||||
}
|
||||
|
||||
if (request.blameStatus !== BlameStatus.DISAGREEMENT) {
|
||||
throw new BadRequestException({
|
||||
success: false,
|
||||
status: "unavailable" satisfies ExpertFileAssignStatus,
|
||||
message: "Request is not available for expert review",
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
request.status !== CaseStatus.WAITING_FOR_EXPERT ||
|
||||
request.blameStatus !== BlameStatus.DISAGREEMENT
|
||||
!isFieldExpertOwner &&
|
||||
request.status !== CaseStatus.WAITING_FOR_EXPERT
|
||||
) {
|
||||
throw new BadRequestException({
|
||||
success: false,
|
||||
@@ -1188,6 +1225,26 @@ export class ExpertBlameService {
|
||||
});
|
||||
}
|
||||
|
||||
if (isFieldExpertOwner && request.expert?.decision) {
|
||||
throw new BadRequestException({
|
||||
success: false,
|
||||
status: "unavailable" satisfies ExpertFileAssignStatus,
|
||||
message: "This request already has an expert decision",
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
isFieldExpertOwner &&
|
||||
request.status !== CaseStatus.WAITING_FOR_EXPERT
|
||||
) {
|
||||
throw new BadRequestException({
|
||||
success: false,
|
||||
status: "unavailable" satisfies ExpertFileAssignStatus,
|
||||
message:
|
||||
"Parties are still completing this file. Lock and review become available when status is WAITING_FOR_EXPERT (e.g. after both parties finish via link).",
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
request.expertInitiated &&
|
||||
request.initiatedByFieldExpertId &&
|
||||
@@ -1272,7 +1329,6 @@ export class ExpertBlameService {
|
||||
|
||||
const assignFilter: Record<string, unknown> = {
|
||||
_id: new Types.ObjectId(requestId),
|
||||
status: CaseStatus.WAITING_FOR_EXPERT,
|
||||
blameStatus: BlameStatus.DISAGREEMENT,
|
||||
$and: [
|
||||
{
|
||||
@@ -1293,8 +1349,11 @@ export class ExpertBlameService {
|
||||
],
|
||||
};
|
||||
|
||||
if (request.expertInitiated) {
|
||||
if (isFieldExpertOwner) {
|
||||
assignFilter.initiatedByFieldExpertId = expertOid;
|
||||
assignFilter.status = CaseStatus.WAITING_FOR_EXPERT;
|
||||
} else {
|
||||
assignFilter.status = CaseStatus.WAITING_FOR_EXPERT;
|
||||
}
|
||||
|
||||
const updated = await this.blameRequestDbService.findOneAndUpdate(
|
||||
|
||||
@@ -43,7 +43,7 @@ export class ExpertBlameV2Controller {
|
||||
summary: "List blame cases for expert review (V2)",
|
||||
description:
|
||||
"Damage experts (`expert`): tenant-scoped **DISAGREEMENT** queue (available, locked, or decided by you). " +
|
||||
"Field experts (`field_expert`): only **DISAGREEMENT** files they initiated that need expert review (e.g. LINK disputes). " +
|
||||
"Field experts (`field_expert`): expert-initiated **DISAGREEMENT** files — including **LINK** flows where parties complete via link and the case moves to `WAITING_FOR_EXPERT` for your review. " +
|
||||
"IN_PERSON expert-initiated blames are usually `AGREED` and are managed via expert-initiated / request-management APIs; after completion, use expert-claim. " +
|
||||
"Optional query: `search`, `sortBy` (publicId | createdAt | requestNo | status), `sortOrder`, `page`, `limit`.",
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user