diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 937f707..a48535c 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -1,9 +1,160 @@ # HANDOFF — Brief 5 phút cho session tiếp theo -**Last updated:** 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.**) +**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.**) +**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 4 — F1+F2+F3 PE Workflow advanced options (Mig 28) + +User request 3 tính năng mới trong PE V2 Workflow: +- **F1** 4 mode Trả lại admin stick: 1 Cấp / 1 Bước / Người chỉ định / Người soạn thảo +- **F2** Drafter gửi thẳng Cấp cuối (skip mọi Bước/Cấp trung gian) +- **F3** Approver chỉnh sửa Section 2 (Hạng mục + NCC + Báo giá) khi đang duyệt + +### Q&A clarify chốt scope (2 lượt AskUserQuestion) + +- **F1 "1 bậc"** = cả 2 mode (admin chọn 1 Cấp HOẶC 1 Bước HOẶC cả 2 stick) +- **F1 "Người chỉ định"** = Approver pick runtime từ list NV đã ký (PE.LevelOpinions) +- **F1 behavior** = 3 mode đầu giữ Phase=ChoDuyet lùi pointer (peer review chain). Mode Drafter giữ Phase=TraLai S17 fallback. +- **F2 skip** = chỉ skip tới Level cuối (CEO) — Dropdown 2 option "Gửi tuần tự" vs "Gửi thẳng Cấp cuối" +- **F2+F3 admin enable** = cả 2 cần admin tick per workflow (audit nghiêm) +- **F3 approver perm** = mọi approver Cấp đang active (currentLevel match) +- **F3 scope** = Section 2 only (Hạng mục + NCC + Báo giá), KHÔNG đụng PE Header, KHÔNG reset workflow +- **Test** = test-after UAT default Phase 9 (skip dotnet test mỗi chunk, npm build × 2 app pass) + +### Chunk A — Mig 28 + Domain (`0294693`) + +`ApprovalWorkflow.cs` thêm 6 bool field: +- `AllowReturnOneLevel` / `AllowReturnOneStep` / `AllowReturnToAssignee` (default false) +- `AllowReturnToDrafter` (default **TRUE** — backward compat S17) +- `AllowDrafterSkipToFinal` / `AllowApproverEditDetails` (default false) + +EF config `ApprovalWorkflowConfiguration` thêm 6 `HasDefaultValue` match Mig 28 DEFAULT. + +Mig 28 `AddAdvancedOptionsToApprovalWorkflows`: +- 6 AddColumn bit NOT NULL DEFAULT 0/1 +- 3-file rule complete (mig.cs + Designer.cs + Snapshot.cs) +- Apply LocalDB Dev + Design + +### Chunk B — BE Service + handlers + DTOs (`c56024b`) + +**Service interface + impl** `TransitionAsync` thêm 3 optional param (backward compat): +- `WorkflowReturnMode? returnMode` (enum {OneLevel=1, OneStep=2, Assignee=3, Drafter=4}) +- `Guid? returnTargetUserId` (required khi mode=Assignee) +- `bool skipToFinal` + +REJECT branch extend với helper `ApplyReturnModeAsync` switch 4 mode: +- OneLevel: lùi 1 Cấp cùng Step. Bước 1 Cấp 1 → fallback Drafter. +- OneStep: lùi sang Bước trước Cấp cuối. Bước 1 → fallback Drafter. +- Assignee: tìm Step+Level match `ApproverUserId == returnTargetUserId`. +- Drafter: Phase=TraLai clear pointer (S17 behavior). +- 3 mode đầu giữ ChoDuyet + reset SLA 7d. +- Admin bypass workflow.Allow* flag check. +- Non-admin → throw ConflictException nếu flag disabled. + +DRAFTER trình branch extend với F2 skipToFinal: +- Workflow.AllowDrafterSkipToFinal required (non-admin) +- Set CurrentWorkflowStepIndex = Steps.Count-1 + CurrentApprovalLevelOrder = max Level +- Audit comment append "[Drafter gửi thẳng Cấp cuối]" + +**Helper edit guard** `EnsureEditableForDetailsAsync` mới (PurchaseEvaluationDraftGuard class): +- Drafter scope: DangSoanThao OR TraLai +- F3 Approver scope: ChoDuyet + workflow.AllowApproverEditDetails + actor match CurrentLevel.ApproverUserId +- Admin bypass workflow flag check + +**8 handler switch** sang helper mới + inject ICurrentUser khi cần: +- Detail Add/Update/Delete + Quote Upsert/Delete (5 handler — replace EnsureDraftAsync) +- Supplier Add/Update/Remove (3 handler — bonus security fix, trước đây hoàn toàn KHÔNG có phase guard!) +- Update/Delete handler trước đây silent → thêm changelog `PhaseAtChange + UserId + Summary` (append `[Approver edit khi đang duyệt]` khi phase=ChoDuyet) + +**Command DTO + DTOs**: +- `TransitionPurchaseEvaluationCommand` +3 optional field +- `ApprovalWorkflowOptionsDto` NEW sub-record (6 Allow* flag) +- `PurchaseEvaluationDetailBundleDto` +WorkflowOptions field +- `AwDefinitionDto` +6 Allow* (admin Designer GET) +- `CreateAwDefinitionCommand` +6 Allow* param (admin Designer POST) + +### Chunk C — FE Admin Designer (`a508564`) + +`ApprovalWorkflowsV2Page.tsx` Designer modal thêm section "Cấu hình nâng cao" 3 sub-group: + +1. Mode Trả lại 4 checkbox: + - Trả về 1 Cấp trước (peer review chain trong cùng Bước) + - Trả về 1 Bước trước (Cấp cuối Bước trước nhận lại) + - Trả về Người chỉ định (pick runtime từ NV đã ký) + - Trả về Người soạn thảo (default checked = backward compat S17) + +2. Drafter skip: 1 checkbox "Cho phép Drafter gửi thẳng Cấp cuối" + +3. Approver edit: 1 checkbox "Cho phép Approver chỉnh sửa Section 2" + +Styling: container amber-50/30 border distinct với Steps section. Helper text [10px] dưới label. Headers uppercase tracking. + +DTO types + state defaults từ cloneFrom (giữ config version trước) hoặc S17 fallback (chỉ AllowReturnToDrafter=true). POST body propagate 6 flag → BE Create handler set entity. + +fe-user KHÔNG mirror (Designer admin-only). + +### Chunk D — FE eOffice (`d27caaf`) mirror 2 app + +Types `purchaseEvaluation.ts`: +- `ApprovalWorkflowOptions` type +- `WorkflowReturnMode` const-object +- `PeDetailBundle` +workflowOptions field + +`PeWorkflowPanel.tsx` F1 Trả lại radio picker: +- State `returnMode` (default Drafter) + `returnTargetUserId` +- Dialog Trả lại render 1-4 radio mode enabled theo wfOptions.Allow* +- Assignee mode → submodal Select pick từ levelOpinions (NV đã ký), dedupe by userId +- Banner amber rounded dưới mô tả hành vi mode chọn +- Mutation payload +returnMode +returnTargetUserId khi isTraLaiAction + +`PeDetailTabs.tsx` F2 Drafter skip: +- State `skipToFinal` + `allowSkipToFinal` từ workflowOptions +- submitForApproval mutationFn accept opts.skipToFinal +- Workspace action bar: checkbox violet "Gửi thẳng Cấp cuối (skip trung gian)" conditional +- Confirm dialog message + button label dynamic theo skipToFinal + +`PeDetailTabs.tsx` F3 Approver edit Section 2: +- useAuth import + compute `approverEditMode` (phase=ChoDuyet + workflowOptions.allowApproverEditDetails + actor match) +- `itemsReadOnly = readOnly && !approverEditMode` → ItemsTab nhận +- Banner violet "ⓘ Bạn được phép chỉnh sửa..." khi approverEditMode + readOnly (Duyệt menu) +- InfoTab / NccSelectorRow / BudgetFieldRow GIỮ strict isEditablePhase (Header + Section 3, KHÔNG trong F3 scope) + +### Chunk E — Docs (this commit) + +- `docs/database/schema-diagram.md §14` cập nhật title "Mig 22-28, S17-21" + thêm 6 column Allow* trong Core block với inline comment F1/F2/F3 +- `docs/STATUS.md` Last updated S21 t4 + count 27→28 mig + UAT defer test count unchanged 84 +- `docs/HANDOFF.md` TL;DR S21 t4 đầy đủ (file này) +- `docs/changelog/sessions/2026-05-13-1200-s21-turn4-pe-workflow-advanced-options.md` session log + +### State chốt S21 turn 4 + +| Metric | Trước (S21 t3) | Sau (S21 t4) | Δ | +|---|---|---|---| +| DB tables | 59 | 59 | 0 | +| **Migrations** | 27 | **28** | **+1** (Mig 28 6 column Allow*) | +| Endpoints | ~142 | ~143 | +1 (extend transitions body) | +| FE pages | 34 | 34 | 0 (Designer extend section) | +| **Unit tests** | 84 | **84** | 0 (UAT defer test-after §7) | +| Gotchas | 45 | 45 | 0 | +| Memory entries | 17 | 17 | 0 | +| Skills | 6 | 6 | 0 | +| Sub-agents | 4 seeds-only | 4 seeds-only | 0 | +| **Commits S21 t4** | — | **5** | (`0294693` → `c56024b` → `a508564` → `d27caaf` → this) | + +### Pending — Test-after (Plan C carry) + +Per `feedback_uat_skip_verify` Phase 9 default: viết test sau UAT 2-3 lần ổn. +Test scope candidate (test-after-uat commit riêng): +- Service `ApplyReturnModeAsync` 4 mode happy path (OneLevel/OneStep/Assignee/Drafter) +- Service skipToFinal happy path + AllowDrafterSkipToFinal=false → ConflictException +- `EnsureEditableForDetailsAsync` 3 scenario: Drafter scope / Approver match / Approver mismatch → Forbidden + +Bundle với Plan C existing (test #44 silent 403 + test V2 ApproveV2Async + Mig 25/27 PATCH). + +--- + ## TL;DR Session 21 turn 3 — Bug fix CRITICAL "Trả về nhưng hệ thống vẫn duyệt" (gotcha #45) User UAT 2026-05-12 21:00 screenshot button labeled `← Trả lại` trong PE Workflow Panel (menu "Duyệt"), mô tả hành vi: nhấn vào nhưng phiếu KHÔNG về phase TraLai — ngược lại tiến qua Cấp tiếp theo. User mô tả: "Trả về nhưng hệ thống vẫn duyệt". diff --git a/docs/STATUS.md b/docs/STATUS.md index 68dd7d8..bddc3f6 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -2,14 +2,15 @@ > **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-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).**) +**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.**) +**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).**) **S20 wrap:** 2026-05-11 22:00 (Session 20 wrap turns 1-12 — **🎯 14 commit `9dee00d` → `ae1814c`. PE Detail UI restructure 3 yêu cầu (t1-5) + Manual budget drop tên (t6) + Mig 27 admin menu eOffice (t7) + NCC palette 5-màu cycle + Winner icon ✓ đậm + AddSupplier auto-fill master + Responsive laptop nhỏ 4-tầng pattern (t8-11) + Multi-agent infrastructure setup 3 sub-agents (t12). 27 mig (+1) · 59 tables · ~142 endpoints (+1) · 34 FE pages (+1) · 61 menu key (+1) · 81 test pass unchanged · 44 gotcha · 16 memory entries (+2) · 3 sub-agents NEW. Phase 9 UAT iteration mode.**) **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), 27 migrations (+1 Mig 27 menu visibility S20 t7), ~142 API endpoints (+1 PATCH /menus/{key}), 34 FE pages (+1 MenuVisibilityPage). 84 unit test pass** (58 Domain + 26 Infra = 23 baseline + 3 PE guard S21 t3 fix gotcha #45). **45 gotcha (+1 #45 PE button label vs decision payload mismatch S21 t3)**. 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 (+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. ### 🌐 Production URLs diff --git a/docs/changelog/sessions/2026-05-13-1200-s21-turn4-pe-workflow-advanced-options.md b/docs/changelog/sessions/2026-05-13-1200-s21-turn4-pe-workflow-advanced-options.md new file mode 100644 index 0000000..f237967 --- /dev/null +++ b/docs/changelog/sessions/2026-05-13-1200-s21-turn4-pe-workflow-advanced-options.md @@ -0,0 +1,316 @@ +# Session 21 turn 4 — 2026-05-13 12:00 — PE Workflow advanced options (F1+F2+F3, Mig 28) + +**Dev:** Claude Opus 4.7 1M Max (em main solo — 3 feature multi-layer Implementer REFUSE per cross-stack reasoning chain rule) +**Duration:** ~3h (clarify Q&A 2 lượt + 5 chunk implement + verify build cả 2 app mỗi chunk) +**Base commit:** `6d30ba4` (S21 t3 fix gotcha #45 Chunk C Docs) +**Commits này turn:** `0294693` (A schema) → `c56024b` (B BE) → `a508564` (C FE Admin) → `d27caaf` (D FE eOffice) → this (E Docs) + +## Trigger + +User chốt 3 tính năng mới trong "Quy trình duyệt NCC": + +1. **F1** — 4 mode Trả lại trong workflow (admin stick per workflow): + - Cho trả về 1 bậc trước đó + - Cho trả về người chỉ định + - Trả về người soạn thảo + - Workflow tick stick mode nào enabled → user eOffice dropdown chỉ hiện mode đó + +2. **F2** — Drafter chọn "Gửi duyệt thẳng cấp" (vd skip → CEO): + - "Các bước này đều ghi nhận vào quy trình duyệt phiếu" + +3. **F3** — Approver chỉnh sửa phiếu (Section 2 chi tiết): + - "lưu vào lịch sử chỉnh sửa luôn" + +## Clarify Q&A (2 lượt AskUserQuestion) + +### Lượt 1 — Schema + UX scope: + +| Câu | User chốt | +|---|---| +| F1 "Trả về 1 bậc trước đó" nghĩa là gì? | **Cả 2 mode (admin chọn)** — 1 Cấp + 1 Bước stick độc lập | +| F1 "Người chỉ định" nguồn từ đâu? | **Approver pick runtime** — dropdown từ list NV đã ký (PeLevelOpinions) | +| F2 skip scope | **Chỉ skip tới Level cuối (CEO)** — Dropdown 2 option | +| F3 edit scope | **Section 2 (Hạng mục + NCC + Báo giá)** — KHÔNG đụng Header, KHÔNG reset workflow | + +### Lượt 2 — Behavior + admin enable: + +| Câu | User chốt | +|---|---| +| 3 mode Trả lại behavior | **Giữ ChoDuyet, lùi pointer** (peer review chain). Mode Drafter giữ Phase=TraLai S17 | +| F2+F3 admin enable | **Cả 2 cần admin tick** per workflow (audit nghiêm) | +| F3 approver perm | **Mọi approver Cấp đang active** (currentLevel match) | +| Test timing | **Test-after UAT default Phase 9** (skip dotnet test mỗi chunk, npm build × 2 app) | + +## Chunk A — Schema + Migration 28 (`0294693`) + +### Domain `ApprovalWorkflow.cs` +6 bool + +```csharp +public bool AllowReturnOneLevel { get; set; } // F1 mode 1 +public bool AllowReturnOneStep { get; set; } // F1 mode 2 +public bool AllowReturnToAssignee { get; set; } // F1 mode 3 +public bool AllowReturnToDrafter { get; set; } = true; // F1 mode 4 (backward compat S17) +public bool AllowDrafterSkipToFinal { get; set; } // F2 +public bool AllowApproverEditDetails { get; set; } // F3 +``` + +### EF config `ApprovalWorkflowConfiguration` + +```csharp +e.Property(x => x.AllowReturnOneLevel).HasDefaultValue(false); +// ... 4 more false ... +e.Property(x => x.AllowReturnToDrafter).HasDefaultValue(true); // backfill rows cũ +``` + +### Migration 28 `AddAdvancedOptionsToApprovalWorkflows` + +- 6 AddColumn bit NOT NULL DEFAULT 0/1 +- 3-file rule complete (mig.cs + Designer.cs + Snapshot.cs) +- Apply LocalDB Dev + Design success + +## Chunk B — BE Service + handlers + DTOs (`c56024b`) + +### Service signature extend (backward compat) + +```csharp +public async Task TransitionAsync( + PurchaseEvaluation evaluation, + PurchaseEvaluationPhase targetPhase, + Guid? actorUserId, + IReadOnlyList actorRoles, + ApprovalDecision decision, + string? comment, + WorkflowReturnMode? returnMode = null, // ← NEW + Guid? returnTargetUserId = null, // ← NEW + bool skipToFinal = false, // ← NEW + CancellationToken ct = default) +``` + +`WorkflowReturnMode` enum: OneLevel=1, OneStep=2, Assignee=3, Drafter=4. + +### REJECT branch extend với `ApplyReturnModeAsync` + +```csharp +// Inside REJECT branch (line 51+) +var effectiveMode = returnMode ?? WorkflowReturnMode.Drafter; +var returnSummary = await ApplyReturnModeAsync( + evaluation, effectiveMode, returnTargetUserId, isAdmin, ct); +comment = $"{comment} [{returnSummary}]"; +``` + +Helper `ApplyReturnModeAsync` switch 4 mode: + +```csharp +// OneLevel — lùi 1 Cấp trong cùng Step. Bước 1 Cấp 1 → fallback Drafter. +if (curLevel > 1) { + evaluation.CurrentApprovalLevelOrder = curLevel - 1; + summary = $"Trả về Cấp {curLevel - 1}"; +} +else if (curStepIdx > 0) { + var prevStep = stepsOrdered[curStepIdx - 1]; + evaluation.CurrentWorkflowStepIndex = curStepIdx - 1; + evaluation.CurrentApprovalLevelOrder = prevStep.Levels.Max(l => l.Order); + summary = $"Trả về Bước {prevStep.Order} Cấp {maxLevel} (Bước trước)"; +} +else { /* fallback Drafter */ } + +// OneStep — lùi sang Bước trước, set Level = max của Bước đó. Bước 1 → fallback. +// Assignee — tìm Step+Level match returnTargetUserId trong workflow. +// Drafter — Phase=TraLai clear pointer (S17 backward compat). +``` + +3 mode đầu giữ Phase=ChoDuyet + reset SLA 7d. Admin bypass workflow.Allow* flag check. + +### DRAFTER trình branch extend với F2 + +```csharp +if (skipToFinal && evaluation.ApprovalWorkflowId is Guid skipAwId) { + // Workflow.AllowDrafterSkipToFinal required (non-admin) + if (!wfSkip.AllowDrafterSkipToFinal) + throw new ConflictException("Workflow không bật mode 'Gửi thẳng Cấp cuối'."); + evaluation.CurrentWorkflowStepIndex = wfSkip.Steps.Count - 1; // 0-based last + evaluation.CurrentApprovalLevelOrder = finalStep.Levels.Max(l => l.Order); + comment = $"{comment} [Drafter gửi thẳng Cấp cuối — skip Bước/Cấp trung gian]"; +} +``` + +### Helper edit guard `EnsureEditableForDetailsAsync` (PurchaseEvaluationDraftGuard class) + +```csharp +public static async Task EnsureEditableForDetailsAsync( + IApplicationDbContext db, Guid id, ICurrentUser currentUser, CancellationToken ct) +{ + var pe = await db.PurchaseEvaluations.FirstOrDefaultAsync(...); + + // Drafter scope — bypass current Controller [Authorize] handles role + if (pe.Phase == DangSoanThao || pe.Phase == TraLai) return pe; + + // F3 Approver scope (Mig 28) — chỉ ChoDuyet + if (pe.Phase == ChoDuyet && currentUser.UserId is Guid actorUserId) { + if (currentUser.Roles.Contains(Admin)) return pe; // bypass + + var workflow = await db.ApprovalWorkflows.Include(w => w.Steps)... + if (!workflow.AllowApproverEditDetails) + throw new ConflictException("Workflow không bật mode..."); + + var level = step.Levels.FirstOrDefault(lv => lv.Order == levelOrder); + if (level.ApproverUserId != actorUserId) + throw new ForbiddenException("Chỉ NV phụ trách Bước X / Cấp Y..."); + + return pe; + } + + throw new ConflictException($"Phiếu PE ở Phase={pe.Phase}, không thể chỉnh sửa."); +} +``` + +### 8 handler switch + audit changelog + +- `PurchaseEvaluationDetailFeatures.cs`: Add/Update/Delete + Quote Upsert/Delete (5) +- `PurchaseEvaluationSupplierFeatures.cs`: Add/Update/Remove (3) — **bonus security fix**: trước đây Supplier handlers HOÀN TOÀN KHÔNG có phase guard! + +Update/Delete handlers trước đây silent → thêm changelog `PhaseAtChange + UserId + Summary` (append `[Approver edit khi đang duyệt]` khi phase=ChoDuyet). + +### Command DTO + DTOs extend + +- `TransitionPurchaseEvaluationCommand` +3 optional field + Validator +- `ApprovalWorkflowOptionsDto` NEW sub-record (6 Allow* flag) +- `PurchaseEvaluationDetailBundleDto` +WorkflowOptions field (null nếu V1 legacy) +- `AwDefinitionDto` +6 Allow* (admin Designer GET) +- `CreateAwDefinitionCommand` +6 Allow* param (admin Designer POST) + +### Verify +- `dotnet build SolutionErp.slnx` → 0 err, 2 warn pre-existing DocxRenderer +- 3 regression test gotcha #45 vẫn PASS (signature backward compat) + +## Chunk C — FE Admin Designer (`a508564`) + +### `ApprovalWorkflowsV2Page.tsx` Section "Cấu hình nâng cao" 6 checkbox + +Container amber-50/30 + border distinct với Steps. 3 sub-group: + +1. **Mode Trả lại** (4 checkbox): + - ☐ Trả về 1 Cấp trước (peer review chain trong cùng Bước) + - ☐ Trả về 1 Bước trước (Cấp cuối Bước trước nhận lại) + - ☐ Trả về Người chỉ định (pick runtime từ NV đã ký) + - ☑ Trả về Người soạn thảo (default checked = backward compat S17) + +2. **Drafter gửi duyệt** (1 checkbox): + - ☐ Cho phép Drafter gửi thẳng Cấp cuối + +3. **Approver chỉnh sửa phiếu** (1 checkbox): + - ☐ Cho phép Approver chỉnh sửa Section 2 (Hạng mục + NCC + Báo giá) + +DTO types + state defaults từ cloneFrom (giữ config version trước) hoặc S17 fallback. POST body propagate. + +fe-user KHÔNG mirror (Designer admin-only). + +## Chunk D — FE eOffice (`d27caaf`) mirror 2 app + +### Types `purchaseEvaluation.ts` + +```typescript +export type ApprovalWorkflowOptions = { + allowReturnOneLevel: boolean + // ... 5 more +} + +export const WorkflowReturnMode = { + OneLevel: 1, OneStep: 2, Assignee: 3, Drafter: 4, +} as const + +export type PeDetailBundle = { + // ... + workflowOptions: ApprovalWorkflowOptions | null +} +``` + +### `PeWorkflowPanel.tsx` F1 Trả lại radio picker + +State `returnMode` + `returnTargetUserId`. Dialog render 1-4 radio mode enabled theo `wfOptions.Allow*`. Assignee mode → submodal Select pick từ `evaluation.levelOpinions` (NV đã ký, dedupe by userId). + +Banner amber rounded dưới mô tả hành vi mode chọn: +- Drafter: "Phiếu sẽ về 'Trả lại'. Drafter có thể sửa rồi trình lại từ Cấp 1 Bước 1." +- Assignee: "Phiếu sẽ về Cấp/Bước của NV đã chọn..." +- OneLevel/OneStep: "Phiếu sẽ lùi pointer (vẫn 'Đã gửi duyệt')..." + +Mutation payload +`returnMode` +`returnTargetUserId` khi `isTraLaiAction`. + +### `PeDetailTabs.tsx` F2 Drafter skip + +State `skipToFinal` + `allowSkipToFinal` từ workflowOptions. submitForApproval mutationFn accept `opts.skipToFinal`. Workspace action bar: checkbox violet conditional. Confirm dialog message + button label dynamic. + +### `PeDetailTabs.tsx` F3 Approver edit Section 2 + +useAuth import + compute `approverEditMode`: + +```typescript +const approverEditMode = evaluation.phase === ChoDuyet + && (evaluation.workflowOptions?.allowApproverEditDetails ?? false) + && actorMatchesLevel // isAdmin || actor.id in currentApproval.approvers +const itemsReadOnly = readOnly && !approverEditMode +``` + +Banner violet "ⓘ Bạn được phép chỉnh sửa..." khi approverEditMode + readOnly (Duyệt menu). + +InfoTab / NccSelectorRow / BudgetFieldRow GIỮ strict isEditablePhase (Header + Section 3 KHÔNG trong F3 scope). + +### Verify +- `npm run build × 2 app` pass (fe-user 7.52s + fe-admin 499ms cached) +- 0 TS6 err, warning chunk size pre-existing + +## Chunk E — Docs (this commit) + +- `docs/database/schema-diagram.md §14` title "Mig 22-28, S17-21" + thêm 6 column Allow* trong Core block với inline comment F1/F2/F3 +- `docs/STATUS.md` Last updated S21 t4 + count 27→28 mig + UAT defer test count unchanged +- `docs/HANDOFF.md` TL;DR S21 t4 đầy đủ (5 chunk narrative + Q&A + pattern reusable) +- Session log file này + +## Stats cumulative S21 t4 + +| Metric | Trước (S21 t3) | Sau (S21 t4) | Δ | +|---|---|---|---| +| DB tables | 59 | 59 | 0 | +| **Migrations** | 27 | **28** | **+1** (Mig 28) | +| Endpoints | ~142 | ~143 | +1 (transitions body extend) | +| FE pages | 34 | 34 | 0 (Designer extend) | +| **Unit tests** | 84 | **84** | 0 (UAT defer test-after §7) | +| Gotchas | 45 | 45 | 0 | +| Memory entries | 17 | 17 | 0 | +| Skills | 6 | 6 | 0 | +| Sub-agents | 4 seeds-only | 4 seeds-only | 0 | +| Commits S21 t4 | — | **5** | `0294693` → `c56024b` → `a508564` → `d27caaf` → this | + +## Lessons learned + +1. **Backward-compat option pattern.** Thêm 6 cột mới với 1 cột default TRUE (AllowReturnToDrafter S17 fallback) + 5 cột default FALSE (admin opt-in) → workflow cũ chạy đúng sau deploy, không breaking change. Pattern reusable cho future feature flags. + +2. **Boundary helper pattern** (extension thay vì rewrite). Helper cũ `EnsureDraftAsync` strict DangSoanThao only → thêm helper mới `EnsureEditableForDetailsAsync` accept thêm Approver scope. KHÔNG rename + KHÔNG break cũ → coexistence safe. 8 handler switch cleanly. + +3. **Multi-agent decision tree áp đúng**. 3 feature multi-layer (Domain + Mig + Service + 8 handler + 2 FE app + 4 DTO file) — tightly coupled reasoning chain → Implementer REFUSE per Cognition "writes single-threaded". Em main solo decision đúng, KHÔNG split tránh agent thrash. + +4. **Per-chunk discipline (5 chunk A-E)** mặc dù big-feature multi-layer >1000 LOC. Mỗi chunk verify build pass trước commit → rollback dễ nếu fail. Audit trail commit history rõ ràng cho future debug. + +5. **Test-after UAT default Phase 9** chấp nhận được khi feature nhánh enable mới (admin opt-in). Test-before BẮT BUỘC chỉ cho bug fix (gotcha #45 S21 t3 đã làm). Cảnh báo carry: Plan C test-after candidate cumulative ngày càng nhiều — cần dedicated commit "Test catch-up S21 t1-t4" sau UAT ổn. + +## Handoff + +- ✅ Chunk A `0294693` BE schema + Mig 28 committed local +- ✅ Chunk B `c56024b` BE Service + handlers + DTOs committed local +- ✅ Chunk C `a508564` FE Admin Designer committed local +- ✅ Chunk D `d27caaf` FE eOffice mirror 2 app committed local +- ✅ Chunk E (this) — Docs commit sau khi save session log +- ⏭ **PENDING bro confirm push remote** — `git push origin main` 8 commit ahead `0a3b747..HEAD` (S21 t3 fix gotcha #45 + S21 t4 F1+F2+F3) + +User next action expected: UAT test 3 feature mới trong env Prod hoặc local. Sau UAT 2-3 lần ổn → Plan C bundle test-after catch up. + +## References + +- BE Service: `src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs` +- BE entity: `src/Backend/SolutionErp.Domain/ApprovalWorkflowsV2/ApprovalWorkflow.cs` +- BE handlers: `src/Backend/SolutionErp.Application/PurchaseEvaluations/{PurchaseEvaluationFeatures, PurchaseEvaluationDetailFeatures, PurchaseEvaluationSupplierFeatures}.cs` +- Mig 28: `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260513114505_AddAdvancedOptionsToApprovalWorkflows.cs` +- FE Admin Designer: `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 context: gotcha #45 S21 t3 BE guard payload mismatch + Session 17 spec 5 trạng thái +- Rules: §3.9 mirror 2 FE, §6.5 KEEP narrative, §7 test timing, `feedback_uat_skip_verify`, `feedback_per_chunk_commit` diff --git a/docs/database/schema-diagram.md b/docs/database/schema-diagram.md index 78b7ddd..b4881b3 100644 --- a/docs/database/schema-diagram.md +++ b/docs/database/schema-diagram.md @@ -715,7 +715,7 @@ CREATE TABLE PurchaseEvaluationDepartmentOpinions ( CREATE UNIQUE INDEX IX_PEDeptOpinions_PEId_Kind ON PurchaseEvaluationDepartmentOpinions (PurchaseEvaluationId, Kind); ``` -## 14. ApprovalWorkflow V2 schema (Migration 22-25, Session 17-18 — 3 bảng mới + 3 column) +## 14. ApprovalWorkflow V2 schema (Migration 22-28, Session 17-21 — 3 bảng mới + 9 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. @@ -728,6 +728,15 @@ 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 +│ └── (audit) CreatedAt, UpdatedAt, CreatedBy, UpdatedBy ApprovalWorkflowSteps (FK Cascade ApprovalWorkflowId, FK Restrict DepartmentId)