[CLAUDE] Docs: Session 17 wrap-up — PE Workflow V2 end-to-end consolidation
User chốt MD wrap-up Session 17 (13 commit từc847dc0→de0f38d) — 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:
@ -157,6 +157,34 @@ Session log: `2026-04-28-chot-session-4-budget.md`.
|
||||
|
||||
## 📝 Phase 9 — UAT + Ops + carry over (Session 6+ active)
|
||||
|
||||
### ✅ Session 17 done (2026-05-08) — PE Workflow V2 schema + Service wire end-to-end (Mig 22-24, 13 commit `c847dc0` → `de0f38d`)
|
||||
|
||||
User chốt sau Session 16 drastic refactor flat (Mig 21) vẫn chưa đúng intent. Yêu cầu schema riêng + Menu mới "Duyệt NCC (Mới)" — Quy trình > Bước (Phòng) > Cấp (NV cụ thể qua ApproverUserId). State machine 5 trạng thái với Trả lại = Phase RIÊNG (Option A user chốt diagram).
|
||||
|
||||
- [x] **Mig 22** `AddApprovalWorkflowsV2` — 3 entity ApprovalWorkflow/Step/Level + ApplicableType enum (DuyetNcc/DuyetNccPhuongAn/Contract). 3 CREATE TABLE + UNIQUE (Code, Version) + FK Cascade Step→Workflow + Level→Step + FK Restrict Department + ApproverUserId. DbInitializer +menu V2. Designer page `/system/approval-workflows-v2/:typeCode` ~480 LOC. (`c847dc0/f6047d5/2781c7e/12daa7f`)
|
||||
|
||||
- [x] **Designer iter 2** đúng intent: max 3 cấp × N NV/cấp + sequential gating C2/C3 disabled khi prev empty + filter NV theo Phòng + no-dup same level. Validator BE Order∈{1,2,3} + HaveSequentialOrders + HaveNoDuplicateApproverInSameLevel. (`9712778` iter 1 sai → `f3bea3c` iter 2 đúng)
|
||||
|
||||
- [x] **State machine 5 trạng thái** Nháp / Đã gửi duyệt / Trả lại / Từ chối / Đã duyệt. TraLai = Phase RIÊNG (98), KHÔNG revert DangSoanThao + KHÔNG jump-back step. Drafter từ TraLai sửa+gửi lại chạy LẠI từ Cấp 1 Bước 1. ContractPhase + BudgetPhase +TraLai. PE/Contract/Budget Policy + Service Reject branch trỏ → TraLai. RejectedAtStepIndex/RejectedFromPhase deprecated (giữ DB column). 4 test mới TraLai entry point. FE rename "Bản nháp" → "Nháp". (`ff21120`)
|
||||
|
||||
- [x] **Mig 23** `AddApprovalWorkflowIdToPurchaseEvaluation` — pin V2 vào PE entity. CreatePurchaseEvaluationCommand +Validate ApplicableType match PE.Type. UpdateDraft cho phép sửa Phase=Nháp/TraLai. Workspace Select bắt buộc workflow lúc create + display "QT-DN-V2-001 v01 — Tên (đang áp dụng)". (`0a40c65`)
|
||||
|
||||
- [x] **Mig 24** `AddCurrentApprovalLevelOrderToPe` + Service V2 wire. PE Service branch theo `ApprovalWorkflowId` set or null: V2 `ApproveV2Async` group Levels by Order = Cấp (OR-of-N approvers cùng cấp), match `actor.Id ∈ ApproverUserId`, advance levelOrder++ → idx++ + reset levelOrder=1 → DaDuyet. V1 `ApproveV1LegacyAsync` giữ logic cũ. Synthetic Policy `ForV2Schema()` cho FE nextPhases. (`b41484b`)
|
||||
|
||||
- [x] **UX V2-aware** disable button + banner. DTO `CurrentApproval` + `ApprovalFlow` (full Steps/Levels Status Done/Current/Pending). Banner emerald "Đến lượt bạn" / amber "Không phải lượt bạn — chỉ {NV X / Y} duyệt được". Button Duyệt forward disabled khi non-approver + tooltip. Trả lại + Từ chối vẫn enabled. Inbox V2-aware `ResolveV2InboxIdsAsync`. 2 dropdown filter "Quy trình" + "Trạng thái" (chỉ ở Duyệt). Panel 3 thay 4 phase cards bằng flow workflow thực tế. (`d814429/9e63e2d/d250ae4/74745a7/de0f38d`)
|
||||
|
||||
- [x] **Test setup**: SQL `clean-transactional-uat.sql` clean prod (9 PE + 11 HĐ + 19 Notif xóa) giữ master via SSH VPS. Test user `nv.test@solutions.com.vn`/`TestUser@123456` (Drafter, CCM) tạo qua API admin. (`ac41d5e`)
|
||||
|
||||
**Stats final Session 17:** 24 mig (+3), 58 DB tables (+3), ~140 endpoints (+5), 33 FE pages (+1 Designer V2), **81 test pass** (+4 TraLai entry point Domain).
|
||||
|
||||
**Defer Session 18+:**
|
||||
- [ ] **Contract V2 wire** (Mig 25) — mirror PE pattern: thêm `Contract.ApprovalWorkflowId` + `CurrentApprovalLevelOrder` + `ContractWorkflowService.ApproveV2Async` + Workspace Select V2 trong ContractCreatePage
|
||||
- [ ] **Phân quyền strict V2** — hiện loose UAT (mọi authenticated user thấy phiếu V2). List chỉ Drafter + approver any-Step + Admin
|
||||
- [ ] **Drop legacy V1** sau khi không còn phiếu pin `WorkflowDefinitionId`: drop tables + cleanup migration drop `RejectedAtStepIndex`/`RejectedFromPhase` columns
|
||||
- [ ] **Admin role bypass** decision prod — option C có audit log "[Admin override]" nếu cần (hiện UAT bypass không log riêng)
|
||||
- [ ] **Test V2 Service wire** (defer khi UAT confirm + có sample data) — Domain test cho ApproveV2Async match logic
|
||||
- [ ] **Budget V2 wire** (defer xa hơn — sau Contract V2)
|
||||
|
||||
### ✅ Session 16 done (2026-05-08) — DRASTIC REFACTOR flat workflow Phòng × Cấp (Mig 21, 2 commit Chunk A+B)
|
||||
|
||||
User chốt drastic refactor: bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking. Workflow flat list (Phòng × Cấp × Approvers). Pin WorkflowDefinitionId. Per memory `feedback_drastic_refactor_scope.md`: dedicated session + conservative buffer.
|
||||
|
||||
@ -0,0 +1,125 @@
|
||||
# 2026-05-08 (Session 17 wrap-up) — PE Workflow V2 schema + Service wire end-to-end
|
||||
|
||||
**13 commit** từ `c847dc0` → `de0f38d`. Resume từ Session 16 (drastic refactor flat Mig 21) — user phát hiện schema flat vẫn không đúng intent → yêu cầu schema riêng + Menu mới.
|
||||
|
||||
## Spec từ user
|
||||
|
||||
```
|
||||
Mã Quy trình - Tên Quy trình
|
||||
* Bước 1 - Phòng A
|
||||
* Cấp 1 - NV X (1 NV cụ thể qua ApproverUserId)
|
||||
* Cấp 2 - NV Y
|
||||
* Bước 2 - Phòng B
|
||||
* Cấp 1 - NV Z
|
||||
```
|
||||
|
||||
Khác Mig 21: mỗi Cấp = NV CỤ THỂ qua `ApproverUserId`, KHÔNG group Dept+PositionLevel/Role/User.
|
||||
|
||||
## State machine 5 trạng thái (Option A user chốt diagram)
|
||||
|
||||
```
|
||||
Nháp ──Drafter trình──► Đã gửi duyệt ──approve cấp cuối──► Đã duyệt (terminal)
|
||||
├─ Trả lại ──────────► Trả lại
|
||||
└─ Từ chối ──────────► Từ chối (terminal)
|
||||
Trả lại ──Drafter sửa+gửi lại──► Đã gửi duyệt (chạy LẠI từ Cấp 1 Bước 1)
|
||||
```
|
||||
|
||||
Trả lại = Phase RIÊNG (`TraLai=98`), KHÔNG revert DangSoanThao + KHÔNG jump-back. Drafter từ TraLai gửi lại = entry point thứ 2 mirror DangSoanThao → workflow chạy lại từ đầu.
|
||||
|
||||
## Migration 22-24 (3 mig mới)
|
||||
|
||||
| Mig | Tên | Nội dung |
|
||||
|---|---|---|
|
||||
| **22** | `AddApprovalWorkflowsV2` | 3 entity: ApprovalWorkflow + Step + Level + ApplicableType enum (DuyetNcc/DuyetNccPhuongAn/Contract). 3 CREATE TABLE + UNIQUE (Code, Version) + FK Cascade Step→Workflow + Level→Step + FK Restrict Department + ApproverUserId. Menu V2 seed |
|
||||
| **23** | `AddApprovalWorkflowIdToPurchaseEvaluation` | PE.ApprovalWorkflowId Guid? FK Restrict — pin V2 lúc create |
|
||||
| **24** | `AddCurrentApprovalLevelOrderToPe` | PE.CurrentApprovalLevelOrder int? — track Cấp đang chờ |
|
||||
|
||||
## 13 commit timeline
|
||||
|
||||
| # | Commit | Tóm tắt |
|
||||
|---|---|---|
|
||||
| 1 | `c847dc0` | Chunk A — Domain V2 + EF Config + Mig 22 + Menu seed |
|
||||
| 2 | `f6047d5` | Chunk B — Application CQRS + API ApprovalWorkflowsV2Controller |
|
||||
| 3 | `2781c7e` | Chunk C — FE Designer ~480 LOC `ApprovalWorkflowsV2Page.tsx` |
|
||||
| 4 | `12daa7f` | Chunk D — Docs Schema mới |
|
||||
| 5 | `9712778` | Designer iter 1 — lock 3 cấp/bước (sai intent) |
|
||||
| 6 | `f3bea3c` | Designer iter 2 — đúng intent: max 3 cấp × N NV/cấp + sequential gating + filter Phòng + Validator BE strict |
|
||||
| 7 | `ff21120` | State machine 5 trạng thái — TraLai Phase riêng. Policy + Service Reject branch → TraLai. 4 test mới TraLai entry. FE rename "Bản nháp" → "Nháp" |
|
||||
| 8 | `d642fd3` | Docs STATUS Session 17 5 trạng thái |
|
||||
| 9 | `0a40c65` | Mig 23 pin V2 vào PE + Workspace Select V2 |
|
||||
| 10 | `b41484b` | Mig 24 + Service V2 wire `ApproveV2Async` + Synthetic Policy ForV2Schema |
|
||||
| 11 | `d814429` | UX disable button non-approver + banner "Đến lượt bạn" / "Không phải lượt" + DTO CurrentApproval |
|
||||
| 12 | `9e63e2d` `d250ae4` `74745a7` | List/Inbox V2-aware + 2 dropdown filter (chỉ ở Duyệt) |
|
||||
| 13 | `ac41d5e` | SQL clean prod + test user `nv.test@solutions.com.vn` |
|
||||
| — | `de0f38d` | Panel 3 flow render — bỏ phase cards, hiện Bước/Cấp với Status Done/Current/Pending |
|
||||
|
||||
## Service V2 wire — branch theo schema pin
|
||||
|
||||
```csharp
|
||||
// PurchaseEvaluationWorkflowService.TransitionAsync (Mig 24)
|
||||
if (evaluation.ApprovalWorkflowId is Guid awId)
|
||||
await ApproveV2Async(...)
|
||||
// Load AW.Steps + Levels Include 3-level
|
||||
// Group Levels by Order = Cấp (OR-of-N approvers cùng cấp)
|
||||
// Match actor.Id ∈ ApproverUserId
|
||||
// Advance: levelOrder++ trong Step → idx++ + reset levelOrder=1 → DaDuyet
|
||||
else
|
||||
await ApproveV1LegacyAsync(...) // giữ logic Mig 21
|
||||
```
|
||||
|
||||
## UX V2-aware
|
||||
|
||||
- Banner emerald "✓ Đến lượt bạn duyệt" / amber "⚠ Không phải lượt bạn — chỉ {NV X / Y} duyệt được"
|
||||
- Button Duyệt forward `disabled` khi V2 + actor không trong Cấp + tooltip
|
||||
- Trả lại + Từ chối vẫn enabled (BE không gating reject theo cấp)
|
||||
- Inbox `ResolveV2InboxIdsAsync` precompute IDs khớp actor.Id
|
||||
- 2 dropdown filter "Quy trình duyệt" + "Trạng thái" (chỉ ở Duyệt — Danh sách giữ 1 dropdown)
|
||||
- Panel 3 thay 4 phase cards bằng flow workflow thực tế:
|
||||
```
|
||||
✓ Bước 1 — Phòng CCM
|
||||
✓ Cấp 1 (đã duyệt) NV Test
|
||||
● Cấp 2 (đang chờ) Phan Văn Chương ← highlight brand
|
||||
○ Cấp 3 (chưa) Nguyễn Văn Trường
|
||||
```
|
||||
|
||||
## Test setup prod
|
||||
|
||||
- SQL `clean-transactional-uat.sql` xóa 9 PE + 11 HĐ + 19 Notif + reset CodeSequences. Giữ master (Users=8, Suppliers=19, Projects=9, Departments=9, V1+V2 Workflows). Run via SSH VPS `.\SQLEXPRESS`.
|
||||
- Test user `nv.test@solutions.com.vn` / `TestUser@123456` (Drafter, CCM, ID `881269c7-dbb5-4aad-a92c-08deace07898`) tạo qua API admin `POST /api/users`.
|
||||
|
||||
## Stats final Session 17
|
||||
|
||||
| | Trước | Sau |
|
||||
|---|---|---|
|
||||
| Migrations | 21 | **24** (+3) |
|
||||
| DB tables | 55 | **58** (+3) |
|
||||
| API endpoints | ~134 | **~140** (+5) |
|
||||
| FE pages | 32 | **33** (+1) |
|
||||
| Test pass | 77 | **81** (+4 TraLai entry point) |
|
||||
| Gotchas | 41 | **43** (+2 dual schema + Step.Order ≠ index) |
|
||||
|
||||
## Gotchas mới (2)
|
||||
|
||||
- **#42 Dual schema V1 vs V2 — Service phải branch theo pin field**: phiếu pin V2 mà Service đọc `WorkflowDefinitionId` → match approver schema cũ → Forbidden. Fix: branch `if (ApprovalWorkflowId is Guid)` → `ApproveV2Async`.
|
||||
- **#43 Step.Order ≠ index 0-based**: EF không thể query `Step.Order == idx + 1` thẳng. Cần precompute candidates EF + in-memory sort by Order + array index.
|
||||
|
||||
## Pending Session 18+
|
||||
|
||||
| Task | Estimate | Priority |
|
||||
|---|---|---|
|
||||
| Contract V2 wire (Mig 25) | 4-6h dedicated session | High (sau khi PE UAT confirm) |
|
||||
| Phân quyền strict V2 | 2h | Medium |
|
||||
| Drop legacy V1 + cleanup deprecated columns | Mig 26 cleanup, 2h | Low (chờ migrate hết phiếu cũ) |
|
||||
| Test Domain ApproveV2Async + match logic | 3h | Medium |
|
||||
| Budget V2 wire (Mig 27) | 4h dedicated | Low |
|
||||
| Admin role bypass option C audit log | 2h | Low |
|
||||
|
||||
## Memory entry — không tạo mới
|
||||
|
||||
Session 17 decisions ghi đầy đủ vào `memory/project_solution_erp.md`. Pattern dual schema reusable nhưng quá specific cho project — không tạo `feedback_dual_schema_pattern.md` riêng (anti-pattern §9.5 "viết skill chỉ để có thêm" áp dụng cho memory).
|
||||
|
||||
## Skill update
|
||||
|
||||
`contract-workflow` skill +section "Phase 9+ done (Mig 22-24 — Session 17) — V2 schema riêng + state machine 5 trạng thái" — mention V2 cross-ref + Service branch + match logic V2 vs V1 table + Designer constraints.
|
||||
|
||||
KHÔNG tạo skill `pe-workflow-v2` mới — overlap với `contract-workflow` existing skill (cùng pattern state machine + versioned WF, khác schema).
|
||||
Reference in New Issue
Block a user