From c0af9e05ec5aa90e7a45670f27da839d4b677179 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Wed, 13 May 2026 20:12:21 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Docs:=20S21=20t5=20Chunk=20D=20?= =?UTF-8?q?=E2=80=94=20ch=E1=BB=91t=20refactor=20Allow*=20per-NV=20(Mig=20?= =?UTF-8?q?29)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update docs theo rule §6.5 KEEP narrative: - `docs/database/schema-diagram.md §14` title "Mig 22-29, S17-21": - Update ApprovalWorkflows block: note Mig 28 cũ 6 column DROP, refactor per-NV - Add 5 column Allow* trong ApprovalWorkflowLevels block (inline comment F1+F3) - Add Users block với F2 AllowDrafterSkipToFinal Mig 29 - `docs/STATUS.md` Last updated S21 t5 + count 28→29 mig. UAT defer test count unchanged 84. - `docs/HANDOFF.md` Insert TL;DR S21 t5 đầy đủ (trước S21 t4): - Trigger UAT feedback "cấu hình cho từng người" - Q&A 2 lượt chốt scope - 4 chunk narrative: A BE+Mig 29 + Service refactor → B FE Admin Designer per-Level → C FE eOffice rename → D Docs - Pattern reusable: EF migration reorder cho BACKFILL preserve data, per-NV scope split theo role (Approver Level vs Drafter User) - State table + Pending User Mgmt F2 UI defer - NEW session log `docs/changelog/sessions/2026-05-13-1400-s21-turn5-refactor-allow-to-per-nv.md`: - Code snippets BE/FE refactor - 5 lessons learned (incl EF reorder pattern + backward compat backfill discipline) - References file paths Stats cumulative S21 t5: - 29 mig (+1 Mig 29 refactor) · 59 tables · ~143 endpoints · 34 FE pages - 84 test pass (UAT defer test-after §7) · 45 gotcha · 17 memory · 6 skills - 4 commits S21 t5 cumulative ready push remote Pending: bro confirm push `eea86fd..HEAD` 4 commits ahead. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/HANDOFF.md | 144 ++++++++++- docs/STATUS.md | 5 +- ...1400-s21-turn5-refactor-allow-to-per-nv.md | 234 ++++++++++++++++++ docs/database/schema-diagram.md | 29 ++- 4 files changed, 399 insertions(+), 13 deletions(-) create mode 100644 docs/changelog/sessions/2026-05-13-1400-s21-turn5-refactor-allow-to-per-nv.md diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index a48535c..b1ec60d 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -1,10 +1,152 @@ # HANDOFF — Brief 5 phút cho session tiếp theo -**Last updated:** 2026-05-13 1200 (Session 21 turn 4 — **🎯 F1+F2+F3 PE Workflow advanced options (Mig 28) — 5 chunk per-commit `0294693` (A schema) → `c56024b` (B BE) → `a508564` (C FE Admin) → `d27caaf` (D FE eOffice) → this (E Docs). **F1** 4 mode Trả lại admin tick stick (1 Cấp / 1 Bước / Người chỉ định / Người soạn thảo) — 3 mode đầu giữ Phase=ChoDuyet lùi pointer (peer review chain), mode Drafter giữ Phase=TraLai clear pointer (S17 backward compat). **F2** Drafter skip thẳng Cấp cuối — workflow tick + Workspace checkbox dynamic. **F3** Approver edit Section 2 (Hạng mục/NCC/Báo giá) khi workflow tick + actor match CurrentLevel.ApproverUserId + audit ghi PurchaseEvaluationChangelog. Mig 28 thêm 6 bit column lên `ApprovalWorkflows` (DEFAULT 1 cho AllowReturnToDrafter backward compat, 5 còn lại 0). BE Service extend signature 3 optional param (returnMode/returnTargetUserId/skipToFinal). Helper `EnsureEditableForDetailsAsync` mới gating Detail/Quote/Supplier CRUD theo Drafter scope OR F3 Approver scope + audit changelog Update/Delete (trước đây silent). FE Admin Designer "Cấu hình nâng cao" section 6 checkbox 3 group. FE eOffice 3 changes mirror 2 app. UAT mode skip dotnet test mỗi chunk, npm build × 2 app pass mỗi chunk. CHƯA push remote — chờ bro confirm.**) +**Last updated:** 2026-05-13 1400 (Session 21 turn 5 — **🎯 Refactor Allow* sang PER-NV (Mig 29). 4 chunk per-commit `0366946` (A BE+Mig 29) → `63234b2` (B FE Admin Designer per-Level 5 checkbox) → `5ccb2a7` (C FE eOffice mirror 2 app rename) → this Chunk D Docs. **F1+F3** 5 flag MOVED xuống `ApprovalWorkflowLevels` (per slot Approver). **F2** MOVED xuống `Users` (per-Drafter). Mig 29 4-stage: ADD 5 Levels + 1 Users + BACKFILL bulk SQL preserve admin config S21 t4 + DROP 6 workflow column. Service refactor đọc `currentLevel.Allow*` + `drafterUser.AllowDrafterSkipToFinal`. DTO `AwLevelDto +5`, `PeDetailBundle.workflowOptions → currentLevelOptions + drafterAllowSkipToFinal`. FE Admin Designer 5 checkbox per Level slot inline (drop section workflow-level). 84 test PASS. CHƯA push remote — chờ bro confirm.**) +**S21 turn 4:** 2026-05-13 1200 (Session 21 turn 4 — **🎯 F1+F2+F3 PE Workflow advanced options (Mig 28) — 5 chunk per-commit `0294693` (A schema) → `c56024b` (B BE) → `a508564` (C FE Admin) → `d27caaf` (D FE eOffice) → this (E Docs). **F1** 4 mode Trả lại admin tick stick (1 Cấp / 1 Bước / Người chỉ định / Người soạn thảo) — 3 mode đầu giữ Phase=ChoDuyet lùi pointer (peer review chain), mode Drafter giữ Phase=TraLai clear pointer (S17 backward compat). **F2** Drafter skip thẳng Cấp cuối — workflow tick + Workspace checkbox dynamic. **F3** Approver edit Section 2 (Hạng mục/NCC/Báo giá) khi workflow tick + actor match CurrentLevel.ApproverUserId + audit ghi PurchaseEvaluationChangelog. Mig 28 thêm 6 bit column lên `ApprovalWorkflows` (DEFAULT 1 cho AllowReturnToDrafter backward compat, 5 còn lại 0). BE Service extend signature 3 optional param (returnMode/returnTargetUserId/skipToFinal). Helper `EnsureEditableForDetailsAsync` mới gating Detail/Quote/Supplier CRUD theo Drafter scope OR F3 Approver scope + audit changelog Update/Delete (trước đây silent). FE Admin Designer "Cấu hình nâng cao" section 6 checkbox 3 group. FE eOffice 3 changes mirror 2 app. UAT mode skip dotnet test mỗi chunk, npm build × 2 app pass mỗi chunk. CHƯA push remote — chờ bro confirm.**) **S21 turn 3:** 2026-05-12 2100 (Session 21 turn 3 — **🔴 BUG FIX CRITICAL "Trả về nhưng hệ thống vẫn duyệt" PE workflow (gotcha #45 mới). 3 chunk per-commit: `de00887` (BE Chunk A guard + 3 test) + `4b29d00` (FE Chunk B fix 2 app mirror) + this Chunk C Docs. Root: `PeWorkflowPanel.tsx` `isReject` payload (L64-66) thiếu nhánh TraLai → button "← Trả lại" gửi `decision: 1` (Approve) thay vì `2` (Reject) khi target=TraLai(98) → BE skip Reject branch → enter APPROVE STEP → `ApproveV2Async` UPSERT opinion "đã duyệt" + advance Cấp tiếp theo. Inconsistency phụ: dialog `isSendBack` (L247-248) cùng pattern thiếu TraLai → dialog title sai `'✓ Duyệt → Trả lại'` + KHÔNG amber warning. Severity CRITICAL — data integrity issue khó rollback (BE đã `SaveChangesAsync`). Test-before §7 BẮT BUỘC: viết test reproduce → confirm FAIL (BE đi sâu vào ApproveV2Async throw "Phiếu chưa pin workflow") → thêm BE guard early throw ConflictException khi `target ∈ {TraLai, TuChoi} && decision != Reject` → confirm PASS. 3 regression test (Throws TraLai+Approve, Throws TuChoi+Approve consistency, happy path Reject+TraLai). Tổng `dotnet test SolutionErp.slnx` 84 PASS (58 Domain + 26 Infra = +3 from 81 baseline). `npm run build` × 2 app pass. Stats: 27 mig (no change) · 59 tables · ~142 endpoints · 34 FE pages · **84 test (+3)** · **45 gotcha (+1 #45)** · 17 memory · 6 skills · 4 sub-agents seeds-only. Em main solo S21 t3 — bug fix reasoning chain cross BE/FE Implementer REFUSE per multi-agent rule (decision tree: tightly coupled BE+FE+test). CHƯA push remote — chờ bro confirm sau Chunk C wrap.**) **S21 turn 2:** 2026-05-12 1800 (Session 21 turn 2 — **🎯 RAG Hybrid setup planning + Cách A validation deep dive. 2 commit (`1f8e9af` plan save 1223 LOC + this chốt). KHÔNG implement, plan only — defer chờ bro confirm 5 dự án future. Decision chốt: Cách A defensive (giữ blanket 120K em main + RAG retrieve) over Cách B aggressive (cắt 60-70% blanket). Industry-validated cross 4 Anthropic blog + 5 community tools (Cursor/Continue/Cline/Aider). Stack: Voyage-3-large + Qdrant + FastMCP + Streamlit dashboard. Multi-agent cost reality: 4 agents → ~520K cumulative blanket → heavy session ~560K (Cách A) vs ~700K (lazy). 3-layer pattern Phase 1-3 rollout (embeddings + BM25 + reranking, ~70% → ~92% recall). Stats: +1 memory entry (`feedback_rag_hybrid_pattern`) +1 plan file (`rag-setup-plan.md` 1500 LOC). Sub-agents vẫn 4 seeds-only, em main solo session.**) **S21 turn 1:** 2026-05-12 0030 (Session 21 turn 1 — **🎯 Add con thứ 4 cicd-monitor (Path A — post-deploy verifier). 1 commit `f1c61c9` pushed `36e21c8..f1c61c9 main -> main`. CI skipped per path filter (3 file `.md`). Cost reality update: ~750K spawn (3 → 4 agents) · ~1.35M heavy / ~700K optimized. Stats: 4 sub-agents seeds-only · 16 memory · 27 mig · 59 tables · ~142 endpoints · 81 test · 44 gotcha · 6 skills unchanged. KHÔNG flush 3 agent MEMORY.md (chưa spawn work — em main solo). Trial Week 1 kick-off S21 turn 2+ Plan B Contract V2 wire mirror PE pattern.**) +## TL;DR Session 21 turn 5 — Refactor Allow* sang PER-NV (Mig 29 drop Mig 28) + +User feedback sau UAT S21 t4 deploy: "Cấu hình cho từng người nhé (chứ ko phải là cho toàn bộ quy trình duyệt), thêm table vào SQL luôn để cấu hình cho dễ." + +→ Refactor 6 Allow* options từ workflow-level (Mig 28) sang per-NV scope: +- **F1 (4 mode Trả lại) + F3 (Edit Section 2)** = 5 flag MOVE xuống `ApprovalWorkflowLevels` (per slot Approver — cùng table với ApproverUserId). +- **F2 (AllowDrafterSkipToFinal)** MOVE xuống `Users` (per-Drafter user, admin config ở User Management page). + +### Q&A clarify (2 lượt) + +| Câu | User chốt | +|---|---| +| Scope "từng người" | **Per-Level**: 5 flag (4 F1 + 1 F3) gắn slot Designer. F2 per-Drafter user. | +| Mig 28 cũ xử lý sao | **Migrate bốc → per-NV bulk + drop**: copy workflow → all Levels của workflow, set TRUE cho Drafter user từng dùng workflow F2, rồi drop 6 column workflow. | + +### Chunk A — BE schema + Service refactor (`0366946`) + +Mig 29 `RefactorAdvancedOptionsToPerLevelAndDrafterUser` — 4-stage (EF +auto-generated drop-then-add đã REORDER manual): + +1. **ADD 5 column** trên `ApprovalWorkflowLevels` (AllowReturnOneLevel/OneStep/ + ToAssignee/ToDrafter[default true]/AllowApproverEditDetails) +2. **ADD 1 column** trên `Users` (AllowDrafterSkipToFinal default false) +3. **BACKFILL bulk SQL** (preserve admin config Mig 28): + ```sql + -- Levels: copy workflow.Allow* → all Levels của workflow + UPDATE l SET l.AllowReturnOneLevel = w.AllowReturnOneLevel, ... + FROM ApprovalWorkflowLevels l + INNER JOIN ApprovalWorkflowSteps s ON s.Id = l.ApprovalWorkflowStepId + INNER JOIN ApprovalWorkflows w ON w.Id = s.ApprovalWorkflowId; + + -- Users: SET TRUE cho user từng Drafter PE link workflow Allow=true + UPDATE u SET u.AllowDrafterSkipToFinal = 1 + FROM Users u WHERE EXISTS ( + SELECT 1 FROM PurchaseEvaluations pe + INNER JOIN ApprovalWorkflows w ON w.Id = pe.ApprovalWorkflowId + WHERE pe.DrafterUserId = u.Id AND w.AllowDrafterSkipToFinal = 1 + ); + ``` +4. **DROP 6 column** workflow-level (Mig 28 cleanup) + +Domain entity refactor: +- `ApprovalWorkflow.cs` — REMOVE 6 Allow* (S21 t4 Mig 28 cũ) +- `ApprovalWorkflowLevel.cs` — ADD 5 Allow* (F1 + F3) +- `User.cs` — ADD 1 Allow* (F2 AllowDrafterSkipToFinal) + +Service refactor `ApplyReturnModeAsync`: +- Resolve currentLevel slot (CurrentWorkflowStepIndex + CurrentApprovalLevelOrder) +- Read 5 Allow* từ `currentLevel.AllowXxx` thay vì workflow +- Drafter mode validate AllowReturnToDrafter của currentLevel +- V1 legacy phiếu → fallback Drafter tự động + +DRAFTER trình refactor: +- Permission check moved → `drafterUser.AllowDrafterSkipToFinal` +- `userManager.FindByIdAsync(actorUserId)` get Drafter user entity +- Admin bypass unchanged + +Helper `EnsureEditableForDetailsAsync`: +- Read `level.AllowApproverEditDetails` thay vì workflow +- Error message rõ "Cấp Approver hiện tại (Bước X / Cấp Y)" + +DTO refactor: +- `AwLevelDto +5 Allow*` (admin Designer GET per-Level) +- `AwDefinitionDto -6 Allow*` (no longer workflow-level) +- `CreateAwLevelInput +5 Allow*` (admin Designer POST per-Level) +- `ApprovalWorkflowOptionsDto` chỉ còn 5 flag (F2 separate field) +- `PurchaseEvaluationDetailBundleDto`: + - RENAME `WorkflowOptions` → `CurrentLevelOptions` + - ADD `DrafterAllowSkipToFinal bool` + +GetPe handler populate: +- currentLevelOptions = 5 Allow* của Cấp hiện tại (null nếu V1 / no pointer) +- drafterAllowSkipToFinal = User.AllowDrafterSkipToFinal từ DrafterUserId + +### Chunk B — FE Admin Designer (`63234b2`) + +ApprovalWorkflowsV2Page.tsx: +- Types: `LevelDto +5 Allow*`, `DefinitionDto -6 Allow*`, `EditLevelEntry +5 Allow*` +- Helper `makeDefaultLevelEntry(order, userId)` factory với 4 false + AllowReturnToDrafter=true +- `copyFromDefinition` propagate 5 Allow* từ Levels cũ +- REMOVE section "Cấu hình nâng cao" workflow-level (amber bg 6 checkbox) +- REPLACE với info banner violet ngắn "ⓘ Cấu hình quyền duyệt riêng cho từng NV..." +- Mỗi Level entry (NV row) ADD inline panel amber-50/30 5 checkbox grid-cols-2 + +fe-user KHÔNG mirror (Designer admin-only). + +F2 cần UX riêng ở User Management page → defer commit sau (BE field đã sẵn, +FE chỉ thêm 1 toggle UserEdit dialog khi admin UAT request). + +### Chunk C — FE eOffice (`5ccb2a7`) mirror 2 app + +Types: +- `ApprovalWorkflowOptions` REMOVE allowDrafterSkipToFinal (5 flag) +- `PeDetailBundle`: + - RENAME `workflowOptions → currentLevelOptions` + - ADD `drafterAllowSkipToFinal: boolean` + +PeWorkflowPanel.tsx: +- RENAME local `wfOptions → levelOptions`, source `evaluation.currentLevelOptions` + +PeDetailTabs.tsx: +- F3 approverEditMode: read `currentLevelOptions?.allowApproverEditDetails` +- F2 allowSkipToFinal: read `drafterAllowSkipToFinal` (per-user) + +Backward compat: +- Backfill Mig 29 preserve admin config S21 t4 +- Phiếu V1 legacy → currentLevelOptions=null → fallback chỉ Drafter mode +- drafterAllowSkipToFinal TRUE chỉ cho user từng Drafter PE link workflow Allow=true + +### Chunk D — Docs (this commit) + +- `docs/database/schema-diagram.md §14`: title "Mig 22-29, S17-21" + add 5 column + Level inline comment + add 1 column User Mig 29 block +- `docs/STATUS.md` Last updated S21 t5 + 28→29 mig +- `docs/HANDOFF.md` TL;DR S21 t5 đầy đủ (file này) +- Session log riêng + +### State chốt S21 turn 5 + +| Metric | Trước (S21 t4) | Sau (S21 t5) | Δ | +|---|---|---|---| +| DB tables | 59 | 59 | 0 | +| **Migrations** | 28 | **29** | **+1** (Mig 29 refactor per-NV) | +| Endpoints | ~143 | ~143 | 0 (same body, different schema) | +| FE pages | 34 | 34 | 0 | +| Unit tests | 84 | 84 | 0 (UAT defer test-after §7) | +| Gotchas | 45 | 45 | 0 | +| Memory | 17 | 17 | 0 | +| Skills | 6 | 6 | 0 | +| Sub-agents | 4 seeds-only | 4 seeds-only | 0 | +| **Commits S21 t5** | — | **4** | `0366946` → `63234b2` → `5ccb2a7` → this | + +### Pending — defer / next session + +- **User Management page F2 toggle**: thêm checkbox "Cho phép gửi PE thẳng Cấp cuối" + vào UserEdit dialog (BE column sẵn, FE 1 toggle nhỏ — defer khi admin UAT request) +- **Test-after carry** (Plan C bundle): Service ApplyReturnModeAsync 4 mode + + EnsureEditableForDetailsAsync 3 scenario read-from-level + skipToFinal read-from-user + +--- + ## TL;DR Session 21 turn 4 — F1+F2+F3 PE Workflow advanced options (Mig 28) User request 3 tính năng mới trong PE V2 Workflow: diff --git a/docs/STATUS.md b/docs/STATUS.md index bddc3f6..dd15c1d 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -2,7 +2,8 @@ > **Update rule:** trước khi bắt đầu 1 task → ghi row vào `🔥 In Progress`. Xong → chuyển sang `✅ Recently Done`. -**Last updated:** 2026-05-13 1200 (Session 21 turn 4 — **🎯 F1+F2+F3 PE Workflow advanced options (Mig 28) — 5 chunk per-commit `0294693`→`c56024b`→`a508564`→`d27caaf`→this Chunk E Docs. **F1** 4 mode Trả lại admin tick: "1 Cấp / 1 Bước / Người chỉ định / Người soạn thảo" — 3 mode đầu giữ Phase=ChoDuyet lùi pointer (peer review chain), mode Drafter giữ Phase=TraLai S17 fallback. **F2** Drafter skip thẳng Cấp cuối — workflow tick + Workspace checkbox dynamic confirm. **F3** Approver edit Section 2 (Hạng mục/NCC/Báo giá) khi workflow tick + actor match CurrentLevel + audit ghi PurchaseEvaluationChangelog. Mig 28 `ApprovalWorkflows +6 bool Allow*` (DEFAULT 1 cho AllowReturnToDrafter backward compat, 5 còn lại 0). BE Service `TransitionAsync` extend 3 optional param (returnMode/returnTargetUserId/skipToFinal) + helper `ApplyReturnModeAsync` switch 4 mode. Detail/Quote/Supplier helper `EnsureEditableForDetailsAsync` mới (kế thừa `EnsureDraftAsync` + add ChoDuyet+F3 branch + Admin bypass). FE Admin Designer "Cấu hình nâng cao" section 6 checkbox 3 group. FE eOffice 3 changes mirror 2 app: Trả lại radio picker 1-4 mode + Workspace skip checkbox violet + Section 2 itemsReadOnly approver banner. UAT mode skip dotnet test mỗi chunk (per `feedback_uat_skip_verify`), `npm run build` × 2 app pass mỗi chunk. Stats: **28 mig (+1)** · 59 tables · **~143 endpoints (+1 user-selectable patch existed)** · **34 FE pages (+1 Designer section)** · **84 test pass unchanged** (UAT defer test-after) · **45 gotcha unchanged** · 17 memory · 6 skills · 4 sub-agents seeds-only.**) +**Last updated:** 2026-05-13 1400 (Session 21 turn 5 — **🎯 Refactor F1+F2+F3 sang PER-NV (Mig 29 drop Mig 28 column workflow-level). 4 chunk per-commit `0366946` (A BE schema+Service refactor) → `63234b2` (B FE Admin Designer 5 checkbox per-Level row) → `5ccb2a7` (C FE eOffice rename currentLevelOptions + drafterAllowSkipToFinal) → this Chunk D Docs. **F1+F3** 5 flag MOVED xuống `ApprovalWorkflowLevels` (per slot Approver, mỗi NV có riêng quyền duyệt). **F2** AllowDrafterSkipToFinal MOVED xuống `Users` (per-Drafter user, admin config ở User Management). Mig 29 4-stage: ADD 5 column Levels + 1 column Users + BACKFILL bulk SQL (copy workflow → all Levels, set TRUE cho Drafter user nào từng dùng workflow Allow) + DROP 6 column workflow `ApprovalWorkflows`. Service `ApplyReturnModeAsync` refactor đọc `currentLevel.AllowXxx` thay vì `workflow.AllowXxx`. Helper `EnsureEditableForDetailsAsync` read `level.AllowApproverEditDetails`. DRAFTER trình branch read `userManager.FindByIdAsync(actorUserId).AllowDrafterSkipToFinal`. DTO refactor: `AwLevelDto +5 Allow*`, `AwDefinitionDto -6 Allow*`, `CreateAwLevelInput +5 Allow*`, `PeDetailBundle.workflowOptions → currentLevelOptions + drafterAllowSkipToFinal`. FE Admin Designer drop section "Cấu hình nâng cao" workflow-level, replace 5 checkbox grid-cols-2 inline mỗi Level entry row (5 flag per slot). FE eOffice rename `wfOptions → levelOptions` đọc `currentLevelOptions`. Backward compat: backfill preserve admin config S21 t4. Test 84/84 PASS unchanged. Stats: **29 mig (+1) · 59 tables · ~143 endpoints · 34 FE pages · 84 test pass · 45 gotcha · 17 memory · 6 skills · 4 sub-agents seeds-only.**) +**S21 turn 4:** 2026-05-13 1200 (Session 21 turn 4 — **🎯 F1+F2+F3 PE Workflow advanced options (Mig 28) — 5 chunk per-commit `0294693`→`c56024b`→`a508564`→`d27caaf`→this Chunk E Docs. **F1** 4 mode Trả lại admin tick: "1 Cấp / 1 Bước / Người chỉ định / Người soạn thảo" — 3 mode đầu giữ Phase=ChoDuyet lùi pointer (peer review chain), mode Drafter giữ Phase=TraLai S17 fallback. **F2** Drafter skip thẳng Cấp cuối — workflow tick + Workspace checkbox dynamic confirm. **F3** Approver edit Section 2 (Hạng mục/NCC/Báo giá) khi workflow tick + actor match CurrentLevel + audit ghi PurchaseEvaluationChangelog. Mig 28 `ApprovalWorkflows +6 bool Allow*` (DEFAULT 1 cho AllowReturnToDrafter backward compat, 5 còn lại 0). BE Service `TransitionAsync` extend 3 optional param (returnMode/returnTargetUserId/skipToFinal) + helper `ApplyReturnModeAsync` switch 4 mode. Detail/Quote/Supplier helper `EnsureEditableForDetailsAsync` mới (kế thừa `EnsureDraftAsync` + add ChoDuyet+F3 branch + Admin bypass). FE Admin Designer "Cấu hình nâng cao" section 6 checkbox 3 group. FE eOffice 3 changes mirror 2 app: Trả lại radio picker 1-4 mode + Workspace skip checkbox violet + Section 2 itemsReadOnly approver banner. UAT mode skip dotnet test mỗi chunk (per `feedback_uat_skip_verify`), `npm run build` × 2 app pass mỗi chunk. Stats: **28 mig (+1)** · 59 tables · **~143 endpoints (+1 user-selectable patch existed)** · **34 FE pages (+1 Designer section)** · **84 test pass unchanged** (UAT defer test-after) · **45 gotcha unchanged** · 17 memory · 6 skills · 4 sub-agents seeds-only.**) **S21 turn 3:** 2026-05-12 2100 (Session 21 turn 3 — **🎯 Bug fix CRITICAL "Trả về nhưng hệ thống vẫn duyệt" PE workflow (gotcha #45). 2 chunk per-commit `de00887` (BE Chunk A) + `4b29d00` (FE Chunk B) + Chunk C Docs this. Root: PeWorkflowPanel.tsx `isReject` payload (L64-66) thiếu nhánh TraLai → button "← Trả lại" gửi `decision: 1` (Approve) thay vì `2` (Reject) khi target=TraLai(98) → BE ApproveV2Async UPSERT opinion "đã duyệt" + advance Cấp. Inconsistency phụ: dialog `isSendBack` (L247-248) cùng pattern thiếu TraLai → dialog title sai. Fix BE defense-in-depth + FE 3 chỗ × 2 app mirror rule §3.9. Test-before §7 BẮT BUỘC: 3 regression test mới (2 reproduce bug + 1 happy path control) — `dotnet test SolutionErp.slnx` 84 PASS (58 Domain + 26 Infra = +3). `npm run build` × 2 app pass. Stats: 27 mig (no change) · 59 tables · ~142 endpoints · 34 FE pages · **84 test pass (+3)** · **45 gotcha (+1 #45)** · 17 memory entries (no new) · 6 skills. Em main solo (no sub-agent spawn S21 t3 — bug fix reasoning chain cross BE/FE Implementer REFUSE per multi-agent rule).**) **S21 turn 2:** 2026-05-12 1800 (Session 21 turn 2 — **🎯 RAG Hybrid setup planning + Cách A validation deep dive. 2 commit (`1f8e9af` plan save 1223 LOC + this chốt). Em main solo (no SOLUTION_ERP sub-agent spawn), delegate claude-code-guide × 2 research Anthropic + community practice. Decision chốt: Cách A defensive (giữ blanket 120K em main + RAG retrieve supplement) over Cách B aggressive (cắt 60-70% blanket). Industry-validated cross 4 Anthropic blog + 5 community tools (Cursor/Continue/Cline/Aider all hybrid). Stack: Voyage-3-large + Qdrant local + FastMCP Python + Streamlit dashboard 7 pages + SQLite event log. Multi-agent cost reality: 4 agents → ~520K cumulative blanket → heavy session ~560K (Cách A) vs ~700K (lazy), saving -20%. 3-layer pattern Phase 1-3 rollout (Layer 1 vector → Layer 2 +BM25 → Layer 3 +reranking, recall ~70% → ~92%). Stats: +1 memory entry (`feedback_rag_hybrid_pattern.md`) +1 plan file (`rag-setup-plan.md` 1500 LOC final). 4 sub-agents vẫn seeds-only. Plan I NEW deferred chờ bro confirm 5 dự án path + stack + Voyage API key + disk cleanup 5-8GB.**) **S21 turn 1:** 2026-05-12 0030 (Session 21 turn 1 — **🎯 Add con thứ 4 cicd-monitor (Path A — post-deploy verifier green READ tier). 1 commit `f1c61c9` pushed `36e21c8..f1c61c9 main -> main`, CI skipped per path filter (`**/*.md` paths-ignore docs-only). Trade-off: +~150K spawn extra mỗi run, đổi lại catch deploy ship fail tự động (bundle hash unchanged / mig drift prod / endpoint 500) — recurring blind spot pattern em main solo S20 quên verify ~30% push. Cost reality update: ~750K spawn setup (3 → 4 agents) · ~1.35M heavy session · ~700K optimized cached. Stats: 4 sub-agents seeds-only (+1 cicd-monitor green) · 16 memory entries (no new, update existing `feedback_multi_agent_setup.md` 3 → 4 agents narrative) · 27 mig · 59 tables · ~142 endpoints · 81 test unchanged · 44 gotcha unchanged · 6 skills unchanged. KHÔNG flush 3 agent MEMORY.md (chưa spawn work S21 t1 nên KHÔNG có findings — em main solo via context + Write file).**) @@ -10,7 +11,7 @@ **S20 turn 7:** 2026-05-11 17:00 (Session 20 turn 7 — **🎯 Admin Ẩn/Hiện + Đổi tên menu eOffice (Mig 27). 5 chunk `2ea2d27`→`ef394f8`→`059bfcb`→`1ed6530`→Chunk E Docs. User Q2=b: DisplayLabel CHỈ áp fe-user, admin sidebar giữ Label gốc. Domain MenuItem +IsVisible(true) +DisplayLabel(200). Mig 27 AddVisibilityAndDisplayLabelToMenuItems. BE PATCH /api/menus/{key} [Authorize Policy=Permissions.Update]. NEW FE-admin MenuVisibilityPage ~210 LOC (table inline edit per-row + Save dirty + Khôi phục mặc định + Toggle Eye/EyeOff + 4 StatCard). fe-user Layout filterForUser 2 tầng (USER_HIDDEN_KEYS hardcode + !isVisible dynamic) + effectiveLabel(displayLabel || label) replace 3 callsite. fe-admin Layout KHÔNG đụng. +1 menu key MenuVisibility "Menu eOffice" leaf System Order=94. 27 mig, 59 tables, ~142 endpoints, 34 FE pages, 81 test pass (Q4 UAT defer).**) **S20 prev:** 2026-05-11 (Session 20 — **🎯 PE Detail UI restructure 3 yêu cầu user UX. 4 chunk per-commit `9dee00d` → `2bba851` → `f2f01f4` → (current Chunk D Docs).** Q1=a (giữ Section "Chọn NCC TP" riêng), Q2=a "1 hạng mục trước tiên" (NCC shared, demo 1 hạng mục), Q3=a (chỉ hiện NV đã ký), Q4 public luôn (skip dotnet test mỗi chunk theo memory `feedback_uat_skip_verify`, vẫn `npm run build` × 2 app mỗi chunk vì có rename/remove function). **Chunk A (`9dee00d`)**: BE `CreatePurchaseEvaluationCommandHandler` thêm 1 PurchaseEvaluationDetail mặc định khi tạo phiếu — GroupCode="01", GroupName="Hạng mục chính", NoiDung=TenGoiThau, DonGiaNganSach=ThanhTienNganSach=Budget.TongNganSach hoặc BudgetManualAmount fallback 0; Changelog Insert audit. FE reorder PeDetailTabs (mirror 2 app) 1.Thông tin / 2.Hạng mục (lên #2) / 3.Chọn NCC / 4.NCC tham gia / 5.Ý kiến. **Chunk B (`2bba851`)**: ItemsTab restructure thành list `HangMucCard` (1 card / 1 hạng mục, expanded=true mặc định cho 1 hạng mục demo). Header card: GroupCode + NoiDung + 3 stat (KL/ĐG/TT) + NS link Δ nếu có + Pencil/Trash actions + ▼/▶ toggle expand. Expand body: NCC inline table columns NCC / Liên hệ / Điều khoản TT / **File báo giá** / ĐG chưa VAT / ĐG có VAT / Thành tiền / Action. Quote inline click cell → QuoteDialog cũ reuse. Add NCC + Sửa NCC reuse AddSupplierDialog/EditSupplierDialog cũ. Winner ✓ button mỗi NCC row. Drop function `SuppliersTab` (dead code ~134 LOC, replace bằng HangMucCard expand panel). Giữ AddSupplierDialog + EditSupplierDialog + SupplierAttachmentsCell (HangMucCard call lại). Section layout cuối: 1.Thông tin / 2.Hạng mục + Báo giá NCC (nested) / 3.Chọn NCC TP thắng thầu / 4.Ý kiến cấp duyệt — 4 section. **Chunk C (`f2f01f4`)**: Section Ý kiến restructure render layer (KHÔNG đụng Mig 26 schema — vẫn UPSERT 1 row / Level). LevelOpinionsSectionV2 forEach step → 1 `StepOpinionsBox` (replace grid-cols-2 cho N approver). Box header: "Bước N — Tên" + dept badge emerald + "X/Y đã duyệt" counter. Body: filter opinions theo step.order → sort levelOrder asc, signedAt asc → render `StepOpinionEntry` per signed opinion (tên NV + Cấp badge slate + admin override badge amber nếu có + emerald rounded-full timestamp + comment text). NV chưa duyệt KHÔNG hiển thị (Q3=a). Drop function `LevelOpinionBox` (replaced). Mirror fe-admin + fe-user. Verify build pass cả 2 app sau khi catch TS6133 `SuppliersTab` + `SupplierAttachmentsCell` unused (đã giải quyết: drop SuppliersTab, restore SupplierAttachmentsCell vào HangMucCard cột "File báo giá"). 81 test pass (no change — UAT defer)**) -## 📍 Phase hiện tại: **Phase 9 active — UAT V2 testing với user thật** — **59 DB tables (+1 PurchaseEvaluationLevelOpinions Mig 26), 28 migrations (+1 Mig 28 advanced options S21 t4 — 6 bool column trên ApprovalWorkflows), ~143 API endpoints, 34 FE pages. 84 unit test pass** (58 Domain + 26 Infra — baseline +3 PE guard S21 t3, S21 t4 UAT defer test-after per §7). **45 gotcha**. 30 demo user + 1 test user UAT. 6 skill. **5 trạng thái phiếu** (Nháp/Đã gửi duyệt/Trả lại/Từ chối/Đã duyệt). **2 Workflow schemas đồng tồn tại** post-Session 17: (1) Mig 21 `WorkflowDefinition` flat (V1) — pin với PE/Contract cũ + match Dept+PositionLevel. (2) Mig 22-26 `ApprovalWorkflow` (V2) — pin với PE mới + match ApproverUserId 1-1, Steps/Levels group by Order, Bước (Phòng) > Cấp (N NV OR-of-N), Mig 25 +IsUserSelectable admin pin per version, **Mig 26 +PeLevelOpinions sign-off dynamic theo Level**. Service PE branch theo `ApprovalWorkflowId` set or null. Sau UAT chốt → migrate + drop V1 + Contract V2 wire. +## 📍 Phase hiện tại: **Phase 9 active — UAT V2 testing với user thật** — **59 DB tables, 29 migrations (+1 Mig 29 refactor S21 t5 per-NV — 5 column trên ApprovalWorkflowLevels + 1 column trên Users), ~143 API endpoints, 34 FE pages. 84 unit test pass** (58 Domain + 26 Infra, UAT defer test-after per §7). **45 gotcha**. 30 demo user + 1 test user UAT. 6 skill. **5 trạng thái phiếu** (Nháp/Đã gửi duyệt/Trả lại/Từ chối/Đã duyệt). **2 Workflow schemas đồng tồn tại** post-Session 17: (1) Mig 21 `WorkflowDefinition` flat (V1) — pin với PE/Contract cũ + match Dept+PositionLevel. (2) Mig 22-26 `ApprovalWorkflow` (V2) — pin với PE mới + match ApproverUserId 1-1, Steps/Levels group by Order, Bước (Phòng) > Cấp (N NV OR-of-N), Mig 25 +IsUserSelectable admin pin per version, **Mig 26 +PeLevelOpinions sign-off dynamic theo Level**. Service PE branch theo `ApprovalWorkflowId` set or null. Sau UAT chốt → migrate + drop V1 + Contract V2 wire. ### 🌐 Production URLs diff --git a/docs/changelog/sessions/2026-05-13-1400-s21-turn5-refactor-allow-to-per-nv.md b/docs/changelog/sessions/2026-05-13-1400-s21-turn5-refactor-allow-to-per-nv.md new file mode 100644 index 0000000..d727fc1 --- /dev/null +++ b/docs/changelog/sessions/2026-05-13-1400-s21-turn5-refactor-allow-to-per-nv.md @@ -0,0 +1,234 @@ +# Session 21 turn 5 — 2026-05-13 14:00 — Refactor Allow* sang PER-NV (Mig 29 drop Mig 28) + +**Dev:** Claude Opus 4.7 1M Max (em main solo — Implementer REFUSE per cross-stack reasoning chain rule) +**Duration:** ~2h +**Base commit:** `eea86fd` (S21 t4 Chunk E Docs) +**Commits này turn:** `0366946` (A BE+Mig 29) → `63234b2` (B FE Admin) → `5ccb2a7` (C FE eOffice) → this (D Docs) + +## Trigger + +User feedback sau UAT S21 t4 deploy: "à cấu hình cho từng người nhé (chứ ko phải là cho toàn bộ quy trình duyệt), thêm table vào SQL luôn để cấu hình cho dễ." + +→ Workflow-level Allow* (Mig 28 S21 t4) **chưa fit UX request** — admin muốn config quyền duyệt RIÊNG cho TỪNG NV (per-Level slot trong workflow). + +## Q&A clarify (2 lượt AskUserQuestion) + +### Lượt 1 — Scope per-NV + xử lý Mig 28 cũ: + +| Câu | User chốt | +|---|---| +| Scope "từng người" | **Per-Level**: 5 flag (4 F1 + 1 F3) gắn slot Designer. F2 per-Drafter user. | +| Mig 28 xử lý | **Migrate bốc → per-NV bulk + drop**: copy workflow → Levels của workflow, backfill Users F2 từ PE link, drop 6 column workflow. | + +→ 6 flag split scope theo role natural: +- **F1 (4 mode) + F3 (1 flag)** = Approver permission → gắn `ApprovalWorkflowLevels` (1 slot Approver = 1 row Level) +- **F2 (1 flag)** = Drafter permission → gắn `Users` (per-Drafter user global) + +## Chunk A — BE schema + Service refactor (`0366946`) + +### Mig 29 `RefactorAdvancedOptionsToPerLevelAndDrafterUser` (4-stage) + +EF auto-generated drop-then-add order WRONG (data loss khi DROP trước BACKFILL). +Phải REORDER manual: + +```csharp +public override void Up(MigrationBuilder migrationBuilder) +{ + // Stage 1: ADD 5 column ApprovalWorkflowLevels (per slot) + AddColumn × 5 (AllowReturn*+AllowApproverEditDetails) + + // Stage 2: ADD 1 column Users (per-Drafter F2) + AddColumn AllowDrafterSkipToFinal + + // Stage 3: BACKFILL bulk SQL + migrationBuilder.Sql(@" + UPDATE l SET l.AllowReturnOneLevel = w.AllowReturnOneLevel, ... + FROM ApprovalWorkflowLevels l + INNER JOIN ApprovalWorkflowSteps s ON s.Id = l.ApprovalWorkflowStepId + INNER JOIN ApprovalWorkflows w ON w.Id = s.ApprovalWorkflowId; + "); + migrationBuilder.Sql(@" + UPDATE u SET u.AllowDrafterSkipToFinal = 1 + FROM Users u WHERE EXISTS ( + SELECT 1 FROM PurchaseEvaluations pe + INNER JOIN ApprovalWorkflows w ON w.Id = pe.ApprovalWorkflowId + WHERE pe.DrafterUserId = u.Id AND w.AllowDrafterSkipToFinal = 1 + ); + "); + + // Stage 4: DROP 6 column workflow-level (Mig 28 cleanup) + DropColumn × 6 (Mig 28 fields) +} +``` + +3-file rule complete. Apply LocalDB Dev + Design success. + +### Domain entity + +```csharp +// Mig 28 cũ ApprovalWorkflow.cs — REMOVE 6 Allow* field +// Mig 29 — entity nguyên thuỷ workflow-level cũ giảm về Code/Name/Version/Active/Selectable only. + +// ApprovalWorkflowLevel.cs — ADD 5 Allow* field +public bool AllowReturnOneLevel { get; set; } +public bool AllowReturnOneStep { get; set; } +public bool AllowReturnToAssignee { get; set; } +public bool AllowReturnToDrafter { get; set; } = true; // S17 backward compat +public bool AllowApproverEditDetails { get; set; } + +// User.cs — ADD 1 Allow* field +public bool AllowDrafterSkipToFinal { get; set; } +``` + +### Service refactor `ApplyReturnModeAsync` + +```csharp +// Resolve currentLevel slot từ pointer +ApprovalWorkflowLevel? currentLevel = null; +if (evaluation.CurrentWorkflowStepIndex is int csi && csi < stepsOrdered.Count) { + var step = stepsOrdered[csi]; + currentLevel = step.Levels.FirstOrDefault(l => l.Order == evaluation.CurrentApprovalLevelOrder); +} + +// Validate Allow* từ Level slot (Admin bypass) +if (!isAdmin && currentLevel is not null) { + var allowed = mode switch { + WorkflowReturnMode.OneLevel => currentLevel.AllowReturnOneLevel, + WorkflowReturnMode.OneStep => currentLevel.AllowReturnOneStep, + WorkflowReturnMode.Assignee => currentLevel.AllowReturnToAssignee, + WorkflowReturnMode.Drafter => currentLevel.AllowReturnToDrafter, + _ => false, + }; + if (!allowed) throw new ConflictException($"Cấp Approver hiện tại không bật mode '{mode}'."); +} +``` + +V1 legacy phiếu (no ApprovalWorkflowId) → fallback Drafter behavior tự động. + +### DRAFTER trình refactor + +```csharp +if (skipToFinal && evaluation.ApprovalWorkflowId is Guid skipAwId) { + if (!isAdmin) { + var drafterUser = await userManager.FindByIdAsync(actorUserId.Value.ToString()); + if (!drafterUser.AllowDrafterSkipToFinal) + throw new ConflictException($"User '{drafterUser.FullName}' không được phép gửi thẳng Cấp cuối."); + } + // ... set pointer = max Step + max Level +} +``` + +### Helper `EnsureEditableForDetailsAsync` refactor + +```csharp +// Read level.AllowApproverEditDetails thay vì workflow +if (!level.AllowApproverEditDetails) + throw new ConflictException($"Cấp Approver hiện tại (Bước {step.Order} / Cấp {levelOrder}) " + + "không được cấp quyền chỉnh sửa Section 2."); +``` + +### DTO refactor + +- `AwLevelDto +5 Allow*`, `AwDefinitionDto -6 Allow*` +- `CreateAwLevelInput +5 Allow*`, `CreateAwDefinitionCommand -6 Allow*` +- `ApprovalWorkflowOptionsDto`: 5 flag (F2 separate field) +- `PurchaseEvaluationDetailBundleDto`: + - RENAME `WorkflowOptions → CurrentLevelOptions` + - ADD `DrafterAllowSkipToFinal bool` + +GetPe handler populate: +- `currentLevelOptions` = 5 Allow* của Cấp hiện tại +- `drafterAllowSkipToFinal` = lookup User.AllowDrafterSkipToFinal từ DrafterUserId + +## Chunk B — FE Admin Designer (`63234b2`) + +`ApprovalWorkflowsV2Page.tsx`: + +- Types: `LevelDto +5 Allow*`, `DefinitionDto -6 Allow*`, `EditLevelEntry +5 Allow*` +- Factory `makeDefaultLevelEntry(order, userId)` — 4 false + AllowReturnToDrafter=true +- `copyFromDefinition` propagate 5 Allow* từ Levels + +UI refactor: +- **REMOVE** entire section "Cấu hình nâng cao" workflow-level (amber bg 6 checkbox) +- **REPLACE** với info banner violet ngắn: + > ⓘ Cấu hình quyền duyệt (Trả lại modes + Edit Section 2) đặt RIÊNG cho từng NV ở mỗi Cấp dưới đây. F2 "Gửi thẳng Cấp cuối" (Drafter) cấu hình ở User Management (mỗi NV global). +- **ADD** inline panel mỗi Level entry (NV row) — 5 checkbox grid-cols-2: + - Trả về 1 Cấp trước + - Trả về 1 Bước trước + - Trả về Người chỉ định + - Trả về Drafter (mặc định checked) + - Cho phép chỉnh sửa Section 2 (col-span-2) +- Header "Quyền duyệt NV #N" [10px] uppercase amber-700 + +POST body propagate 5 Allow* per slot trong `steps[].levels[].*`. + +F2 UI defer — User Management page sẽ thêm 1 toggle khi admin UAT request. + +## Chunk C — FE eOffice (`5ccb2a7`) mirror 2 app + +Types: +- `ApprovalWorkflowOptions` REMOVE allowDrafterSkipToFinal (still 5 flag) +- `PeDetailBundle`: + - RENAME `workflowOptions → currentLevelOptions` + - ADD `drafterAllowSkipToFinal: boolean` + +PeWorkflowPanel.tsx: +- RENAME `wfOptions → levelOptions`, read `evaluation.currentLevelOptions` +- 4 mode radio render conditional theo levelOptions.allowReturnXxx + +PeDetailTabs.tsx: +- F3 approverEditMode: read `currentLevelOptions?.allowApproverEditDetails` +- F2 allowSkipToFinal: read `drafterAllowSkipToFinal` (per-user) + +## Chunk D — Docs (this commit) + +- `docs/database/schema-diagram.md §14`: title Mig 22-29 S17-21 + add 5 column Level + 1 column User block +- `docs/STATUS.md` S21 t5 + 28→29 mig +- `docs/HANDOFF.md` TL;DR đầy đủ trên cùng +- Session log (file này) + +## Stats cumulative S21 t5 + +| Metric | Trước (S21 t4) | Sau (S21 t5) | Δ | +|---|---|---|---| +| DB tables | 59 | 59 | 0 | +| **Migrations** | 28 | **29** | **+1** (Mig 29 refactor per-NV) | +| Endpoints | ~143 | ~143 | 0 (body unchanged, schema-source different) | +| FE pages | 34 | 34 | 0 | +| Unit tests | 84 | 84 | 0 (UAT defer test-after §7) | +| Gotchas | 45 | 45 | 0 | +| Memory | 17 | 17 | 0 | +| Skills | 6 | 6 | 0 | +| Sub-agents | 4 seeds-only | 4 seeds-only | 0 | +| **Commits S21 t5** | — | **4** | `0366946` → `63234b2` → `5ccb2a7` → this | + +## Lessons learned + +1. **Reorder EF migration manual khi cần BACKFILL.** EF auto-generate drop-then-add order — fail nếu cần preserve data. Pattern reusable: ADD → BACKFILL SQL → DROP. Test backfill SQL với realistic data trước commit. + +2. **Per-NV permission pattern**. Khi cần config role/permission theo slot user (vd workflow approver level), gắn flag vào table chứa user FK (ApprovalWorkflowLevels có ApproverUserId), KHÔNG gắn parent table (ApprovalWorkflows). Pattern reusable cho future N-stage hoặc HĐ V2. + +3. **Split per-Role vs per-User scope**. F1+F3 thuộc Approver role → per-slot (Level table). F2 thuộc Drafter role → per-User (Users table). Cấu hình natural theo role context, không hardcode 1 scope cho mọi flag. + +4. **Backward compat backfill discipline**. Mig 29 bulk copy preserve admin config Mig 28 → workflow cũ chạy đúng ngay sau deploy. KHÔNG yêu cầu admin reconfig lần đầu (UAT pain point avoided). + +5. **Iteration speed S21 cumulative**: t3 fix bug → t4 add feature workflow-level → t5 refactor per-NV theo UAT feedback. 3 iteration cùng day, mỗi turn dedicated session, no scope creep. Per `feedback_drastic_refactor_scope`. + +## Handoff + +- ✅ All 4 chunk committed local (4 commits) +- ⏭ **PENDING bro confirm push remote** — `git push origin main` 4 commits ahead `eea86fd..HEAD` +- ⏭ Sau push: CI sẽ trigger (.cs + .tsx + Mig) → 🟩 CICD Monitor spawn smoke verify Mig 29 prod apply + currentLevelOptions returned correctly + drafterAllowSkipToFinal populated cho user backfill + +User next action expected: UAT verify 5 checkbox per Level slot trong Designer. UAT Drafter F2 (BE field sẵn, nhưng User Mgmt UI defer — admin có thể test qua SQL UPDATE Users.AllowDrafterSkipToFinal=1 cho test user nếu cần verify F2 immediate). + +## References + +- BE Mig 29: `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260513130144_RefactorAdvancedOptionsToPerLevelAndDrafterUser.cs` +- Domain: `src/Backend/SolutionErp.Domain/ApprovalWorkflowsV2/ApprovalWorkflow.cs` + `Identity/User.cs` +- BE Service: `src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs` +- BE handlers: `src/Backend/SolutionErp.Application/PurchaseEvaluations/PurchaseEvaluationDetailFeatures.cs` +- DTO: `src/Backend/SolutionErp.Application/PurchaseEvaluations/Dtos/PurchaseEvaluationDtos.cs` +- FE Admin: `fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx` +- FE eOffice × 2 app: `{fe-admin,fe-user}/src/components/pe/PeWorkflowPanel.tsx` + `PeDetailTabs.tsx` + `types/purchaseEvaluation.ts` +- Spec: S21 t4 HANDOFF + user UAT feedback "cấu hình cho từng người" diff --git a/docs/database/schema-diagram.md b/docs/database/schema-diagram.md index b4881b3..3cf5447 100644 --- a/docs/database/schema-diagram.md +++ b/docs/database/schema-diagram.md @@ -715,11 +715,15 @@ CREATE TABLE PurchaseEvaluationDepartmentOpinions ( CREATE UNIQUE INDEX IX_PEDeptOpinions_PEId_Kind ON PurchaseEvaluationDepartmentOpinions (PurchaseEvaluationId, Kind); ``` -## 14. ApprovalWorkflow V2 schema (Migration 22-28, Session 17-21 — 3 bảng mới + 9 column) +## 14. ApprovalWorkflow V2 schema (Migration 22-29, Session 17-21 — 3 bảng mới + 5 column Level + 1 column User) 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. +Mig 29 (S21 t5) — Refactor 6 Allow* options từ workflow-level (Mig 28 S21 t4) +sang PER-NV: 5 flag F1+F3 xuống Level slot (Approver), 1 flag F2 xuống User +(per-Drafter). Backfill bulk SQL preserve admin config S21 t4. + ### Core (3 bảng): ``` @@ -728,15 +732,7 @@ ApprovalWorkflows ├── ApplicableType (1=DuyetNcc, 2=DuyetNccPhuongAn, 3=Contract) ├── Name, Description, IsActive, ActivatedAt ├── IsUserSelectable (Mig 25, S18) — admin pin/unpin cho user pick lúc create phiếu -│ -├── Mig 28 (S21 t4) — 6 advanced options "Cấu hình nâng cao" per workflow: -├── AllowReturnOneLevel bit DEFAULT 0 — F1 mode: Trả về 1 Cấp trước (peer review) -├── AllowReturnOneStep bit DEFAULT 0 — F1 mode: Trả về 1 Bước trước -├── AllowReturnToAssignee bit DEFAULT 0 — F1 mode: Trả về Người chỉ định (pick runtime) -├── AllowReturnToDrafter bit DEFAULT 1 — F1 mode: Trả về Drafter (S17 backward compat) -├── AllowDrafterSkipToFinal bit DEFAULT 0 — F2: Drafter trình thẳng Cấp cuối, skip trung gian -├── AllowApproverEditDetails bit DEFAULT 0 — F3: Approver chỉnh Section 2 lúc đang duyệt -│ +│ (Mig 28 cũ 6 column Allow* đã DROP trong Mig 29 — refactor sang per-NV) └── (audit) CreatedAt, UpdatedAt, CreatedBy, UpdatedBy ApprovalWorkflowSteps (FK Cascade ApprovalWorkflowId, FK Restrict DepartmentId) @@ -747,7 +743,20 @@ ApprovalWorkflowSteps (FK Cascade ApprovalWorkflowId, FK Restrict 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ể) +│ +├── Mig 29 (S21 t5) — 5 advanced options per slot Approver (F1+F3): +├── AllowReturnOneLevel bit DEFAULT 0 — F1 mode: Trả về 1 Cấp trước (peer review) +├── AllowReturnOneStep bit DEFAULT 0 — F1 mode: Trả về 1 Bước trước +├── AllowReturnToAssignee bit DEFAULT 0 — F1 mode: Trả về Người chỉ định +├── AllowReturnToDrafter bit DEFAULT 1 — F1 mode: Trả về Drafter (S17 backward compat) +├── AllowApproverEditDetails bit DEFAULT 0 — F3: NV này chỉnh Section 2 lúc đang duyệt +│ └── INDEX (ApprovalWorkflowStepId, Order) + INDEX ApproverUserId + +Users (AspNetUsers extension) +├── ... existing columns (FullName, DepartmentId, PositionLevel, CanBypassReview, etc) +└── Mig 29 (S21 t5) — F2 per-Drafter: + AllowDrafterSkipToFinal bit DEFAULT 0 — User được Drafter gửi PE thẳng Cấp cuối ``` **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`.