[CLAUDE] Docs: Session 17 wrap-up — PE Workflow V2 end-to-end consolidation

User chốt MD wrap-up Session 17 (13 commit từ c847dc0de0f38d) — PE
Workflow V2 schema + Service wire + UX iteration đầy đủ.

MD updates:
- STATUS.md — header counts (24 mig, 58 tables, 81 test, 43 gotcha) +
  Recently Done row consolidate Session 17 wrap-up (gộp 4 row iter cũ
  thành 1 row tổng, không cắt narrative quan trọng)
- HANDOFF.md — TL;DR Session 17 + Chunk E (Designer iter + State
  machine + Service wire + UX) + Pending Session 18+
- CLAUDE.md — count (22→24 mig, 77→81 test) + V2 schema overview
- changelog/migration-todos.md — section  Session 17 done với 7 task
  ticked + Defer Session 18+ explicit
- database/schema-diagram.md — §14 ApprovalWorkflow V2 schema (3 bảng
  + 2 column PE + state transitions + Service branch + Designer
  constraints + match approver V2 vs V1 table)
- gotchas.md — +#42 Dual schema branch + #43 Step.Order ≠ index 0-based
- skill contract-workflow — +section "Phase 9+ done Mig 22-24" với V2
  spec + Service branch + match logic table + Designer constraints +
  UX V2-aware + Defer Session 18+
- changelog/sessions/2026-05-08-1100-pe-workflow-v2-end-to-end.md (NEW)
  — full session log với 13 commit timeline + stats + gotchas + pending

Memory:
- project_solution_erp.md — entry Session 17 wrap-up đầy đủ (KHÔNG tạo
  file feedback mới — decisions specific cho project, không reusable
  cross-project per §9.5 anti-pattern)

Verify:
- 81 test pass (58 Domain + 23 Infra) — không thay đổi sau wrap-up
- 0 BE error
- 2 FE builds OK (đã verify ở các commit trước)
- Quy tắc consolidate §6.5: KHÔNG cắt narrative, chỉ phân tầng + remove
  duplicate. Session 17 row dài cố ý — cover full 13 commit context cho
  Session 18+ đọc lại.
This commit is contained in:
pqhuy1987
2026-05-08 16:40:49 +07:00
parent de0f38dd25
commit 8680f4c849
8 changed files with 411 additions and 19 deletions

View File

@ -715,7 +715,89 @@ CREATE TABLE PurchaseEvaluationDepartmentOpinions (
CREATE UNIQUE INDEX IX_PEDeptOpinions_PEId_Kind ON PurchaseEvaluationDepartmentOpinions (PurchaseEvaluationId, Kind);
```
## 14. Liên quan
## 14. ApprovalWorkflow V2 schema (Migration 22-24, Session 17 — 3 bảng mới + 2 column)
Schema riêng song song WorkflowDefinition V1 (Mig 21) — pin per phiếu PE.
V1 vẫn giữ cho phiếu cũ; V2 mới là active cho phiếu tạo từ Session 17 trở đi.
### Core (3 bảng):
```
ApprovalWorkflows
├── Id (PK), Code, Version (UNIQUE Code+Version)
├── ApplicableType (1=DuyetNcc, 2=DuyetNccPhuongAn, 3=Contract)
├── Name, Description, IsActive, ActivatedAt
└── (audit) CreatedAt, UpdatedAt, CreatedBy, UpdatedBy
ApprovalWorkflowSteps (FK Cascade ApprovalWorkflowId, FK Restrict DepartmentId)
├── Id (PK), ApprovalWorkflowId, Order (1-based, sort key — KHÔNG phải position)
├── Name (vd "Phòng A"), DepartmentId? (hint)
└── INDEX (ApprovalWorkflowId, Order) + INDEX DepartmentId
ApprovalWorkflowLevels (FK Cascade ApprovalWorkflowStepId, FK Restrict ApproverUserId)
├── Id (PK), ApprovalWorkflowStepId, Order (1/2/3 trong Step)
├── Name? (vd "Cấp 1"), ApproverUserId (1 NV cụ thể)
└── INDEX (ApprovalWorkflowStepId, Order) + INDEX ApproverUserId
```
**Convention quan trọng:** nhiều `ApprovalWorkflowLevel` rows cùng `Order` trong cùng Step = **same Cấp với N approvers** (OR-of-N). Ví dụ Cấp 1 có 2 NV: 2 row Level cùng `Order=1` khác `ApproverUserId`.
### Pin V2 vào PE (Mig 23-24):
```
PurchaseEvaluations (column mới)
├── ApprovalWorkflowId Guid? (FK Restrict ApprovalWorkflows) — pin lúc create
└── CurrentApprovalLevelOrder int? — track Cấp đang chờ duyệt (1/2/3)
```
Combined với `CurrentWorkflowStepIndex int?` (Mig 21) để track full state V2:
- `CurrentWorkflowStepIndex` = 0-based index của Step (sau khi sort by `Order`)
- `CurrentApprovalLevelOrder` = `Order` của Cấp đang chờ trong Step đó
Service iterate: `steps[CurrentStepIndex].Levels.Where(l => l.Order == CurrentLevelOrder)` → list approvers, match `actor.Id ∈ ApproverUserId`.
### Match approver V2 (khác V1):
| Schema | Match logic | Approver type |
|---|---|---|
| V1 (Mig 21) | `actor.Dept == step.Dept && actor.PositionLevel >= step.PositionLevel` OR Approvers Role/User explicit | Group qua Dept+Level |
| V2 (Mig 22-24) | `actor.Id == any level.ApproverUserId where level.Order == currentLevelOrder` | NV cụ thể 1-1 |
### State transitions (V2 + V1 cùng):
```
DangSoanThao ──Drafter trình──► ChoDuyet (idx=0, levelOrder=1 nếu V2)
TraLai ──Drafter sửa+gửi lại──► ChoDuyet (idx=0, levelOrder=1) — chạy LẠI từ đầu
ChoDuyet ──Approver duyệt cấp──► ChoDuyet (advance levelOrder hoặc idx)
──Approver Trả lại──► TraLai (clear pointers)
──Approver Từ chối──► TuChoi (terminal khoá)
──last step+level done──► DaDuyet (terminal)
```
### Service branch theo schema pin:
```csharp
// PurchaseEvaluationWorkflowService.TransitionAsync (Mig 24 wire)
if (evaluation.ApprovalWorkflowId is Guid awId)
await ApproveV2Async(...) // iterate ApprovalWorkflowSteps + Levels match ApproverUserId
else
await ApproveV1LegacyAsync(...) // iterate WorkflowSteps match Dept+PositionLevel
```
### Designer constraints (FE Validator + BE Validator):
- Tối đa 3 Cấp/Bước (`Order ∈ {1,2,3}`)
- Sequential gating: `1``1+2``1+2+3` (không cho `2` nếu thiếu `1`, không `1+3` nếu thiếu `2`)
- No duplicate `(Order, ApproverUserId)` cùng Step (1 NV không thêm 2 lần cùng Cấp)
- Mỗi Cấp có N NV (OR-of-N)
- Đổi Phòng → clear toàn bộ approvers (NV cũ có thể không thuộc Phòng mới)
### Pending Session 18+:
- Contract V2 wire (Mig 25): mirror PE pattern — thêm `Contract.ApprovalWorkflowId` + `CurrentApprovalLevelOrder`
- Drop legacy V1: sau khi không còn phiếu pin `WorkflowDefinitionId` → drop `WorkflowDefinitions` + `WorkflowSteps` + `WorkflowStepApprovers` + drop deprecated columns `RejectedAtStepIndex` / `RejectedFromPhase`
## 15. Liên quan
- [`database-guide.md`](database-guide.md) — conventions + migration workflow + cheatsheet đầy đủ
- [`../architecture.md`](../architecture.md) — layered architecture + data flow