Files
solution-erp/docs/changelog/migration-todos.md
pqhuy1987 7c83ac8f17 [CLAUDE] Docs+Skill: chốt Session 14 wrap-up — PE 3-button workflow + Task 2 defer
Session 14 (2026-05-07) docs/skill update:

STATUS.md:
- Last updated + Phase summary count (95→96 test, 20 mig, 57 bảng, 41 gotcha)
- Recently Done row Session 14 chi tiết (3-button + Task 2 in-progress + DesignTime/Runtime DB gotcha)

HANDOFF.md:
- TL;DR Session 14 prepend với 1 commit + Task 2 defer
- 5 cảnh báo Session 15+: TraLai phase orphan / Task 2 sample seed / DesignTime
  vs Runtime DB / Budget N-stage defer / schema-diagram §17-19 defer

migration-todos.md: Phase 9 + Session 14 block 4 sub-task done + 2 defer task

Session log NEW `2026-05-07-2500-3-button-workflow.md`:
- Bối cảnh + spec 3-button (Duyệt/Trả lại/Từ chối) + implementation chi tiết
  (Domain policy expand + Service tách reject + FE button + dialog warning)
- Tests update (95→96 với +1 NEW Reject_To_TuChoi_Locks_Permanently)
- Task 2 in-progress: DesignTime vs Runtime DB gotcha + API exit 255 sớm
- Plan organization sau S14

Skill ef-core-migration:
- description + heading: 17→20 migration
- Bảng migration history thêm Mig 18-19 (PE) + Mig 20 (Contract)
- Section MỚI "N-stage workflow pattern (Mig 18-20)" — architecture decision
  với filtered unique trick + per-module migration packaging guideline
- Phase 8 update: 83→96 test breakdown

Skill contract-workflow:
- Section MỚI "Phase 9+ done (Mig 18-20 — Session 12/13/14)":
  * N-stage workflow PE + Contract (entity + filtered unique split + service
    refactor + tests + FE Designer + UsersPage cấp + API)
  * PE 3-button approval (Duyệt/Trả lại/Từ chối) Session 14
  * Defer: Budget N-stage / Phase TraLai=98 orphan

CLAUDE.md root: 16→20 migration + 55→57 bảng + 83→96 test
docs/rules.md §7: Phase 9 active 83→96 test

Verify: dotnet test 96 pass + npm build (skip — pure docs/skill update).

🎉 Session 14 wrap-up complete. Pushed 1 task (3-button) + Task 2 defer.
Cumulative since session start (13h17): 13 commit (1 button removal +
6 PE N-stage Chunk A-F + 5 Contract N-stage Chunk A,B,C,D,F + 1 3-button).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:40:00 +07:00

385 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Migration To-dos — Atomic Roadmap
> Tick `[x]` khi xong. Phase 0-8 đã DONE — collapsed. Detail xem session
> logs trong `docs/changelog/sessions/`. Active work: Phase 9 (UAT + Ops + carry over PE PDF export + Tests Phase 3-5).
## ✅ Phase 0-5 + Tier 3 — Done (2026-04-21..22)
| Phase | Focus | Trạng thái |
|---|---|---|
| 0 Draft | Scaffold .NET 10 + 2 Vite + parse FORM/QT + docs | ✅ |
| 1 Alpha Core | Auth + 12 role + Permission Matrix + 3 master CRUD + Contract draft | ✅ |
| 2 Form Engine | OpenXml + ClosedXML render + LibreOffice PDF + DynamicForm | ✅ |
| 3 Workflow | State machine 9 phase + RG-001 code gen + SLA job + attachments + SignalR realtime | ✅ |
| 4 Reporting | Dashboard KPI + Excel export + MyDashboard role-aware + brand identity #1F7DC1 | ✅ |
| 5 Production | CI/CD Gitea Actions + 3 IIS site + Let's Encrypt + Security headers + Users CRUD | ✅ |
| Tier 3 | Versioned workflow + 3-panel layout + 4-bảng overhaul + 4 master catalogs + 16 demo users + RolesPage CRUD + 7 demo HĐ varied | ✅ |
Detail chi tiết: `docs/changelog/sessions/2026-04-21-*.md` + `2026-04-22-0300-tier3-feature-complete.md` + `2026-04-23-*.md`.
## ✅ Phase 6 — Module Duyệt NCC (tiền-HĐ) — Done
### Iter 1 (2026-04-23)
- [x] Migration 12 `AddPurchaseEvaluations` — 10 bảng (Header/Suppliers/Details/Quotes/Approvals/Changelogs/Attachments/WorkflowDefinitions/Steps/StepApprovers)
- [x] Domain — 2 enum (Type A/B, Phase 7-state) + Policy record + Registry + FromDefinition builder
- [x] Seed — 13 menu Pe_*/PeWf_* + 2 WorkflowDefinition v01 (QT-DN-A 3-step, QT-DN-B 5-step)
- [x] Application CQRS ~900 LOC — Create/Update/Transition/List/Inbox/Get/Delete + Supplier CRUD + Detail CRUD + Quote Upsert + SelectWinner + Changelog
- [x] PurchaseEvaluationWorkflowService — policy guard + approval + notification + changelog
- [x] PurchaseEvaluationsController — 17 endpoint REST
- [x] FE 2 app — Types + PurchaseEvaluationsListPage 3-panel + Create page + PeDetailTabs + PeWorkflowPanel + Menu resolver Pe_*
- [x] Kế thừa HĐ — `CreateContractFromEvaluationCommand` (guard DaDuyet + SelectedSupplier + !ContractId) → Contract draft. FE CreateContractDialog pick ContractType.
- [x] **Migration 13** `AddPurchaseEvaluationCodeSequences` — atomic MaPhieu sequence `PE/{YYYY}/{A|B}/{Seq:D3}`
- [x] Demo PE seed — 4 phiếu varied phase (A-001/A-002/A-003/B-001) + Pe_* permission defaults 7 role × 9 menu key
Session log: `2026-04-23-2300-purchase-evaluations.md` + `2026-04-24-1030-pe-polish-demo-maphieu-perms.md`.
### Iter 2 — UX polish (2026-04-24)
- [x] Rename menu "Phương Án" → "Giải pháp" + backfill DB (zero breaking change)
- [x] Menu tree inheritance extend Pe_*/PeWf_* (`GetMyMenuTreeQuery` + 4 root)
- [x] Accordion mutex Pe_* groups + sidebar w-72 + label nowrap
- [x] NavLink active check query string (queryMatches helper) — fix 2 leaf cùng highlight
- [x] PE detail flat layout: Panel 2 = 4 section (Thông tin/NCC/Hạng mục/**Bảng so sánh**), Panel 3 += Approvals + Changelog
- [x] Upload file đính kèm per-NCC (SupplierAttachmentsCell) + Bảng so sánh tổng (GeneralAttachmentsSection, supplierRowId=null) + enum `ComparisonTable=4`
- [x] readOnly mode menu "Duyệt" (pendingMe=1) — hide Sửa/Xóa/Thêm/Edit/Upload/Delete, giữ download + transition + comment
- [x] Contract: move Lịch sử điều chỉnh Panel 2 → Panel 3 (Chi tiết HĐ full-width)
- [x] Demo email rebrand `@solutionerp.local``@solutions.com.vn` + `BackfillUserEmailDomainAsync` (idempotent rename 4 field Email/NormalizedEmail/UserName/NormalizedUserName)
Session log: `2026-04-24-chot-session-3-pe-polish.md`.
### ✅ Domain rebrand `.huypham.vn` → `.solutions.com.vn` (2026-04-24)
- [x] 18 file repo (FE env + scripts + CI/CD + docs + skill + code comments)
- [x] `scripts/migrate-domains.ps1` (ASCII-only #30) — 3 IIS binding + 3 cert Let's Encrypt + auto HTTPS + redirect
- [x] CI/CD auto rebuild BE CORS + FE bundle VITE_API_BASE_URL
- [x] E2E verified 3 domain live + preflight OK
Sub: `api.solutions.com.vn` · `admin.solutions.com.vn` · `eoffice.solutions.com.vn`. Old `.huypham.vn` vẫn fallback (chưa remove — Phase 9 Ops).
## 📝 Phase 7 — PE feature gap + Budget BE (Session 4 partial done)
### A. PE feature gap (3 task — phần lớn đóng ở Phase 8 S5)
- [x] **PE Workflow admin designer UI** `/system/pe-workflows/:typeCode` — done S5 (`5d94bb4`)
- BE `Application/PurchaseEvaluations/PeWorkflowAdminFeatures.cs` (mirror `WorkflowAdminFeatures.cs`)
- `Api/Controllers/PeWorkflowsController.cs`
- FE `fe-admin/src/pages/system/PeWorkflowsPage.tsx` + `PeWorkflowDesigner.tsx`
- Route `/system/pe-workflows/:typeCode` (menu PeWf_* + resolver đã sẵn)
- [x] **Ý kiến 4 phòng ban** (Phê duyệt / P.CCM / P.MuaHàng / SM-PM) ở tab Thông tin — done S5 (`5d94bb4`, Migration 15)
- Option A: 4 text field + signoff date + UserId vào header
- Option B: dùng `PurchaseEvaluationApprovals` với roleKind extra field
- **Chốt:** dùng Migration 15 `AddPurchaseEvaluationDepartmentOpinions` (separate table UNIQUE PEId+Kind, 4 box sign-off 2x2 grid OpinionBox như Excel mẫu) — tốt hơn Option A/B vì audit qua Changelog + Upsert preserve chữ ký cũ khi text-only edit.
- [ ] **Export phiếu PDF/Excel** — tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx` → carry over Phase 9 (user pending — không quan trọng lắm)
### B. Optional polish (carry over Phase 9 — làm khi UAT phát sinh)
- [ ] Auto-map PE Details → Contract per-type Details khi gen HĐ (phức tạp vì 7 schema khác nhau)
- [ ] Payment terms tách field từ JSON → 6 column (Tạm ứng/TT tạm/Quyết toán/Bảo hành/Hạn mức/Đánh giá)
- [ ] Matrix Quotes bulk paste từ Excel
- [ ] fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi"
### C. Ops (carry over Phase 9 — Hard blockers)
- [ ] Remove binding cũ `.huypham.vn` sau verify stable: `ssh vietreport-vps ; cd C:\solution-erp\scripts ; .\migrate-domains.ps1 -RemoveOld -SkipCert`
- [ ] win-acme scheduled task fix unhealthy (cert expire 2026-06-18)
- [ ] UAT thật 1 tuần với 2-3 user (30 demo user — 16 sample + 14 Solutions thật)
- [ ] SMTP config → Email outbox
- [ ] Rotate credentials (admin + 30 demo + SA + vrapp + JWT)
- [ ] Schedule SQL backup Task Scheduler
### D. Module Ngân sách (Budget) — Session 4 ✅ partial done
- [x] **Migration 14** `AddBudgets` — 4 bảng (Budgets/BudgetDetails/BudgetApprovals/BudgetChangelogs) + index BudgetId nullable trên Contract & PurchaseEvaluation
- [x] Domain — `Budget` (Header) + `BudgetDetail` (flat row) + `BudgetApproval` + `BudgetChangelog` + enum `BudgetPhase` 5-state + `BudgetEntityType` Header/Detail/Workflow
- [x] `BudgetPolicy.Default` hardcoded simple 3-step (Drafter→CCM→CEO + Reject từ ChoCCM/ChoCEO về DangSoanThao)
- [x] Application CQRS ~340 LOC — Create + UpdateDraft + Transition + List + GetDetail + Delete (only DangSoanThao/TuChoi) + Detail CRUD (auto-recompute TongNganSach) + ListChangelogs
- [x] `BudgetsController` 11 endpoint REST
- [x] Menu seed `Budgets` root + 3 leaf (Bg_List/Bg_Create/Bg_Pending) order=27 icon Wallet
- [x] **14 demo user Solutions thật** — PRO 5 + CCM 7 + ISO 1 + CEO 1 (pwd `User@123456`). Reconcile pattern (gotcha #38 4-field rename). Tổng 30 user (16 sample cũ + 14 Solutions thật mới).
Session log: `2026-04-28-chot-session-4-budget.md`.
### E. Pending migrations
- [ ] `AddPePaymentTermFields` (nếu chốt UX tách field — JSON blob → 6 column)
- [x] **`AddPurchaseEvaluationDepartmentOpinions`** ✅ migration 15 (S5)
- [ ] `AddBudgetCodeSequences` (nếu chốt format MaNganSach atomic — hiện Random.Shared)
- [ ] `AddBudgetVersionedWorkflow` (nếu user cần admin config UI thay vì hardcoded `BudgetPolicy.Default`)
## ✅ Phase 8 — Budget FE + PE/HD integration (Session 5 done)
### A. FE Budget pages — done ✅
- [x] `fe-admin/src/types/budget.ts` (BudgetPhase 5-state enum + DTO types)
- [x] `fe-admin/src/pages/budgets/BudgetsListPage.tsx` (3-panel `[340px_1fr_360px]` + filter Phase/Năm + ?phase=Pending alias + readOnly mode + BudgetDetailPage fullpage mobile)
- [x] `fe-admin/src/pages/budgets/BudgetCreatePage.tsx` (form Header — Tên/Năm/Dự án/Phòng ban/Mô tả)
- [x] `fe-admin/src/components/budgets/BudgetDetailTabs.tsx` (Section Thông tin Header + Section Hạng mục table CRUD inline auto-compute ThanhTien=KL×ĐG)
- [x] `fe-admin/src/components/budgets/BudgetWorkflowPanel.tsx` (Panel 3 timeline activePhases + nextPhases buttons + Dialog comment + Approvals/Changelog)
- [x] Mirror tất cả sang `fe-user/`
- [x] App.tsx routes `/budgets`, `/budgets/new`, `/budgets/:id` cả 2 app
- [x] Menu resolver `Bg_*` (Bg_List → `/budgets`, Bg_Pending → `/budgets?phase=Pending`, Bg_Create → `/budgets/new`)
### B. PE/Contract → Budget integration — done ✅
- [x] **PE form** + Select "Ngân sách" filter Phase=DaDuyet, ProjectId match, BE validate
- [x] **Contract form** (Header + Edit) tương tự, EditForm read-only link card khi !isDraft
- [x] PE Detail Hạng mục thêm cột "NS link · Δ" — match per-row qua `groupCode|itemCode` + footer aggregate (xanh dưới / đỏ vượt / xám khớp)
- [x] PE Detail UI restructure 4 section đánh số match form spec PHIẾU TRÌNH KÝ
- [x] BE: BudgetSummaryDto shared + Create/Update PE+Contract commands + BudgetId? + GetQueries load Budget
- [x] CreateContractFromEvaluation carry forward pe.BudgetId → contract.BudgetId
### C. PE Workflow Designer admin UI — done ✅
- [x] BE `PeWorkflowAdminFeatures.cs` ~250 LOC mirror Contract pattern
- [x] BE `PeWorkflowsController` 2 endpoint reuse policy `Workflows.*`
- [x] FE `PeWorkflowsPage.tsx` ~500 LOC + designer dialog (clone/edit/+Role/+User)
- [x] App.tsx route `/system/pe-workflows/:typeCode`
### D. Ý kiến 4 phòng ban — done ✅
- [x] Migration 15 `AddPurchaseEvaluationDepartmentOpinions` (UNIQUE PEId+Kind)
- [x] Domain entity + enum `PeDepartmentKind` (PheDuyet/Ccm/MuaHang/SmPm)
- [x] BE Upsert (sign=true → set SignedAt+UserId, sign=false giữ chữ ký cũ) + Delete + 2 endpoint
- [x] FE Section "5. Ý kiến 4 phòng ban (sign-off)" 2x2 grid OpinionBox
### E. Tests Phase 1-2-3mini + CI optimize — done ✅
- [x] **Phase 1**`tests/SolutionErp.Domain.Tests/` (xUnit + FluentAssertions 7.2): 54 test policy state machine (Contract WF + PE WF + Budget) + Registry + FromDefinition versioned + UserKindApprover
- [x] **Phase 2**`tests/SolutionErp.Infrastructure.Tests/` (EF SQLite + TestApplicationDbContext override `nvarchar(max) → TEXT`): 17 test code generator format + sequence + year boundary + persistence verify
- [x] **Phase 3 mini**`tests/.../Application/PeWorkflowAdminTests.cs`: 6 test CreatePeWorkflowDefinitionCommand versioning (auto-increment + deactivate cũ + EvaluationType independence + steps/approvers persistence)
- [x] CI gate `.gitea/workflows/deploy.yml` — 2 step `dotnet test` trước build, fail → no deploy
- [x] **Total 77 test pass / ~3s**
- [x] **CI manual checkout bypass github.com** — fix gotcha #39 (act_runner TCP timeout 21s)
- [x] **CI path filter docs-only skip** — gotcha #41 (paths-ignore behavior)
- [ ] **Tests Phase 3 full** — Opinion Upsert + Budget link validation (cần Identity UserManager setup helper)
- [ ] **npm junction cache CI optimize** (rollback ở `a21790d` — gotcha #40 chưa debug)
## 📝 Phase 9 — UAT + Ops + carry over (Session 6+ active)
### ✅ Session 14 done (2026-05-07) — PE 3-button workflow Duyệt/Trả lại/Từ chối (1 commit)
User chỉ thị thay 2-button approval (Duyệt + Reject mơ hồ) bằng 3 hành động rõ:
- Duyệt = forward
- Trả lại = về DangSoanThao + Drafter sửa (smart reject Mig 16 + clear N-stage rows + Drafter resume jump-back)
- Từ chối = Phase=TuChoi, phiếu khoá vĩnh viễn (17 handler Mig 16 lock edit)
- [x] **Domain `PurchaseEvaluationPolicy.cs`** — NccOnly + NccWithPlan thêm (X → TuChoi) transition cho mọi phase trung gian + FromDefinition expand step (trừ DangSoanThao) thêm (step.Phase → TuChoi).
- [x] **Service** `PurchaseEvaluationWorkflowService.TransitionAsync` — Reject branch tách 2 case: target=TuChoi giữ nguyên (no override / no RejectedFromPhase / no N-stage clear). target khác (DangSoanThao) → smart reject pattern.
- [x] **FE PeWorkflowPanel** (admin + user mirror) — render 3 button "✓ Duyệt → X" / "← Trả lại" / "✗ Hủy / Từ chối" + decision logic + dialog confirm với warning red (Cancel) / amber hint (SendBack).
- [x] **Tests** — rename Reject test target TuChoi→DangSoanThao + NEW Reject_To_TuChoi_Locks_Permanently + update NStage_Reject_Clears target. **95 → 96 test pass**.
**Defer Session 15+:**
- [ ] Task 2 sample seed N-stage (block trên DesignTime vs Runtime DB gotcha + API exit sớm khi DbInitializer seeding).
- [ ] Phase TraLai = 98 enum orphan — có thể remove migration nếu cleanup, ko gây hại.
### ✅ Session 13 done (2026-05-07) — Mirror N-stage Contract (Mig 20, 5 commit per-chunk + skip Chunk E auto-bind)
User chỉ thị mirror N-stage từ PE sang Contract. Budget defer (cần migration `AddBudgetVersionedWorkflow` trước — hardcoded `BudgetPolicy.Default` chưa có WorkflowDefinition entity). Pattern reusable đầy đủ từ PE.
- [x] **Chunk A (`951ffa3`)** Domain entity `WorkflowStepInnerStep` (Domain/Contracts/) + nav WorkflowStep.InnerSteps + ALTER ContractDeptApproval.InnerStepId + EF config FK Cascade Step / Restrict Dept+InnerStep + **Migration 20** `AddContractWorkflowInnerStepsAndAlterDeptApprovalUnique` GỘP 1 (CREATE TABLE WorkflowStepInnerSteps + ALTER InnerStepId + DropIndex old + Recreate filtered legacy `WHERE InnerStepId IS NULL` + new filtered N-stage `WHERE InnerStepId IS NOT NULL`).
- [x] **Chunk B (`04cf2a0`)** Application CQRS DTO mirror PE Chunk B — `WorkflowStepInnerStepDto` + extend `WorkflowStepDto` + `CreateWorkflowStepInnerStepInput` + extend `CreateWorkflowStepInput` (default null backward compat) + Validator child rules + Handler atomic batch insert.
- [x] **Chunk C (`e247b67`)** ContractWorkflowService refactor mirror PE — load InnerSteps eager + reject branch clear N-stage rows + dept block split hasInnerSteps→N-stage logic / else→legacy 2-stage. N-stage flow giống PE: match firstPending Order asc + (exact level OR canBypass + level≥), exact upsert / bypass batch upsert, recheck stillPending → BLOCK.
- [x] **Chunk D (`7c0772a`)** ContractNStageApprovalTests 6 test mirror PE pattern + helper SeedWorkflowDefinitionAsync 2 step adjacent (DangGopY + DangDamPhan) + SeedContractAsync với Project + Supplier + FakeChangelogService + FakeContractCodeGenerator stubs. Bug fix legacy fallback test: switched phase pair sang DangKiemTraCCM → DangTrinhKy + role CostControl khớp Standard.Transitions. **89→95 test pass**.
- [x] **Chunk E SKIP** — WorkflowsController auto-bind `[FromBody] CreateWorkflowDefinitionCommand` record qua JSON, no code change cần.
- [x] **Chunk F (current)** FE WorkflowsPage Designer extend mirror PeWorkflowsPage Chunk F: InnerStepDto + EditInnerStep types + copyFromDefinition include + departmentsList query + sub-section "Cấp duyệt nhỏ trong phòng" drag-list + button "+ Thêm cấp duyệt" emerald + payload include Order asc. Empty state hint fallback 2-cấp legacy. KHÔNG đụng fe-user (admin-only).
**Backward compat 100%**: workflow Contract no InnerSteps configured → service fallback legacy 2-stage Mig 16. Data legacy InnerStepId=null vẫn enforce unique cũ qua filtered index.
**Defer Session 14+:**
- [ ] **Budget N-stage** — cần migration `AddBudgetVersionedWorkflow` trước (4 bảng + ALTER Budget.WorkflowDefinitionId), sau đó migration `AddBudgetWorkflowInnerSteps`. User quyết riêng (feature mở rộng module lớn).
- [ ] schema-diagram.md §17 Mig 20 update — defer cron audit 2026-06-01.
- [ ] Skill ef-core-migration row Mig 20 — defer cron audit.
### ✅ Session 12 done (2026-05-07) — N-stage workflow approval (Mig 18+19, 6 commit per-chunk, PE-only)
User yêu cầu: workflow level cha (= phase) cấu hình được level con (Phòng × Cấp NV/PP/TP) sequential, mỗi cấp = 1 inner step duyệt riêng, có bypass cùng dept. 6 spec defaults chốt + 6 chunk per-commit.
- [x] **Chunk A (`13ab533`)** Domain + Migration 18 `AddPeWorkflowInnerStepsAndPositionLevel` — enum PositionLevel (NV/PP/TP), entity PurchaseEvaluationWorkflowStepInnerStep + nav, User.PositionLevel int? + PEDeptApproval.InnerStepId Guid?. EF config FK Cascade Step / Restrict Dept+InnerStep. 3-file rule.
- [x] **Chunk B (`0e56bd0`)** Application CQRS DTO — PeWorkflowStepInnerStepDto + extend PeWorkflowStepDto + CreatePeWorkflowStepInnerStepInput (default null backward compat) + Validator child rules + Handler atomic batch insert + UserDto +PositionLevel + SetUserPositionLevelCommand mirror SetBypassReview pattern.
- [x] **Chunk C (`0c62e24`)** Service N-stage logic + **Migration 19** `AlterPeDeptApprovalsUniqueFilteredForInnerSteps` (filtered unique legacy `WHERE InnerStepId IS NULL` + new N-stage `WHERE InnerStepId IS NOT NULL`). PurchaseEvaluationWorkflowService refactor — load InnerSteps eager, reject clear N-stage rows tại fromPhase, dept block split hasInnerSteps→N-stage / else→legacy 2-stage. N-stage: match firstPending (Order asc IsRequired) same dept + (exact level OR canBypass + level≥), exact upsert 1 row InnerStepId, bypass batch upsert NV+PP+TP cùng dept ≤ actor (audit IsBypassed cho cấp dưới), recheck stillPending → BLOCK + log "duyệt cấp X (còn Y pending)".
- [x] **Chunk D (`3d76c6b`)** Tests N-stage 6 test mới (FirstInner_NV_blocks / All_3_levels_sequential_pass / TP_bypass_skips_lower / Wrong_dept_403 / Reject_clears_rows / Legacy_fallback_no_inner) + IdentityFixture extend `+positionLevel` + helper SeedWorkflowDefinitionAsync 2 step adjacent. **83→89 test pass**.
- [x] **Chunk E (`83ffabd`)** API `PATCH /users/{id}/position-level` mirror SetBypassReview + body `{positionLevel:int?}` + Authorize Users.Update.
- [x] **Chunk F (current)** FE-Admin types/users.ts + positionLevel field + PositionLevel const + Label/Short maps. PeWorkflowsPage Designer extend InnerStep DTO + EditInnerStep type + sub-section "Cấp duyệt nhỏ trong phòng" drag-list { Phòng × Cấp + required } + button "+ Thêm cấp duyệt" emerald + departmentsList query + payload include. UsersPage column "Cấp" badge NV/PP/TP emerald + action button cycle null→1→2→3→null. KHÔNG đụng fe-user (admin-only).
**Backward compat 100%:** workflow no InnerSteps configured → service fallback legacy 2-stage Mig 16. Data legacy rows InnerStepId=null vẫn enforce unique cũ qua filtered index.
**Defer Session 13+:**
- [ ] Mirror Contract + Budget N-stage (sau khi UAT PE 2-3 tuần ổn). Pattern lặp lại Domain entity + Service logic + Tests reusable từ PE.
- [ ] Seed/migrate User.PositionLevel cho 30 demo user (hiện chỉ admin set qua UsersPage cycle).
- [ ] schema-diagram.md §15 Mig 18 + §16 Mig 19 update (defer cron audit 2026-06-01 — small drift).
- [ ] Skill ef-core-migration row Mig 18+19 (defer cron audit).
- [ ] Skill contract-workflow N-stage cross-ref section (defer cron audit).
### ✅ Session phase 2 done (2026-05-08 00:30) — B12-B14 PE detail polish iterate (3 commit FE-only)
User UAT iteration tiếp sau wrap-up `6e7a6db`. Áp rule strict verify khi rename/remove (lesson hotfix CI).
- [x] **B12 (`378c993`)** — "Lưu" no-close + "Xóa phiếu" red bottom (CHỈ Bản nháp soft-delete) + bỏ header bar workspace "Sửa header"/"Xóa"/"Đóng" + Section 4 column header `s.supplierName` + Section 3 chặn xóa NCC khi có quotes
- [x] **B13 (`e320027`)** — InfoTab `useEffect` re-trigger edit khi pencil click phiếu khác + sync values + Pencil "sáng lên" active state (`bg-brand-100 + ring`) khi `editingRowId === p.id` + wire `editingRowId` từ Workspace → PeListPanel
- [x] **B14 (`d2306b8`)** — QuoteDialog bỏ checkbox `isSelected` (consolidate winner ở Section 2.a) + winner column Section 4 LUÔN highlight emerald (header `✓ ` prefix + cells full column) + QuoteDialog full overlay loading + spinner + NccSelectorRow inline spinner "Đang chọn NCC + sync cột giá Section 4…"
**Verify:** `npm run build` × 2 app pass · `dotnet test` 83 pass · push OK.
### ✅ Session S10-11+++++++ done (2026-05-07) — PE Workspace UX overhaul đầy đủ (23 commit)
User UAT live mode iterate liên tục. Áp rule `feedback_uat_skip_verify` (memory): skip dotnet test + npm build sau mỗi chunk, push ngay. Lesson hotfix CI `0ae3fe2`: rename/remove → BẮT BUỘC `npm run build` 1 lần trước commit.
**11 batch deliverable** (chi tiết narrative đầy đủ xem `STATUS.md` Recently Done row đầu tiên + session log `2026-05-07-2359-pe-workspace-ux-overhaul.md`):
- [x] **B1 (S10)** PE Thao tác 2-panel workspace mirror HĐ Thầu phụ pattern (4 commit `ee0d360``d04bd88`)
- [x] **B2 (S11)** Migration 17 `AddManualBudgetFieldsToPeAndContract` — 4 cột manual budget cho PE + HĐ + App CQRS + FE (5 commit `ecd5f7e``bf17740`)
- [x] **B3 (S11+)** BudgetFieldRow inline editor Section 2.b — toggle + Select OR 2 input + auto-detect mode (3 commit `19712d8``7f38c02`)
- [x] **B4 (S11++)** InfoTab inline edit Section 1 + PeListPanel pencil hover + URL `?editHeader=1` (3 commit `5a89dd2``cb0598d`)
- [x] **B5 (S11+++)** Workspace "new" sectioned create view 5 sections + LockedHint S3-5 (1 commit `66fa469`)
- [x] **B6 (S11++++)** Pe_*_List Danh sách disable toàn bộ tương tác (2 commit `7dfeb1a`+`a1665ee`)
- [x] **B7 (S11+++++)** Workspace "new" lock Loại quy trình theo URL + Select preset Điều khoản TT (1 commit `18ebfa1`)
- [x] **B8 (S11++++++)** PE Display status meta — Bản nháp / Đã gửi duyệt / Đã duyệt / Từ chối (1 commit `0c5db13`)
- [x] **B9 (S11+++++++)** Phase TraLai = 98 + pencil always visible + edit gating + editableOnly filter (1 commit `d15398f`)
- [x] **B10 (hotfix CI)** TS errors `forcedPhase` rename + unused `PurchaseEvaluationType` import (1 commit `0ae3fe2`)
- [x] **B11 (last)** PE detail polish — NCC selector dropdown + Section 3 winner protect + Bottom action bar Lưu/Gửi Duyệt (1 commit `4c0625c`)
**Defer cho Session 12+** (cần explicit UAT trigger):
- [ ] Workflow transition vào TraLai — BE workflow service chưa wire button "Trả lại" cho approver. FE đã ready accept.
- [ ] BE multi-phase filter param `?phases=2,3,4,5,6` — cho FE display status "Đã gửi duyệt" precision filter.
- [ ] Section 5 Opinion sign trong Duyệt mode — verify nếu UAT user cần sign opinions ở leaf "Duyệt" (hiện code path opinionsReadOnly = readOnly khi mode='detail').
### ✅ Session 11 done (2026-05-07) — Migration 17 manual budget fields PE + HĐ
(Note: Session 11 + 11+ + 11++ + ... đã merge vào batch S10-11+++++++ ở trên — đây là sub-row chronological, KHÔNG cắt narrative cũ.)
### ✅ Session 9 done (2026-05-04) — Chunk E-bis complete (FE 2-stage + HĐ/Budget mirror + 6 test)
User chỉ thị "làm hết cho xong tính năng luôn" sau Session 8 close bug fix anh Kiệt phía BE PE. Session 9 đóng toàn bộ pending Chunk E-bis (defer từ session 8).
- [x] **Chunk E2 — FE Workflow Panel PE** hiển thị progress 2-stage timeline per phase × dept (commit `f8eebd5`). Component `DeptApprovalsSection` group by phase × dept, highlight amber khi current phase có Review nhưng chưa Confirm, badge fuchsia "bypass" khi NV.CanBypassReview. Cả fe-admin + fe-user (rule §3.9).
- [x] **Chunk E3 — FE UserManager toggle CanBypassReview** (commit `4380bdc`). UserDto BE thêm field `CanBypassReview`. UsersPage column "Bypass" + button ShieldCheck (icon highlight fuchsia khi enabled). Endpoint backend đã có sẵn từ Session 8 Chunk E1. fe-user KHÔNG có UsersPage (admin-only).
- [x] **Chunk E4 — HĐ 2-stage logic mở rộng** (commit `b6f5a16`). ContractWorkflowService thêm `UserManager<User>` DI + mirror toàn bộ logic 2-stage từ PE service (sau policy guard, trước gen mã HĐ). ContractDepartmentApprovalFeatures.cs (List query mirror PE pattern). Endpoint `GET /contracts/{id}/department-approvals`. FE WorkflowHistoryPanel section "Tiến trình duyệt 2-cấp phòng ban" insert giữa WorkflowSummaryCard và Lịch sử duyệt.
- [x] **Chunk E5 — Budget 2-stage logic mở rộng** (commit `1fc439b`). TransitionBudgetCommandHandler thêm `INotificationService` + `IDateTime` DI + mirror 2-stage logic. BudgetDepartmentApprovalFeatures.cs + endpoint. FE BudgetWorkflowPanel section "Tiến trình duyệt 2-cấp phòng ban". Note: low-priority cho Budget (ít user duyệt budget per dept) nhưng giữ consistent UX 3 module.
- [x] **Chunk E6 — 6 test 2-stage + IdentityFixture helper** (commit `8353fe8`). IdentityFixture (Common/) setup ServiceProvider với Identity stack đầy đủ — DbContext SQLite shared connection + AddIdentityCore<User> + AddRoles<Role> + AddEntityFrameworkStores. Single shared scope cho fixture lifetime đảm bảo DbContext + UserManager đồng instance. Helper `CreateUserAsync(email, name, deptId, roles, canBypassReview)` reusable. 6 test PeTwoStageApprovalTests: NV_Review_Blocks_Phase_Transition (đóng bug anh Kiệt — test chính xác) / TPB_Confirm_After_NV_Review_Allows_Transition / NV_With_BypassReview_Allows_Transition_With_IsBypassed_True / Admin_Skips_TwoStage_Logic_Entirely / Reject_Sets_RejectedFromPhase_And_Forces_DangSoanThao / Resume_After_Reject_Jumps_Back_To_RejectedPhase. Tests Contract + Budget 2-stage skipped — logic identical PE, ROI thấp; pattern reusable nếu UAT phát hiện regression riêng. **Total 77→83 test pass**.
Stats sau session 9:
- BE LOC: ~13750 → ~14400 (+650, gồm Contract + Budget 2-stage logic + 2 List feature + Test fixture)
- API endpoints: ~131 → ~133 (+2 List dept-approvals HĐ/Budget)
- Tests: 77 → **83** (+6 PE 2-stage)
- Schema không đổi (Migration 16 đã có sẵn từ session 8)
- 5 commit per-chunk pushed → 1 CI run trigger qua HEAD = `8353fe8`
Pending còn lại Phase 9: chỉ Hard blockers Ops (UAT thật / SMTP / Rotate creds / SQL backup). Feature 2-stage đầy đủ.
### ✅ Session 8 done (2026-05-04) — Migration 16: 2-stage dept approval + smart reject + lock edit
**Bối cảnh:** Anh Kiệt (FDC) báo bug PE workflow: NV.PRO tạo phiếu → duyệt được hết phase. Phân quyền sai vì policy chỉ check role, không check Stage 2-cấp.
**3 ràng buộc gộp 1 migration:**
- [x] **Lock edit khi Phase != DangSoanThao** — 17 handler thêm guard (Contract Detail × 15 qua helper `EnsureContractType`, PE Detail × 5 qua helper mới `PurchaseEvaluationDraftGuard`, Budget Detail × 3 inline). KHÔNG lock Comment + Attachment + Opinion (workflow design intent).
- [x] **Smart reject + Resume**`Decision=Reject``entity.RejectedFromPhase = currentPhase` + force `targetPhase=DangSoanThao`. Resume: `Drafter trình từ DangSoanThao + RejectedFromPhase != null → jump tới phase đã reject + clear field`. Bypass policy guard ở resume.
- [x] **2-stage dept approval (PE only v1)** — User.DepartmentId != null + role guard:
- DeptManager (TPB) → Stage=Confirm trực tiếp
- User.CanBypassReview=true → Stage=Confirm + IsBypassed=true
- Else (NV) → Stage=Review only, BLOCK transition cho đến khi TPB confirm
- Schema: 3 bảng `*DepartmentApprovals` UNIQUE (TargetId, Phase, Dept, Stage)
- [x] **Migration 16** `AddTwoStageDeptApprovalAndSmartReject` — 4 ALTER + 3 CREATE TABLE + 12 indexes + FK Cascade
- [x] **Endpoint mới**: `GET /api/purchase-evaluations/{id}/department-approvals` (List), `PATCH /api/users/{id}/bypass-review` (toggle)
- [x] **Notify TPB cùng dept** khi NV review (best effort, fail non-critical)
- [x] **Verify**: Build pass + 77 test pass + Migration applied LocalDB OK + schema verified qua sqlcmd
- [x] 5 commit per-chunk: `5fe61cc` (A) · `14f3c9f` (B) · `9747f8c` (C) · `a532ba6` (D) · current (E1)
Session log: `2026-05-04-1230-chot-session-8-2-stage-dept-approval.md`.
**Pending Chunk E-bis** ✅ TẤT CẢ DONE ở Session 9 (xem section trên):
- [x] FE Workflow Panel hiển thị progress 2-stage timeline (E2 — PE) + HĐ (E4) + Budget (E5)
- [x] FE UserManager toggle `CanBypassReview` checkbox (E3)
- [x] HĐ 2-stage mở rộng (E4)
- [x] Budget 2-stage mở rộng (E5)
- [x] Tests 2-stage logic Service-layer (E6 — 6 test PE + IdentityFixture)
### ✅ Session 6 done (2026-04-30 — pure docs work)
- [x] **MD audit + compact** — STATUS -27%, HANDOFF -32%, migration-todos -35%, archive 51 row Phase 0-7 cũ
- [x] **3 skill refresh**`form-engine` Phase 2 MVP → Tier 3 feature-complete, `permission-matrix` 12 menu → ~60 + inheritance roots, `ef-core-migration` 24 DbSet → 52 bảng
- [x] **Rule mới rules.md §7** — Khi nào viết test (timing rule 5-row table)
- [x] **Rule mới rules.md §6.4** — Audit + compact MD định kỳ (cadence + checklist + anti-pattern)
- [x] **rules.md §9.4** — Mở rộng skill audit cross-ref §6.4
### ✅ Session 9+ housekeeping done (2026-05-04 — sau Session 9 close)
- [x] **Audit định kỳ 2026-05** — combined skill + doc drift (commit `7dc0233`). Log `skill-audit-2026-05.md`.
- [x] **Optional polish — fe-user Inbox PE section** (commit `332a90f`). HĐ + PE 2 section trong InboxPage.
- [x] **User Manual 7 file rewrite compact** (commit `16c2c9c`). End-user style: bỏ field/error tables, giữ numbered steps đơn giản. ~86 KB total.
### ✅ Session 11 done (2026-05-07) — Migration 17 manual budget fields PE + HĐ
User feedback: PE/HĐ link Budget Select chỉ Phase=DaDuyet → user phải break flow tạo Budget approved trước. Solution: toggle "Nhập tay" + 2 input field fallback (Tên text + Số tiền number) lưu trên entity, KHÔNG cần Budget entity. Mirror logic PE ↔ HĐ (Q3 chốt).
- [x] **Chunk 1 Domain+Infra** (commit `ecd5f7e`) — Migration 17 `AddManualBudgetFieldsToPeAndContract` 4 ALTER + 2 entity property + 2 EF config (HasMaxLength + HasPrecision). Applied LocalDB. 3-file rule.
- [x] **Chunk 2 App CQRS** (commit `0f7901c`) — Create/Update PE + Contract commands + Validator (>=0 when has value) + Handlers + DTO + diff log audit + CreateContractFromEvaluation carry forward.
- [x] **Chunk 3 FE-Admin** (commit `bab5031`) — types +2 field, PeHeaderForm toggle + 2 input + payload conditional, PeDetailTabs Section "b. Ngân sách" fallback display + badge "nhập tay", refactor PurchaseEvaluationCreatePage wrap PeHeaderForm DRY (222→30 LOC), ContractCreatePage NewForm + EditForm cùng pattern + read-only branch khi !isDraft.
- [x] **Chunk 4 FE-User mirror** (commit `14f8d9d`) — 6 file y hệt content (rule §3.9).
- [x] **Chunk 5 Docs** (commit current) — STATUS row + HANDOFF TL;DR Session 11 + session log `2026-05-07-2300-pe-hd-manual-budget-mig17.md`.
- [x] **Verify**: dotnet build + 83 test pass mỗi chunk · npm build fe-admin + fe-user pass · LocalDB migration applied.
- [x] **KHÔNG đụng** Budget entity / Phase=DaDuyet validation (giữ invariant). Manual fields chỉ là fallback display/note, KHÔNG join với Budget.Details cho per-row comparison ở PE matrix Section 4.
### ✅ Session 10 done (2026-05-07) — PE "Thao tác" 2-panel workspace
User chỉ thị restructure menu PE: leaf "Thao tác" (Pe_*_Create) từ page Create header riêng `/new` sang workspace 2-panel mirror pattern HĐ Thầu phụ ContractCreatePage. Spec chốt 5 câu trước code (xem session log đầy đủ rationale).
- [x] **Chunk 1 fe-admin** (commit `ee0d360`) — `PeListPanel.tsx` (~180 LOC pure picker reuse + sticky "+ Thêm mới") + `PeHeaderForm.tsx` (~210 LOC extract) + `PurchaseEvaluationWorkspacePage.tsx` (~120 LOC 2-panel). PeDetailTabs thêm prop `mode?: 'detail' \| 'workspace'` + Section 5 hint amber + force opinionsReadOnly. Layout resolver remap. App.tsx route mới.
- [x] **Chunk 2 fe-user mirror** (commit `ecf3c59`) — 6 file y hệt content (rule §3.9 duplicate có chủ đích).
- [x] **Chunk 3 docs** (commit `7e3cfa5`) — STATUS Recently Done + HANDOFF TL;DR Session 10 + session log `2026-05-07-2100-pe-workspace-2panel.md`. KHÔNG update skill (per §9.5 — không drift đáng audit, FE pure refactor).
- [x] **Verify**: 2 build (fe-admin + fe-user) pass + dotnet test 83 pass mỗi chunk. Route `/new` cũ giữ tồn tại cho deep-link "Sửa header" button.
- [x] **KHÔNG đụng** BE / migration / schema / endpoint / test count.
### A. Hard blockers (chờ user / ops)
- [ ] UAT thật 1 tuần với 2-3 user (30 demo: 16 sample + 14 Solutions thật)
- [ ] SMTP config → Email outbox (BLOCKED chờ user cấp host/user/pass)
- [ ] Rotate credentials (admin + 30 demo + SA + vrapp + JWT secret + Gitea runner token)
- [ ] Schedule SQL backup daily Task Scheduler
### B. PE feature gap còn lại
- [ ] Export phiếu PDF/Excel — `IDocumentConverter` + template `PE-TrinhDuyet.docx` (user pending — không quan trọng lắm)
### C. Optional polish (làm khi UAT phát sinh)
- [ ] Budget MaNganSach atomic sequence + migration `AddBudgetCodeSequences`
- [ ] Budget versioned workflow + migration `AddBudgetVersionedWorkflow`
- [ ] Payment terms PE tách field (JSON → 6 column)
- [ ] Auto-map PE Details → Contract Details khi gen HĐ
- [ ] Matrix Quotes bulk paste từ Excel
- [x] **fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi"** — done 2026-05-04 (commit `332a90f`). useQuery thứ 2 cho `/pe/inbox` + Panel 1 chia 2 section sticky header. Click PE → navigate `/purchase-evaluations/:id` (page riêng).
### D. Tests Phase 3-5 (làm khi gặp bug recurring để justify ROI)
- [ ] **Phase 3** — Application handler tests (CQRS + EF InMemory) ~15 test
- [ ] **Phase 4** — API smoke tests (WebApplicationFactory) ~7 test
- [ ] **Phase 5** — FE Vitest cho lib utility (queryMatches, fmtMoney) ~10 test
### E. Ops chưa xong
- [ ] Remove binding cũ `.huypham.vn` sau verify stable
- [ ] win-acme scheduled task fix unhealthy (cert expire 2026-06-18)
## 🔁 Skill governance (recurring)
Quy tắc: `docs/rules.md §9`. Audit định kỳ mỗi đầu tháng — workflow §9.4.
- [x] **Setup ban đầu** — 6 skill (3 domain + 3 ops), rules §9 ← `661f859`
- [ ] **Audit 2026-05-01** — log `docs/changelog/skill-audit-2026-05.md`
- [ ] **Audit 2026-06-01**
- [ ] **Audit 2026-07-01**
Cron task `solution-erp-skill-audit-monthly` fire 9:00 AM ngày 1 mỗi tháng.
## 📦 Post-launch (Phase 10+ — future)
- [ ] **Email outbox** (MailKit + SMTP) — blocked chờ SMTP config
- [ ] E-signature integration (VNPT CA hoặc FPT CA)
- [ ] Tích hợp Bravo / SAP ERP import NCC
- [ ] Mobile app (React Native?) cho BOD duyệt ngoài giờ
- [ ] AI: gợi ý điền form dựa HĐ cũ, OCR scan HĐ đối tác
- [ ] Multi-tenant nếu có công ty thứ 2