FIX LOCKS

This commit is contained in:
SepehrYahyaee
2026-04-26 16:08:14 +03:30
parent 885678df7d
commit 4b0e8a3547

View File

@@ -2595,8 +2595,9 @@ export class ExpertClaimService {
requireActorClientKey(actor); requireActorClientKey(actor);
const actorId = actor.sub; const actorId = actor.sub;
const clientKey = actor.clientKey as string; const clientKey = actor.clientKey as string;
const lockTtlMs = this.claimV2WorkflowLockTtlMs;
const now = new Date(); await this.reconcileStaleExpertReviewingClaimLocksForTenant(actor);
const claims = await this.claimCaseDbService.find({ const claims = await this.claimCaseDbService.find({
$or: [ $or: [
// Available claims: waiting for expert, not locked // Available claims: waiting for expert, not locked
@@ -2609,27 +2610,6 @@ export class ExpertClaimService {
'workflow.locked': true, 'workflow.locked': true,
'workflow.lockedBy.actorId': new Types.ObjectId(actorId), 'workflow.lockedBy.actorId': new Types.ObjectId(actorId),
}, },
// EXPERT_REVIEWING but lock cleared or TTL elapsed (reconcile on read; fixes invisible rows)
{
status: ClaimCaseStatus.EXPERT_REVIEWING,
$or: [
{ 'workflow.locked': { $ne: true } },
{ 'workflow.expiredAt': { $lte: now } },
{
$expr: {
$and: [
{ $eq: ['$workflow.locked', true] },
{
$lte: [
{ $add: ['$workflow.lockedAt', lockTtlMs] },
now,
],
},
],
},
},
],
},
// User uploaded all factors; expert must approve/reject (unlocked queue) // User uploaded all factors; expert must approve/reject (unlocked queue)
{ {
status: ClaimCaseStatus.WAITING_FOR_INSURER_APPROVAL, status: ClaimCaseStatus.WAITING_FOR_INSURER_APPROVAL,
@@ -2702,10 +2682,14 @@ export class ExpertClaimService {
const fileCtx = blame ? this.blameFileContextForExpert(blame) : {}; const fileCtx = blame ? this.blameFileContextForExpert(blame) : {};
const lockActive = const lockActive =
!!(c.workflow?.locked && this.isClaimV2WorkflowLockCurrentlyEnforced(c)); !!(c.workflow?.locked && this.isClaimV2WorkflowLockCurrentlyEnforced(c));
const statusForList =
c.status === ClaimCaseStatus.EXPERT_REVIEWING
? ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT
: c.status;
return { return {
claimRequestId: c._id.toString(), claimRequestId: c._id.toString(),
publicId: c.publicId, publicId: c.publicId,
status: c.status, status: statusForList,
currentStep: c.workflow?.currentStep || "", currentStep: c.workflow?.currentStep || "",
locked: lockActive, locked: lockActive,
lockedBy: lockedBy:
@@ -2876,6 +2860,51 @@ export class ExpertClaimService {
); );
} }
/**
* EXPERT_REVIEWING + expired lock is not listed until status is reverted. Run expire for this tenant
* so the next query returns those cases as WAITING_FOR_DAMAGE_EXPERT.
*/
private async reconcileStaleExpertReviewingClaimLocksForTenant(actor: {
sub: string;
clientKey?: string;
}): Promise<void> {
const clientKey = requireActorClientKey(actor);
const lockTtlMs = this.claimV2WorkflowLockTtlMs;
const now = new Date();
const candidates = (await this.claimCaseDbService.find(
{
status: ClaimCaseStatus.EXPERT_REVIEWING,
"workflow.locked": true,
$or: [
{ "workflow.expiredAt": { $lte: now } },
{
$expr: {
$and: [
{ $eq: ["$workflow.locked", true] },
{
$lte: [
{ $add: ["$workflow.lockedAt", lockTtlMs] },
now,
],
},
],
},
},
],
},
{ lean: true, select: "_id owner" },
)) as Array<{ _id: unknown; owner?: unknown }>;
const relevant = candidates.filter((c) =>
claimCaseTouchesClient(c, clientKey),
);
await Promise.all(
relevant.map((c) =>
this.expireClaimWorkflowLockV2IfStale(String(c._id)),
),
);
}
/** /**
* Persist unlock when claim V2 workflow lock TTL has passed. * Persist unlock when claim V2 workflow lock TTL has passed.
* If the expert never submitted a damage reply, restores queue fields from * If the expert never submitted a damage reply, restores queue fields from
@@ -3158,10 +3187,15 @@ export class ExpertClaimService {
} }
: undefined; : undefined;
const statusForExpertApi =
claim.status === ClaimCaseStatus.EXPERT_REVIEWING
? ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT
: claim.status;
return { return {
claimRequestId: claim._id.toString(), claimRequestId: claim._id.toString(),
publicId: claim.publicId, publicId: claim.publicId,
status: claim.status, status: statusForExpertApi,
claimStatus: claim.claimStatus || 'PENDING', claimStatus: claim.claimStatus || 'PENDING',
currentStep: claim.workflow?.currentStep || '', currentStep: claim.workflow?.currentStep || '',
nextStep: claim.workflow?.nextStep, nextStep: claim.workflow?.nextStep,