[CLAUDE] PurchaseEvaluation: Chunk M1 — Fix F1.OneLevel/OneStep edge case Bước 1 → giữ ChoDuyet (KHÔNG fallback Drafter)

Bro UAT S23 t3: "Các tính năng trả lại 1 cấp hoặc chỉ định hoặc edit cho
xử lý ở trạng thái đang gửi duyệt luôn, không về draft."

Investigator audit confirm 4 mode F1.OneLevel/Assignee + F2 + F3+F4 main
path đã giữ Phase=ChoDuyet (Mig 28-31 cumulative). Edge case duy nhất còn
fallback Drafter (Phase=TraLai):

- F1.OneLevel khi đang Bước 1 Cấp 1 (curStepIdx=0, curLevel=1) — no further back
- F1.OneStep khi đang Bước 1 (curStepIdx=0)

Logic cũ (line 303-310 OneLevel + 325-332 OneStep):
```
evaluation.Phase = PurchaseEvaluationPhase.TraLai;  // 98
evaluation.CurrentWorkflowStepIndex = null;
evaluation.CurrentApprovalLevelOrder = null;
evaluation.SlaDeadline = null;
return "Trả về Người soạn thảo (fallback — đang Bước 1 Cấp 1)";
```

Logic mới — reset (0, 1) giữ ChoDuyet:
```
evaluation.CurrentWorkflowStepIndex = 0;
evaluation.CurrentApprovalLevelOrder = 1;
summary = "Action 'Trả lại 1 Cấp/Bước' không lùi được — phiếu reset về Approver Bước 1 Cấp 1";
// SLA reset 7d ở cuối hàm cho 3 mode còn lại
```

Semantic mới (per bro chốt AskUserQuestion Plan M):
- Phase giữ ChoDuyet (KHÔNG TraLai=98)
- Pointer reset về (0, 1) = chính Approver A hiện tại (effectively no-op)
- SLA reset 7d (cuối hàm switch áp dụng cho cả 3 mode F1 non-Drafter)
- Audit log rõ "không lùi được" để Drafter/Admin biết action không hiệu lực

KHÔNG đụng:
- F1.Drafter (line 268-275) giữ nguyên semantic Phase=TraLai
- F1.Assignee (line 335-360) giữ nguyên throw nếu không match
- F2 ApproveV2Async skipToFinal (line 483-524 Plan K L1 vừa fix)
- F3 EnsureEditableForDetailsAsync (PurchaseEvaluationDetailFeatures.cs:42)
- F4 AdjustBudgetCommand handler (PurchaseEvaluationFeatures.cs:272-329)

Verify:
- dotnet build src/Backend/SolutionErp.Infrastructure clean (0 err, 2 warn
  pre-existing DocxRenderer)
- Service.cs 13+/13- LOC change (1 file, surgical edit)
- Pending Chunk M2: 2 edge case test (Implementer Case 3 spawn) + verify
  K7 Approver F2 không cascade
- Pending Chunk M3: FE label rename Phase=TraLai "Trả lại" → "Cần chỉnh sửa lại" (Implementer Case 2 spawn × 2 app)
- Pending Chunk M4: docs + memory update + push + CICD verify

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-15 11:11:36 +07:00
parent 83c9f7b45d
commit c2042ef956

View File

@ -286,7 +286,8 @@ public class PurchaseEvaluationWorkflowService(
{ {
case WorkflowReturnMode.OneLevel: case WorkflowReturnMode.OneLevel:
// Lùi 1 Cấp trong cùng Step. Nếu đang Cấp 1 → lùi sang Bước trước // Lùi 1 Cấp trong cùng Step. Nếu đang Cấp 1 → lùi sang Bước trước
// Cấp cuối. Nếu đang Bước 1 Cấp 1 → fallback Drafter (no further). // Cấp cuối. Nếu đang Bước 1 Cấp 1 → reset về (0, 1) giữ ChoDuyet
// (Plan M S23 t3 — KHÔNG fallback Drafter, phiếu giữ "đang duyệt").
if (curLevel > 1) if (curLevel > 1)
{ {
evaluation.CurrentApprovalLevelOrder = curLevel - 1; evaluation.CurrentApprovalLevelOrder = curLevel - 1;
@ -302,12 +303,12 @@ public class PurchaseEvaluationWorkflowService(
} }
else else
{ {
// Bước 1 Cấp 1 — no further back. Fallback Drafter. // Bước 1 Cấp 1 → reset về (0, 1) giữ Phase=ChoDuyet (no-op effective
evaluation.Phase = PurchaseEvaluationPhase.TraLai; // — Approver A hiện tại). Audit log rõ "không lùi được". SLA reset
evaluation.CurrentWorkflowStepIndex = null; // dưới cuối hàm cho approver giữ nguyên.
evaluation.CurrentApprovalLevelOrder = null; evaluation.CurrentWorkflowStepIndex = 0;
evaluation.SlaDeadline = null; evaluation.CurrentApprovalLevelOrder = 1;
return "Trả về Người soạn thảo (fallback — đang Bước 1 Cấp 1)"; summary = "Action 'Trả lại 1 Cấp' không lùi được — phiếu reset về Approver Bước 1 Cấp 1";
} }
break; break;
@ -323,12 +324,11 @@ public class PurchaseEvaluationWorkflowService(
} }
else else
{ {
// Đang Bước 1 → fallback Drafter // Bước 1 → reset về (0, 1) giữ Phase=ChoDuyet (Plan M S23 t3 —
evaluation.Phase = PurchaseEvaluationPhase.TraLai; // KHÔNG fallback Drafter, phiếu giữ "đang duyệt").
evaluation.CurrentWorkflowStepIndex = null; evaluation.CurrentWorkflowStepIndex = 0;
evaluation.CurrentApprovalLevelOrder = null; evaluation.CurrentApprovalLevelOrder = 1;
evaluation.SlaDeadline = null; summary = "Action 'Trả lại 1 Bước' không lùi được — phiếu reset về Approver Bước 1 Cấp 1";
return "Trả về Người soạn thảo (fallback — đang Bước đầu)";
} }
break; break;