[CLAUDE] Docs: chốt session 5 — Budget FE + PE feature complete + Tests Phase 1-2 + CI gate
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Failing after 22s

Session 5 (29/04) — 6 commit feature + 1 chốt MD này.

==== Stats sau session 5 ====
- 52 DB tables (+1 PEDeptOpinions)
- 15 migrations (+`AddPurchaseEvaluationDepartmentOpinions`)
- ~128 API endpoints (+4)
- ~31 FE pages (+5 Budget + 1 PeWorkflowsPage)
- 71 unit test pass (54 Domain + 17 Infra) — CI gate live, fail → no deploy
- ~13050 BE LOC (+1300)
- 30 demo user, 38 gotchas, 6 skill (no change)

==== MD touched ====
- STATUS.md: header Phase 8 + 6 row Recently Done session 5 + cumulative cột S5 + In Progress S6 (Hard blockers + Optional polish + Tests Phase 3-5 + Ops)
- HANDOFF.md: TL;DR 6 milestone S5 + Cảnh báo S6 (CI test gate workflow mới) + Priority 0 S6 (UAT + Ops focus) + Phase status table cập nhật
- migration-todos.md: Phase 8 done với A/B/C/D/E (FE Budget / PE-HD integration / PE WF Designer / Ý kiến 4 PB / Tests Phase 1-2) + Phase 9 active (UAT + Ops + carry over)
- architecture.md: §11 Testing strategy mới (test pyramid bottom-heavy + stack + CI gate + phased priority + quy tắc bổ sung mỗi feature)
- database/schema-diagram.md: Migration 15 row + total 52 tables + §13 PE Department Opinion (1 bảng UNIQUE PEId+Kind + Upsert behavior + SQL DDL)
- ef-core-migration SKILL: migration 15 entry + 52 tables total + Phase 8 update note
- CLAUDE.md (root): modules table + Tests row + scope `Tests` + Tests section mới + count update 15/52
- docs/CLAUDE.md: 7 module bullet + ERD 52 bảng + Roadmap Phase 8 done + Phase 9 active S6
- memory project_solution_erp.md: Phase 8 summary + Session 6 priority + workflow user mới (dotnet test → commit → push)
- session log 2026-04-29-chot-session-5-budget-fe-pe-tests.md (NEW — 10+ section detail)

==== Verify ====
- dotnet test SolutionErp.slnx → 71 pass / 2s
- git status clean sau commit này

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-29 13:50:54 +07:00
parent df5988b7a9
commit 52999f33fa
9 changed files with 565 additions and 133 deletions

View File

@ -1,6 +1,6 @@
---
name: ef-core-migration
description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 14 migration sẵn (Init → AddBudgets). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
description: Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 15 migration sẵn (Init → AddPurchaseEvaluationDepartmentOpinions). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
when-to-use:
- "thêm migration"
- "EF Core migration"
@ -16,7 +16,7 @@ when-to-use:
> **Context:** .NET 10 + EF Core 10 + SQL Server. DbContext: `ApplicationDbContext` ở `Infrastructure/Persistence/`. Startup: `SolutionErp.Api`.
## Migration history (14 migration hiện có)
## Migration history (15 migration hiện có)
| # | Name | Tables added / changed |
|---|---|---|
@ -34,8 +34,9 @@ when-to-use:
| **12** | **`AddPurchaseEvaluations`** | **10 bảng module Duyệt NCC: PurchaseEvaluations + Suppliers + Details + Quotes + Approvals + Changelogs + Attachments + WorkflowDefinitions/Steps/StepApprovers** |
| **13** | **`AddPurchaseEvaluationCodeSequences`** | **1 bảng `PurchaseEvaluationCodeSequences` (Prefix PK, LastSeq) — atomic sequence cho MaPhieu format `PE/{YYYY}/{A\|B}/{Seq:D3}`** |
| **14** | **`AddBudgets`** | **4 bảng module Ngân sách: Budgets + BudgetDetails + BudgetApprovals + BudgetChangelogs. + nullable FK index `Contracts.BudgetId` & `PurchaseEvaluations.BudgetId`** |
| **15** | **`AddPurchaseEvaluationDepartmentOpinions`** | **1 bảng `PurchaseEvaluationDepartmentOpinions` (UNIQUE PEId+Kind, max 1 row mỗi PeDepartmentKind per phiếu — Phê duyệt/Ccm/MuaHang/SmPm)** |
Total: **51 bảng** dbo + `__EFMigrationsHistory`. Xem `docs/database/schema-diagram.md` ERD đầy đủ.
Total: **52 bảng** dbo + `__EFMigrationsHistory`. Xem `docs/database/schema-diagram.md` ERD đầy đủ.
**Phase 7 pending:**
- `AddPePaymentTermFields` — tách `PurchaseEvaluations.PaymentTerms` JSON thành 6 column riêng
@ -45,6 +46,11 @@ Total: **51 bảng** dbo + `__EFMigrationsHistory`. Xem `docs/database/schema-di
- `AddBudgetCodeSequences` — atomic sequence khi format MaNganSach chốt (mirror Contract/PE pattern, hiện Random.Shared)
- `AddBudgetVersionedWorkflow` — nếu Solutions cần admin config UI (3 bảng `BudgetWorkflowDefinitions/Steps/StepApprovers` + `Budgets.WorkflowDefinitionId?`)
**Phase 8 update (2026-04-29 Session 5):**
- Migration 15 `AddPurchaseEvaluationDepartmentOpinions` — 1 bảng riêng (UNIQUE PEId+Kind), max 4 row mỗi phiếu cho 4 phòng ban (Phê duyệt/Ccm/MuaHang/SmPm). UPDATE in-place khi user đổi ý, audit qua Changelog.
- Tests Phase 1-2 + CI gate live: `tests/SolutionErp.Domain.Tests/` (54 test policy state machine) + `tests/SolutionErp.Infrastructure.Tests/` (17 test code generator). Total 71 test pass / ~2s. CI fail → no deploy.
- Migration verify pattern thêm: chạy `dotnet test` local trước mỗi migration commit để chống regression policy/codegen.
**Phase 7 update (2026-04-28):**
- Migration 14 `AddBudgets` — 4 bảng (Budgets/Details/Approvals/Changelogs) + 2 cột `BudgetId?` thêm vào Contracts & PurchaseEvaluations. Workflow hardcoded `BudgetPolicy.Default` (chưa versioned).
- 14 demo user thật `@solutions.com.vn` qua `SeedDemoUsersAsync` reconcile (PRO 5 + CCM 7 + ISO 1 + CEO 1).

View File

@ -50,19 +50,20 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser
- Audit fields: `CreatedAt`, `UpdatedAt`, `CreatedBy`, `UpdatedBy` (`BaseEntity`)
- Soft delete: `IsDeleted`, `DeletedAt`, `DeletedBy` (`AuditableEntity`)
- Migrations: `dotnet ef migrations add <Name> --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api`
- **Hiện có 14 migration → 51 bảng** (Phase 7 thêm migration 14 `AddBudgets`4 bảng + 2 cột `BudgetId?` nullable trên Contracts/PurchaseEvaluations)
- **Hiện có 15 migration → 52 bảng** (Phase 8 thêm migration 15 `AddPurchaseEvaluationDepartmentOpinions`1 bảng UNIQUE PEId+Kind cho 4 box sign-off "Phê duyệt/CCM/MuaHàng/SM-PM")
### Modules
| Module | Namespace | Migration | Trạng thái |
|---|---|---|---|
| Contract (HĐ) | `Domain/Contracts/` | 1-11 | Feature-complete (7 ContractType × 9 phase) |
| PurchaseEvaluation (Duyệt NCC tiền-HĐ) | `Domain/PurchaseEvaluations/` | 12, 13 | UX polish complete — feature gap: Designer UI / Ý kiến 4 PB / Export PDF |
| **Budget (Ngân sách dự án)** | `Domain/Budgets/` | **14** | **BE done — FE pages chưa làm (Priority 0 session 5)** |
| PurchaseEvaluation (Duyệt NCC tiền-HĐ) | `Domain/PurchaseEvaluations/` | 12, 13, 15 | Feature-complete — chỉ Export PDF còn pending (không quan trọng) |
| Budget (Ngân sách dự án) | `Domain/Budgets/` | 14 | **Feature-complete** — BE + FE 3-panel + integration với PE/HD |
| Master (Supplier/Project/Department) | `Domain/Master/` | 2, 10 | Feature-complete |
| Identity (User/Role/Permission/MenuItem) | `Domain/Identity/` | 1, 3, 11 | Feature-complete (30 demo user — 16 sample + 14 Solutions thật) |
| Forms (Template + Clause) | `Domain/Forms/` | 4 | Feature-complete |
| Notifications | `Domain/Notifications/` | 6 | In-app + SignalR OK, email SMTP TODO |
| **Tests** | `tests/SolutionErp.{Domain,Infrastructure}.Tests/` | — | **71 test pass** (Phase 1: 54 Domain policy + Phase 2: 17 Infra code generator) — CI gate live |
### Commit convention
@ -70,7 +71,23 @@ Kiến trúc: **.NET 10 Clean Architecture + 2 React FE (admin + user) + SQL Ser
[CLAUDE] <scope>: <imperative message>
```
**Scope:** `Contract` · `PurchaseEvaluation` · `Budget` · `Form` · `Workflow` · `Supplier` · `Auth` · `Admin` · `Api` · `App` · `Domain` · `Infra` · `FE-Admin` · `FE-User` · `Docs` · `CICD` · `Scripts` · `Skill`
**Scope:** `Contract` · `PurchaseEvaluation` · `Budget` · `Form` · `Workflow` · `Supplier` · `Auth` · `Admin` · `Api` · `App` · `Domain` · `Infra` · `FE-Admin` · `FE-User` · `Tests` · `Docs` · `CICD` · `Scripts` · `Skill`
## 🧪 Tests (Phase 8 — Session 5)
```
tests/
├── SolutionErp.Domain.Tests/ (54 test policy state machine - Phase 1)
└── SolutionErp.Infrastructure.Tests/ (17 test code generator format/sequence - Phase 2)
```
**71 unit test pass** / ~2s. CI gate `.gitea/workflows/deploy.yml``dotnet test` fail → no deploy.
```bash
dotnet test SolutionErp.slnx # chạy cả 2 test project
```
**Quy tắc:** mỗi feature mới có guard logic / business rule → thêm test trước khi commit. Bug found in production → 1 regression test added before merge. Detail xem `docs/architecture.md §11`.
## 🛠️ Skills (.claude/skills/) — 6 skill PHẢI dùng khi task khớp
@ -98,7 +115,7 @@ Quy tắc: KHÔNG bulk-clone repo skill 3rd party. Chỉ thêm skill PROJECT-SPE
| [`docs/workflow-contract.md`](docs/workflow-contract.md) | State machine 9 phase HĐ + role matrix |
| [`docs/forms-spec.md`](docs/forms-spec.md) | Catalog 8 form + quy định mã HĐ RG-001 |
| [`docs/database/database-guide.md`](docs/database/database-guide.md) | DB conventions + migration workflow + cheatsheet |
| [`docs/database/schema-diagram.md`](docs/database/schema-diagram.md) | ⭐ ERD + luồng DB + data flow 51 table (+ §11 PE module + §12 Budget module) |
| [`docs/database/schema-diagram.md`](docs/database/schema-diagram.md) | ⭐ ERD + luồng DB + data flow 52 table (+ §11 PE module + §12 Budget module + §13 PEDeptOpinions) |
| [`docs/flows/README.md`](docs/flows/README.md) | Index 6 flow (auth, permission, contract, form, SLA) |
| [`docs/gotchas.md`](docs/gotchas.md) | ⭐ 26 bẫy đã gặp — đọc trước khi debug tương tự |
| [`.claude/skills/`](.claude/skills/README.md) | 6 skill: contract-workflow, form-engine, permission-matrix, dependency-audit-erp, ef-core-migration, iis-deploy-runbook |

View File

@ -9,8 +9,9 @@
2. Workflow 9 phase trình ký HĐ (state machine + role guard + SLA auto-approve + versioned WF per ContractType)
3. Tự gen mã HĐ theo Quy định SOL-CCM-RG-001
4. Dashboard báo cáo HĐ theo NCC, dự án, trạng thái, giá trị
5. **Module Duyệt NCC (Phase 6)** — phiếu trình duyệt so sánh giá tiền-HĐ, 2 quy trình A/B, kế thừa HĐ 1-click khi DaDuyet
6. **Module Ngân sách (Phase 7)** — 4 bảng quản lý ngân sách dự án, workflow simple 3-step, link nullable Contract & PE để đối chiếu chi phí
5. **Module Duyệt NCC (Phase 6-8)** — phiếu trình duyệt so sánh giá tiền-HĐ, 2 quy trình A/B versioned admin-config, kế thừa HĐ 1-click khi DaDuyet, Ý kiến 4 phòng ban sign-off
6. **Module Ngân sách (Phase 7-8)** — 4 bảng quản lý ngân sách dự án, workflow simple 3-step, integration link với Contract & PE + cột "So với ngân sách" PE matrix
7. **Test gate (Phase 8)** — xUnit Domain policy + Infra code generator, CI fail-fast no deploy
## 2. Kiến trúc tổng thể
@ -60,13 +61,13 @@ SOLUTION_ERP/
│ ├── HANDOFF.md ⭐ brief 5 phút cho session tiếp
│ ├── PROJECT-MAP.md bản đồ tổng quan
│ ├── rules.md coding conventions
│ ├── architecture.md layered + PE §9 + Budget §10
│ ├── architecture.md layered + PE §9 + Budget §10 + Testing §11
│ ├── gotchas.md 38 pitfall đã gặp
│ ├── forms-spec.md 8 form catalog + RG-001
│ ├── workflow-contract.md 9 phase HĐ + role matrix
│ ├── database/
│ │ ├── database-guide.md conventions + migration workflow
│ │ └── schema-diagram.md ERD 51 bảng (+§11 PE +§12 Budget)
│ │ └── schema-diagram.md ERD 52 bảng (+§11 PE +§12 Budget +§13 PEDeptOpinions)
│ ├── flows/ 6 sequence diagram (auth/permission/contract/form/sla + PE ref architecture)
│ ├── guides/ setup, cicd, deploy, runbook, security
│ ├── changelog/
@ -107,7 +108,7 @@ SOLUTION_ERP/
- **NamGroup** (`D:\Dropbox\CONG_VIEC\NAMGROUP\SOURCECODE_CÔNG_TY\NAMGROUP\`) — template 2 FE + IIS deploy + permission matrix
- **DH_Y_DUOC** (`D:\Dropbox\CONG_VIEC\DAI_Y_DUOC\DH_Y_DUOC_SOURCECODE\DH_Y_DUOC\`) — template backend hiện đại (Clean Arch + CQRS + EF migrations)
## 6. Roadmap (status 2026-04-28)
## 6. Roadmap (status 2026-04-29)
| Phase | Focus | Trạng thái |
|---|---|---|
@ -120,9 +121,9 @@ SOLUTION_ERP/
| **Tier 3** | Versioned WF + 3-panel layout + 4-bảng + 4 catalogs + 16 demo users + Brand identity | ✅ Done |
| **6 Module Duyệt NCC** | PE iter 1 (skeleton) + iter 2 (UX polish) + Domain rebrand `.solutions.com.vn` | ✅ Done |
| **7 Budget BE + 14 Solutions users** | Migration 14 — 4 bảng Budget + workflow simple + 11 endpoint + 14 user thật | ✅ Done (Session 4) |
| **8 Budget FE + PE/HD integration** | FE Budget pages + link select Budget vào PE/Contract form | 📝 Active (Session 5) |
| **PE feature gap** | Designer UI + Ý kiến 4 phòng ban + Export PDF + UAT thật + SMTP | 📝 Carry over |
| **9+ Post-launch** | E-signature, Bravo/SAP, Mobile, AI OCR | 📝 Future |
| **8 Budget FE + PE feature complete + Tests** | FE Budget 3-panel + PE/HD-Budget integration + PE Workflow Designer + Ý kiến 4 PB (mig 15) + Tests Phase 1-2 (71 test) + CI gate | ✅ Done (Session 5) |
| **9 UAT + Ops + carry over** | UAT thật + SMTP + Rotate creds + SQL backup + Export PDF (pending) + Tests Phase 3-5 | 📝 Active (Session 6) |
| **10+ Post-launch** | E-signature, Bravo/SAP, Mobile, AI OCR | 📝 Future |
Chi tiết atomic tasks: [`changelog/migration-todos.md`](changelog/migration-todos.md).
Session logs: [`changelog/sessions/`](changelog/sessions/).

View File

@ -1,10 +1,20 @@
# HANDOFF — Brief 5 phút cho session tiếp theo
**Last updated:** 2026-04-28 (Phase 7**Module Ngân sách (Budget) BE + 14 demo users Solutions thật**)
**Last updated:** 2026-04-29 (Phase 8**Budget FE complete + PE/HD-Budget integration + PE Workflow Designer + Ý kiến 4 PB + Tests Phase 1-2 + CI gate**)
## TL;DR
**Session 4 (28/04)** thêm 2 milestone lớn:
**Session 5 (29/04)** đóng gần hết feature gap + bật test gate:
### 🎯 Headline outcomes
-**Budget feature-complete** — FE 3-panel pages cả 2 app, BE đã sẵn từ session 4
-**PE/HD ↔ Budget integration** — form select Budget filter Phase=DaDuyet + Project, cột "So với ngân sách" ở PE matrix với delta indicator
-**PE Detail UI restructure** match form chính thức PHIẾU TRÌNH KÝ (4 section đánh số)
-**PE Workflow Designer admin UI** `/system/pe-workflows/:typeCode` versioned với clone/edit/+Role/+User
-**Ý kiến 4 phòng ban** — migration 15 + section sign-off 2x2 grid (Phê duyệt/CCM/MuaHàng/SM-PM)
-**Tests Phase 1-2** — 71 unit test pass + CI gate fail-fast (Domain policy + Infra code generator)
**Session 4 (28/04)** đã có:
### A. Module Ngân sách (Budget) BE — migration 14, +4 bảng, +11 endpoint
@ -24,25 +34,23 @@
- Pwd `User@123456`. Reconcile pattern (gotcha #38 4-field rename).
- Tổng 30 user (16 sample cũ giữ + 14 Solutions thật mới).
**Tổng:** 51 DB tables, ~124 endpoints, 14 migrations, 38 gotchas, 5+ commit
session 4 push lên Gitea.
**Tổng cumulative:** 52 DB tables, ~128 endpoints, 15 migrations, 38 gotchas,
**71 unit test**, 6 commit session 5 push lên Gitea.
## ⚠️ CẢNH BÁO session tiếp (Session 5)
## ⚠️ CẢNH BÁO session tiếp (Session 6)
1. **FE Budget pages CHƯA LÀM** — BE đã sẵn sàng nhưng chưa có page nào. Pattern: copy từ PE 3-panel List + Create + Detail tabs (Thông tin / Hạng mục) + WorkflowPanel timeline. Mirror sang fe-user. Thêm route `/budgets`, `/budgets/new`, `/budgets/:id`. Menu resolver `Bg_*` → URL.
2. **PE/Contract → Budget integration CHƯA WIRE** — BE đã có `BudgetId?` nullable FK trên cả 2 entity, FE form chưa thêm field "Ngân sách" select. Cần filter Budget theo `Phase=DaDuyet && NamNganSach=current && ProjectId match`. Tab Hạng mục có thể bonus cột "So với ngân sách".
1. **CI test gate active** — mỗi commit push trigger 2 step `dotnet test` (Domain + Infrastructure) TRƯỚC build/deploy. Test fail = NO deploy. Khi feature mới có bug logic → thêm regression test trước khi commit. Workflow: code → `dotnet test SolutionErp.slnx` local → commit → push.
2. **Login email** `admin@solutionerp.local` / `Admin@123456` — domain default
chưa đổi sang `@solutions.com.vn` (chỉ demo user rebrand qua BackfillDemoEmail).
3. **Chưa xóa binding cũ `.huypham.vn`** — vẫn active fallback. Sau 1-2 ngày
verify stable → `.\migrate-domains.ps1 -RemoveOld -SkipCert` trên VPS.
4. **win-acme scheduled task "unhealthy"** — cert auto-renew có thể fail
khi gần 2026-06-18. Fix: mở `wacs.exe` interactive → Manage Renewals →
recreate task.
5. **PE feature gap carry over** (xem STATUS §C):
- PE Workflow admin designer UI `/system/pe-workflows/:typeCode`
- Section "Ý kiến 4 phòng ban" (Phê duyệt/CCM/MuaHàng/SM-PM)
- Export phiếu PDF/Excel
6. **Login email** `admin@solutionerp.local` / `Admin@123456` — domain default
chưa đổi sang `@solutions.com.vn` (chỉ demo user và rebrand cũ trong BackfillDemoEmail).
7. **Chú ý G-084:** VPS shared với VietReport — mọi reverse proxy / backend
5. **Export phiếu PDF/Excel PE** — pending vô thời hạn (user nói không quan trọng lắm). Khi cần: tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx`.
6. **Atomic sequence Budget chưa chốt**`NS-YYYYMM-XXXX` Random.Shared. Khi format chốt → migration `AddBudgetCodeSequences` + `IBudgetCodeGenerator` SERIALIZABLE pattern (mirror Contract/PE).
7. **Versioned workflow Budget chưa có** — hardcoded `BudgetPolicy.Default` 3-step. Khi user cần admin config UI → migration `AddBudgetVersionedWorkflow` + 3 bảng + pin per Budget.
8. **Chú ý G-084:** VPS shared với VietReport — mọi reverse proxy / backend
service mới phải dùng `127.0.0.1` + bind loopback IPv4 explicit.
## ⭐ Skills (.claude/skills/) — PHẢI dùng khi task khớp
@ -84,10 +92,15 @@ session 4 push lên Gitea.
| **Module Duyệt NCC (tiền-HĐ) E2E** — 10 bảng + 2 workflow + Kế thừa HĐ | ✅ Done |
| **PE polish iter 2** — flat layout + per-NCC attachments + readOnly Duyệt + email rebrand | ✅ Done |
| **Domain rebrand huypham.vn → solutions.com.vn** — 3 subdomain + cert + CORS + FE bundle | ✅ Done |
| **Module Ngân sách BE** — 4 bảng + 11 endpoint + workflow simple + 30 user | ✅ Done (FE TODO) |
| **PE Workflow designer UI + Ý kiến 4 phòng ban + Export PDF** | 📝 Pending session 5+ |
| **FE Budget pages + PE/Contract → Budget integration** | 📝 Priority 0 session 5 |
| 8+ Post-launch (E-signature, Bravo/SAP, Mobile, AI) | 📝 Future |
| **Module Ngân sách BE + 30 user** — 4 bảng + 11 endpoint + workflow simple | ✅ Done (S4) |
| **Module Ngân sách FE** — 3-panel pages + Detail tabs + Workflow Panel cả 2 app | ✅ Done (S5) |
| **PE/Contract → Budget integration** — form Select + cột "So với ngân sách" PE matrix | ✅ Done (S5) |
| **PE Workflow designer admin UI** `/system/pe-workflows/:typeCode` | ✅ Done (S5) |
| **Ý kiến 4 phòng ban PE** — migration 15 + section sign-off 2x2 grid | ✅ Done (S5) |
| **Tests Phase 1-2 + CI gate** — 71 test (Domain policy + Infra code generator) | ✅ Done (S5) |
| **Export phiếu PDF/Excel PE**`IDocumentConverter` + template | 📝 Pending (không quan trọng) |
| **Tests Phase 3-5** — Application handlers + API smoke + FE Vitest | 📝 Pending (làm khi cần) |
| 9+ Post-launch (E-signature, Bravo/SAP, Mobile, AI) | 📝 Future |
## Run nhanh
@ -128,41 +141,39 @@ Login: `admin@solutionerp.local` / `Admin@123456`
## Cần làm kế tiếp
### 🔥 Priority 0 — Budget FE + PE/HD integration (session 5)
### 🔥 Priority 0 — Session 6 (Ops + UAT focus)
Xem **STATUS.md §🔥 In Progress** đầy đủ (nhóm A/B/C/D/E). Tóm tắt:
Đa số feature gap đã đóng. Còn lại chủ yếu Ops/UAT + optional polish.
**A. Budget FE pages — copy pattern PE:**
1. `fe-admin/src/types/budget.ts` (types + enums BudgetPhase + ApprovalDecision reuse)
2. `fe-admin/src/pages/budgets/BudgetsListPage.tsx` (3-panel `lg:grid-cols-[320px_1fr_360px]` — Panel 1 list + Panel 2 detail tabs + Panel 3 workflow timeline)
3. `fe-admin/src/pages/budgets/BudgetCreatePage.tsx` (form Header: tên/năm/dự án/phòng ban/người soạn)
4. `fe-admin/src/components/budgets/BudgetDetailTabs.tsx` (Thông tin / Hạng mục — flat row giống PE Details)
5. `fe-admin/src/components/budgets/BudgetWorkflowPanel.tsx` (Panel 3: timeline phase + ô comment + button Trình)
6. Mirror tất cả sang `fe-user/`
7. App.tsx routes `/budgets`, `/budgets/new`, `/budgets/:id` cả 2 app
8. Menu resolver `Bg_*` ở Layout: `Bg_List``/budgets`, `Bg_Pending``/budgets?phase=Pending`, `Bg_Create``/budgets/new`
**A. Hard blockers (chờ user / ops):**
1. **UAT thật 1 tuần với 2-3 user** — Drafter (QS/NV.PB) + CCM + BOD ghi bug/friction
2. **SMTP config** → bật Email outbox (BLOCKED chờ user cấp host/user/pass)
3. **Rotate credentials** — admin + 30 demo + SA + vrapp + JWT secret
4. **Schedule SQL backup daily**`scripts/backup-sql.ps1` chưa schedule Task Scheduler
**B. PE/Contract → Budget integration:**
1. **PE form** thêm field `Ngân sách` select Budget (filter `Phase=DaDuyet && NamNganSach=current && ProjectId=peProjectId`). Lưu `PE.BudgetId`.
2. **Contract form** tương tự — link sang Budget cho đối chiếu chi phí.
3. PE Detail tab có thể thêm cột "So với ngân sách" — compute từ `BudgetDetail` tương ứng (match GroupCode + ItemCode) nếu có Budget link.
**B. PE feature gap còn lại:**
- **Export phiếu PDF/Excel** PE — pending (user nói không quan trọng lắm). Khi cần: tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx`.
**C. PE feature gap (carry over từ session 3):**
1. **PE Workflow admin designer UI** `/system/pe-workflows/:typeCode` — mirror pattern `WorkflowsPage.tsx` + `WorkflowDesigner.tsx`. BE cần `PeWorkflowAdminFeatures.cs` + `PeWorkflowsController.cs`. Framework backend đã sẵn (3 bảng `PurchaseEvaluationWorkflow*` + `FromDefinition`), chỉ thiếu wire UI.
2. **Ý kiến 4 phòng ban** (Phê duyệt / P.CCM / P.MuaHàng / SM-PM) — Excel form có, entity chưa map. Cần design: 4 text field + signoff date, hoặc dùng Approvals với role-kind.
3. **Export phiếu PDF/Excel** — tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx`.
**D. Optional polish:**
- Budget MaNganSach atomic sequence (hiện Random.Shared, format chốt sau)
- Budget versioned workflow (admin config qua UI — hiện hardcoded `BudgetPolicy.Default`)
- Payment terms PE tách field
- Auto-map PE Details → Contract Details khi gen HĐ
**C. Optional polish (làm khi UAT phát sinh):**
- Budget MaNganSach atomic sequence — chốt format → migration `AddBudgetCodeSequences` mirror Contract/PE
- Budget versioned workflow — admin config UI thay hardcoded `BudgetPolicy.Default`
- Payment terms PE tách field — JSON blob → 6 column riêng
- Auto-map PE Details → Contract per-type Details khi gen HĐ
- Matrix Quotes bulk paste từ Excel
- fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi"
**Đã xong trong session 4 (check STATUS Recently Done):**
- ~~Module Ngân sách BE — 4 bảng + 11 endpoint + workflow simple~~ ✅
- ~~14 demo user Solutions thật (PRO 5 + CCM 7 + ISO 1 + CEO 1)~~ ✅
- ~~Docs cleanup + tái cấu trúc MD~~ ✅
**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
**Đã xong session 5 (check STATUS Recently Done):**
- ~~Module Ngân sách FE — 3-panel pages cả 2 app~~ ✅
- ~~PE/Contract → Budget integration + cột "So với ngân sách"~~ ✅
- ~~PE Detail UI restructure 4 section đánh số~~ ✅
- ~~PE Workflow Designer admin UI~~ ✅
- ~~Ý kiến 4 phòng ban (migration 15)~~ ✅
- ~~Tests Phase 1-2 + CI gate (71 test)~~ ✅
### A. Hard blockers (chờ user / ops)

View File

@ -2,9 +2,9 @@
> **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-04-28 (Phase 7**Module Ngân sách (Budget) BE + 14 demo users Solutions**)
**Last updated:** 2026-04-29 (Phase 8**Budget FE complete + PE/HD-Budget integration + PE Workflow designer + Ý kiến 4 PB + Tests Phase 1-2 + CI gate**)
## 📍 Phase hiện tại: **Module Ngân sách BE deployed** — 51 DB tables (+4 Budget), 14 migrations (+14 `AddBudgets`), ~124 endpoints (+11 Budget). PE module ổn định, Budget BE live (CQRS + Controller + Migration + Menu seed), FE Budget pages chưa làm. 30 demo users (16 sample + 14 Solutions thật: 5 PRO + 7 CCM + 1 ISO + 1 CEO).
## 📍 Phase hiện tại: **Budget feature-complete + PE feature gap đóng + Test gate live** — 52 DB tables (+1 PEDepartmentOpinions), 15 migrations (+`AddPurchaseEvaluationDepartmentOpinions`), ~128 API endpoints (+4 PE WF admin + Opinion). 31 FE pages (+5 Budget + 1 PeWorkflowsPage). **71 unit test pass** (Phase 1: 54 Domain policy + Phase 2: 17 Infra code generator) — CI fail-fast nếu test fail. 30 demo user (16 sample + 14 Solutions thật).
### 🌐 Production URLs
@ -14,42 +14,28 @@
- https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp — Gitea repo + Actions
- Default admin: `admin@solutionerp.local` / `Admin@123456` ⚠️ **RE-ROTATE sau login đầu**
## 🔥 In Progress — Còn lại cho session 5
## 🔥 In Progress — Còn lại cho session 6
### A. Budget — FE pages (BE đã xong)
### A. PE feature gap (carry over)
- [ ] **FE Budget pages** — copy pattern PE:
- `fe-admin/src/types/budget.ts` (types + enums)
- `fe-admin/src/pages/budgets/BudgetsListPage.tsx` (3-panel List + Detail tabs)
- `fe-admin/src/pages/budgets/BudgetCreatePage.tsx` (form Header)
- `fe-admin/src/components/budgets/BudgetDetailTabs.tsx` (Thông tin / Hạng mục)
- `fe-admin/src/components/budgets/BudgetWorkflowPanel.tsx` (Panel 3 timeline + transition)
- Mirror fe-user
- App.tsx routes `/budgets`, `/budgets/new`, `/budgets/:id`
- Menu resolver `Bg_*``/budgets[?phase=Pending]`
- [ ] **Export phiếu PDF/Excel** — tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx` (user pending — không quan trọng lắm)
### B. PE/Contract → Budget integration
### B. Optional polish (khi UAT phát sinh)
- [ ] **PE form** thêm field **"Ngân sách"** select Budget của Project (filter Phase=DaDuyet, NamNganSach=current).
- [ ] **Contract form** tương tự — link sang Budget cho đối chiếu chi phí.
- [ ] PE Detail tab thêm cột "So với ngân sách" (compute từ BudgetDetail tương ứng nếu có).
### C. PE feature gap (carry over từ session 3)
- [ ] **PE Workflow admin designer UI** `/system/pe-workflows/:typeCode`
- [ ] **Section "Ý kiến 4 phòng ban"** (Phê duyệt/CCM/MuaHàng/SM-PM)
- [ ] **Export phiếu PDF/Excel** — tái dùng `IDocumentConverter`
### D. Optional polish
- [ ] Budget MaNganSach atomic sequence (hiện `NS-YYYYMM-XXXX` Random.Shared, chốt format chính thức sau)
- [ ] Budget MaNganSach atomic sequence (hiện `NS-YYYYMM-XXXX` Random.Shared, chốt format chính thức sau → migration `AddBudgetCodeSequences`)
- [ ] Budget versioned workflow (admin config qua UI — hiện hardcoded `BudgetPolicy.Default`)
- [ ] Payment terms tách field (PE)
- [ ] Auto-map PE Details → Contract Details khi gen HĐ
- [ ] Payment terms tách field (PE) — JSON blob → 6 column riêng (migration `AddPePaymentTermFields`)
- [ ] Auto-map PE Details → Contract per-type Details khi gen HĐ
- [ ] Matrix Quotes bulk paste từ Excel
- [ ] fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi"
### E. Deploy / Ops chưa xong
### C. Tests Phase 3-5 (làm khi gặp bug recurring để justify ROI)
- [ ] **Phase 3** — Application handler tests (CQRS) với EF InMemory (~1 ngày, ~15 test)
- [ ] **Phase 4** — API smoke tests qua WebApplicationFactory (~0.5 ngày, ~7 test)
- [ ] **Phase 5** — FE Vitest cho lib utility (queryMatches, fmtMoney) (~0.5 ngày, ~10 test)
### D. Deploy / Ops chưa xong
- [ ] **Remove binding cũ `.huypham.vn`** sau verify: `ssh vietreport-vps ; cd C:\solution-erp\scripts ; .\migrate-domains.ps1 -RemoveOld -SkipCert`
- [ ] **win-acme scheduled task "unhealthy"** — auto-renew fix trước 2026-06-18.
@ -62,6 +48,12 @@
| Ngày | Ai | Task | Commit |
|---|---|---|---|
| 2026-04-29 | Claude | **Tests Phase 2 — Code generator format + sequence (SQLite in-memory)**`tests/SolutionErp.Infrastructure.Tests/` xUnit + EF SQLite 10. `SqliteDbFixture` + `TestApplicationDbContext` subclass override `nvarchar(max) → TEXT` (SQLite không support `max`). 17 test: ContractCodeGenerator (format RG-001 5 type + Framework year scope vs Project scope + sequence per prefix + year boundary reset + persistence verify) + PurchaseEvaluationCodeGenerator (format A/B + 3-digit pad + independent A/B sequences + year boundary). CI gate +1 step. Total 71 test pass / 2.1s. | `df5988b` |
| 2026-04-29 | Claude | **Tests Phase 1 — Domain unit tests + CI gate**`tests/SolutionErp.Domain.Tests/` xUnit 2.9 + FluentAssertions 7.2 (pin trước v8 commercial). 54 test pure function (no DB/IO): WorkflowPolicy (Standard 9-phase + SkipCcm 7-phase + Registry per ContractType + FromDefinition versioned + UserKindApprover) / PEPolicy (NccOnly 3-step + NccWithPlan 5-step + reject paths) / BudgetPolicy (Default 3-step + terminals + SLA spec). `.gitea/workflows/deploy.yml` thêm step "Run unit tests" trước build, fail → `exit $LASTEXITCODE` → no deploy. SolutionErp.slnx + folder `/tests/`. | `d3f9346` |
| 2026-04-29 | Claude | **PE Workflow designer admin UI + Ý kiến 4 phòng ban** — Migration 15 `AddPurchaseEvaluationDepartmentOpinions` (UNIQUE PEId+Kind, 1 row/phòng/phiếu). Domain `PurchaseEvaluationDepartmentOpinion` + enum `PeDepartmentKind` (PheDuyet/Ccm/MuaHang/SmPm). BE: `PeWorkflowAdminFeatures.cs` ~250 LOC mirror Contract pattern (GetOverview + Create version, deactivate cũ atomic) + `PeWorkflowsController` 2 endpoint reuse policy `Workflows.*`. `PeDepartmentOpinionFeatures.cs` Upsert (sign=true→set SignedAt+UserId, sign=false giữ chữ ký cũ) + Delete + 2 endpoint. FE: `PeWorkflowsPage.tsx` ~500 LOC + designer dialog (clone version + add/remove steps + +Role/+User approvers). Section "5. Ý kiến 4 phòng ban (sign-off)" 2x2 grid OpinionBox (read mode chữ ký vs edit textarea + 2 button Lưu/Lưu&Ký). | `5d94bb4` |
| 2026-04-29 | Claude | **PE Detail UI restructure theo spec form PHIẾU TRÌNH KÝ** — 4 section đánh số match form chính thức: "1. Thông tin gói thầu" (a/b chỉ Tên + Dự án) / "2. Chọn NCC/TP" (a NCC chọn / b Ngân sách / c Giá chào thầu auto-compute từ winner quotes / d Bản so sánh embed GeneralAttachments) / "3. NCC/TP tham gia" / "4. Hạng mục + Báo giá". FormRow helper (label 176px + value flex) thay cho dl grid 2-col cũ. | `7e36241` |
| 2026-04-29 | Claude | **PE/Contract → Budget integration + cột "So với ngân sách"** — BE: `BudgetSummaryDto` shared (PE & Contract DetailBundle), Create/Update PE+Contract commands + `BudgetId?` validate cùng Project + Phase=DaDuyet. `CreateContractFromEvaluation` carry forward pe.BudgetId → contract.BudgetId. FE: PE & Contract Create form + Select "Ngân sách" filter Phase=DaDuyet + Project match. PE InfoTab + Contract Edit display Budget link clickable. PE ItemsTab matrix + cột "NS link · Δ" — match per-row qua key `groupCode\|itemCode`, fetch /budgets/{id} riêng + footer aggregate (xanh dưới / đỏ vượt / xám khớp). | `61e5d4d` |
| 2026-04-29 | Claude | **Budget FE 3-panel pages cả 2 app**`types/budget.ts` (BudgetPhase 5-state enum + DTO) + `BudgetsListPage` 3-panel `[340px_1fr_360px]` + filter Phase + Năm + alias `?phase=Pending` + readOnly mode menu Duyệt + `BudgetCreatePage` form Header + `BudgetDetailTabs` flat (Section Thông tin Header + Section Hạng mục table CRUD inline auto-compute ThanhTien=KL×ĐG) + `BudgetWorkflowPanel` Panel 3 timeline + dialog comment + Approvals/Changelog. Mirror fe-user. App.tsx 3 route + Layout resolver `Bg_*`. TS build pass cả 2 app. | `df12fb1` |
| 2026-04-28 | Claude | **Module Ngân sách (Budget) BE — 4 bảng + workflow simple 3-step** — Migration 14 `AddBudgets`. Domain: `Budget` (Header — MaNganSach `NS-YYYYMM-XXXX`, NamNganSach, ProjectId/DepartmentId, DrafterUserId, TongNganSach auto-sum, SlaDeadline) + `BudgetDetail` (flat row pattern: GroupCode/Name + ItemCode + NoiDung + DonViTinh + KhoiLuong/DonGia/ThanhTien) + `BudgetApproval` (workflow history reuse `ApprovalDecision`) + `BudgetChangelog` (`BudgetEntityType` Header/Detail/Workflow). Enum `BudgetPhase` 5 state (DangSoanThao→ChoCCM→ChoCEO→DaDuyet + TuChoi). `BudgetPolicy.Default` hardcoded 3-step (Drafter→CCM→CEO). Application: 11 CQRS handler (~340 LOC: Create/UpdateDraft/Transition/List/GetDetail/Delete + Detail CRUD auto-recompute TongNganSach + ListChangelogs). Api: `BudgetsController` 11 endpoint. Link: Contract.BudgetId? + PE.BudgetId? nullable FK + index. Menu seed `Budgets` root + 3 leaf (Bg_List/Create/Pending) order=27 icon Wallet. **FE chưa làm** (next session). | `a05c57b` |
| 2026-04-28 | Claude | **14 demo users Solutions company thật** — PRO 5 (TPB tra.bui + 4 NV: phuong/binh/danh/dat) + CCM 7 (TPB ngocanh.huynh + 6 NV: ha.dao/cuong.do/long.le/ha.nguyen/dung.nguyen/anh.nguyen) + ISO 1 (chau.le) + CEO 1 (huy.duong). Pattern reconcile per-user try-catch (gotcha #38 4-field rename). Pwd `User@123456`. Tổng 30 user (16 sample cũ + 14 Solutions thật). Test login E2E pass. | `8097892` |
| 2026-04-28 | Claude | **Docs cleanup + chốt session 3** — Tái cấu trúc MD: archive raw dump (forms-spec-raw, workflow-raw) → `_archive/`, compact `migration-todos.md` collapse Phase 0-5+Tier3 cũ, update `CLAUDE.md` modules table + skills 6 (3 domain + 3 ops), `flows/` thêm PE ref architecture, audit `gotchas.md` (38 pitfall — thêm #34 NavLink query string + #35 menu inheritance + #36 Vite env rebuild + #37 mutex accordion family + #38 Identity 4-field rename). Session log `2026-04-23-2200-roles-demo-pending-cleanup.md` + `2026-04-24-chot-session-3-pe-polish.md`. | `309dcd9` · `e71e0eb` · `e65578a` |
@ -117,7 +109,7 @@
| 2026-04-21 | Claude | **Phase 1 foundation** + Docs addition | `702411f` + `49a5f57` |
| 2026-04-21 | Claude | **Phase 0** | `25dad7f` |
Session logs: [P0](changelog/sessions/2026-04-21-1045-phase0-scaffold.md) · [P1f](changelog/sessions/2026-04-21-1100-phase1-foundation.md) · [P1.2](changelog/sessions/2026-04-21-1130-phase1-cruds-permission.md) · [P2](changelog/sessions/2026-04-21-1200-phase2-form-engine.md) · [P3](changelog/sessions/2026-04-21-1330-phase3-workflow.md) · [P4](changelog/sessions/2026-04-21-1430-phase4-report.md) · [P5prep](changelog/sessions/2026-04-21-1530-phase5-prep.md) · [Tier 3](changelog/sessions/2026-04-22-0300-tier3-feature-complete.md) · [Skill gov](changelog/sessions/2026-04-23-0900-skill-governance.md) · [Toolkit+4-bảng+Roles VN](changelog/sessions/2026-04-23-1500-toolkit-data-roles.md) · [Roles+Demo+Pending](changelog/sessions/2026-04-23-2200-roles-demo-pending-cleanup.md) · [PE polish iter 2 + rebrand](changelog/sessions/2026-04-24-chot-session-3-pe-polish.md) · [**Budget BE + 14 Solutions users**](changelog/sessions/2026-04-28-chot-session-4-budget.md)
Session logs: [P0](changelog/sessions/2026-04-21-1045-phase0-scaffold.md) · [P1f](changelog/sessions/2026-04-21-1100-phase1-foundation.md) · [P1.2](changelog/sessions/2026-04-21-1130-phase1-cruds-permission.md) · [P2](changelog/sessions/2026-04-21-1200-phase2-form-engine.md) · [P3](changelog/sessions/2026-04-21-1330-phase3-workflow.md) · [P4](changelog/sessions/2026-04-21-1430-phase4-report.md) · [P5prep](changelog/sessions/2026-04-21-1530-phase5-prep.md) · [Tier 3](changelog/sessions/2026-04-22-0300-tier3-feature-complete.md) · [Skill gov](changelog/sessions/2026-04-23-0900-skill-governance.md) · [Toolkit+4-bảng+Roles VN](changelog/sessions/2026-04-23-1500-toolkit-data-roles.md) · [Roles+Demo+Pending](changelog/sessions/2026-04-23-2200-roles-demo-pending-cleanup.md) · [PE polish iter 2 + rebrand](changelog/sessions/2026-04-24-chot-session-3-pe-polish.md) · [Budget BE + 14 Solutions users](changelog/sessions/2026-04-28-chot-session-4-budget.md) · [**Budget FE + PE/HD-Budget + PE WF Designer + Tests Phase 1-2**](changelog/sessions/2026-04-29-chot-session-5-budget-fe-pe-tests.md)
**Docs entry points:**
@ -125,7 +117,7 @@ Session logs: [P0](changelog/sessions/2026-04-21-1045-phase0-scaffold.md) · [P1
- [`workflow-contract.md`](workflow-contract.md) · [`forms-spec.md`](forms-spec.md)
- [`database/database-guide.md`](database/database-guide.md) · [`database/schema-diagram.md`](database/schema-diagram.md)
- [`flows/`](flows/) (7 file) · [`guides/`](guides/) (4 file) · [`gotchas.md`](gotchas.md)
- [`changelog/migration-todos.md`](changelog/migration-todos.md) · [`changelog/sessions/`](changelog/sessions/) (16 file)
- [`changelog/migration-todos.md`](changelog/migration-todos.md) · [`changelog/sessions/`](changelog/sessions/) (17 file)
- [`.claude/skills/README.md`](../.claude/skills/README.md) — 6 skill (3 domain + 3 ops) · audit định kỳ 1/tháng (cron `solution-erp-skill-audit-monthly` next 2026-05-01)
## 🎯 Next up
@ -162,19 +154,20 @@ Session logs: [P0](changelog/sessions/2026-04-21-1045-phase0-scaffold.md) · [P1
## 📊 Thông số cumulative
| | P0 | P1f | P1.2 | P2 | P3 | P4 | P5prep | Tier3 | +Toolkit | +RolesPg+Demo | +PE module | +PE polish | **+Budget+30 users** |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| BE LOC | 0 | ~400 | ~1500 | ~1900 | ~2700 | ~3100 | ~3300 | ~4800 | ~7800 | ~8800 | ~11100 | ~11400 | **~11750** (+340 Budget) |
| DB tables | 0 | 7 | 12 | 14 | 19 | 19 | 19 | 24 | 36 | 36 | 46 | 47 (+CodeSeq) | **51** (+4 Budget) |
| API endpoints | 0 | 4 | 20 | 23 | 31 | 33 | 35 | ~50 | ~80 | ~93 | ~110 | ~113 | **~124** (+11 Budget) |
| Migrations | 0 | 1 | 3 | 4 | 5 | 5 | 5 | 8 | 11 | 11 | 12 | 13 | **14** (`AddBudgets`) |
| FE pages | 0 | 2 | 6 | 7 | 14 | 16 | 16 | ~20 | ~22 | ~23 | ~26 | ~26 | ~26 (Budget FE TODO) |
| FE components | — | — | — | — | — | — | — | many | many+ | +EditRowDialog | +PE 5-tab | +Compare section | (Budget FE TODO) |
| Scripts PS | 0 | 0 | 0 | 1 | 1 | 1 | 3 | 4 | 4 | 5 | 5 | **6** (+migrate-domains.ps1) | 6 |
| CI/CD workflow | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| Docs | 10 | 13 | 14 | 24 | 26 | 30 | 35 | ~40 | ~42 | ~44 | ~46 | ~48 | **~50** (+session log + chốt) |
| Demo data | 0 | 0 | empty | 0 | 0 | 0 | 0 | 0 | 5+3 | 15+8+7+13+4 | +PE 4 phiếu | +rebrand email | **30 user** (16 sample + 14 Solutions thật) |
| Commits | 1 | 2 | 3 | 5 | 6 | 7 | 8 | ~25 | ~47 | ~52 | ~63 | ~70 | **~75** |
| | P0 | P1f | P1.2 | P2 | P3 | P4 | P5prep | Tier3 | +Toolkit | +RolesPg+Demo | +PE module | +PE polish | +Budget+30 users | **+Session 5** |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| BE LOC | 0 | ~400 | ~1500 | ~1900 | ~2700 | ~3100 | ~3300 | ~4800 | ~7800 | ~8800 | ~11100 | ~11400 | ~11750 | **~13050** (+1300 PE WF Designer + Opinion + Budget integration) |
| DB tables | 0 | 7 | 12 | 14 | 19 | 19 | 19 | 24 | 36 | 36 | 46 | 47 | 51 | **52** (+1 PEDepartmentOpinions) |
| API endpoints | 0 | 4 | 20 | 23 | 31 | 33 | 35 | ~50 | ~80 | ~93 | ~110 | ~113 | ~124 | **~128** (+2 PE WF + 2 Opinion) |
| Migrations | 0 | 1 | 3 | 4 | 5 | 5 | 5 | 8 | 11 | 11 | 12 | 13 | 14 | **15** (`AddPEDepartmentOpinions`) |
| FE pages | 0 | 2 | 6 | 7 | 14 | 16 | 16 | ~20 | ~22 | ~23 | ~26 | ~26 | ~26 | **~31** (+5 Budget × 2 app + PeWorkflowsPage) |
| FE components | — | — | — | — | — | — | — | many | many+ | +EditRowDialog | +PE 5-tab | +Compare section | — | **+Budget tabs/panel + PE OpinionBox + PE 4-section restructure** |
| Scripts PS | 0 | 0 | 0 | 1 | 1 | 1 | 3 | 4 | 4 | 5 | 5 | 6 | 6 | 6 |
| CI/CD workflow | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | **1+test gate (2 step)** |
| **Tests** | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | **71** (54 Domain + 17 Infra) |
| Docs | 10 | 13 | 14 | 24 | 26 | 30 | 35 | ~40 | ~42 | ~44 | ~46 | ~48 | ~50 | **~52** (+session log + Test plan) |
| Demo data | 0 | 0 | empty | 0 | 0 | 0 | 0 | 0 | 5+3 | 15+8+7+13+4 | +PE 4 phiếu | +rebrand email | 30 user | 30 user |
| Commits | 1 | 2 | 3 | 5 | 6 | 7 | 8 | ~25 | ~47 | ~52 | ~63 | ~70 | ~75 | **~82** (+6 session 5) |
## 🚨 Blockers / risks

View File

@ -309,7 +309,59 @@ DangSoanThao(1) ──Trình──► ChoCCM(2) ──Duyệt──► ChoCEO(3)
Chi tiết: [`database/schema-diagram.md §12`](database/schema-diagram.md).
## 11. Liên quan
## 11. Testing strategy (Phase 8 — Session 5)
Test pyramid bottom-heavy, **không E2E** (brittle, maintenance cao cho solo dev).
```
┌──────────────────────────────────┐ ❌ Skip
│ E2E / UI (Playwright) │ Brittle, slow, tốn time maintain
├──────────────────────────────────┤ ⚠️ TODO Phase 4
│ API smoke (WebApplicationFactory)│ 1-2 happy path roundtrip per critical endpoint
├──────────────────────────────────┤ ⚠️ TODO Phase 3
│ Application Handler (CQRS) │ EF InMemory cho business rules
├──────────────────────────────────┤ ✅ Done Phase 2 — 17 test
│ Infrastructure (SQLite in-mem) │ Code generator format + sequence + year boundary
├──────────────────────────────────┤ ✅ Done Phase 1 — 54 test
│ Domain Policy (pure functions) │ State machine + role transition + Registry mapping
└──────────────────────────────────┘
```
**Stack:**
| Layer | Framework | Note |
|---|---|---|
| .NET test | xUnit 2.9.3 | Industry standard |
| Assertion | FluentAssertions 7.2 | Pin trước v8 commercial license |
| EF testing | Microsoft.EntityFrameworkCore.Sqlite 10 | Phase 2+ supports transactions |
| Test fixture | Custom `SqliteDbFixture` + `TestApplicationDbContext` | Override `nvarchar(max) → TEXT` cho SQLite compat |
**CI gate:** `.gitea/workflows/deploy.yml` chạy `dotnet test` cho từng test project TRƯỚC build/publish/deploy. Test fail `exit $LASTEXITCODE` no deploy.
```yaml
- name: Run unit tests (Domain)
run: dotnet test tests/SolutionErp.Domain.Tests/...
- name: Run integration tests (Infrastructure)
run: dotnet test tests/SolutionErp.Infrastructure.Tests/...
```
**Phase priority — anti-overkill:**
1. **Phase 1 (Done)** Domain policy: 3 test file (Contract WF / PE WF / Budget) cover transition rules + Registry + FromDefinition. ~54 test, < 7s.
2. **Phase 2 (Done)** Infra code generator: format RG-001/PE + sequence increment + year boundary (framework ). ~17 test, ~2s.
3. **Phase 3 (Pending)** Application CQRS handler: critical state-change handlers (Transition + Create) với EF InMemory.
4. **Phase 4 (Pending)** API smoke: WebApplicationFactory + SQLite, 5-7 endpoint critical roundtrip.
5. **Phase 5 (Pending)** FE Vitest cho lib utility (queryMatches, fmtMoney) chỉ pure functions, KHÔNG component snapshot.
**Quy tắc bổ sung mỗi feature mới:**
- Domain policy method 2-3 test (positive + negative + edge)
- CQRS handler guard 1 test mỗi guard branch
- Bug found in production 1 regression test added before merge
Detail run: `dotnet test SolutionErp.slnx` chạy cả 71 test ~2s.
## 12. Liên quan
- [`rules.md`](rules.md) coding conventions
- [`database/database-guide.md`](database/database-guide.md) DB schema chi tiết

View File

@ -1,7 +1,7 @@
# Migration To-dos — Atomic Roadmap
> Tick `[x]` khi xong. Phase 0-5 + Tier 3 + Phase 6 đã DONE — collapsed. Detail xem session
> logs trong `docs/changelog/sessions/`. Active work: Phase 7 partial (Budget BE done, FE WIP) + Phase 8 (Budget refinement).
> 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)
@ -102,34 +102,87 @@ Session log: `2026-04-28-chot-session-4-budget.md`.
### E. Pending migrations
- [ ] `AddPePaymentTermFields` (nếu chốt UX tách field)
- [ ] `AddPeDepartmentOpinions` (nếu chọn Option A header columns)
- [ ] `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 + integration (Session 5 active)
## Phase 8 — Budget FE + PE/HD integration (Session 5 done)
### A. FE Budget pages — copy pattern PE
### A. FE Budget pages — done ✅
- [ ] `fe-admin/src/types/budget.ts` (types + enum BudgetPhase + ApprovalDecision reuse)
- [ ] `fe-admin/src/pages/budgets/BudgetsListPage.tsx` (3-panel `[320px_1fr_360px]`)
- [ ] `fe-admin/src/pages/budgets/BudgetCreatePage.tsx` (form Header)
- [ ] `fe-admin/src/components/budgets/BudgetDetailTabs.tsx` (Thông tin / Hạng mục)
- [ ] `fe-admin/src/components/budgets/BudgetWorkflowPanel.tsx` (Panel 3 timeline + transition + comment)
- [ ] Mirror tất cả sang `fe-user/`
- [ ] App.tsx routes `/budgets`, `/budgets/new`, `/budgets/:id` cả 2 app
- [ ] Menu resolver `Bg_*` (Bg_List → `/budgets`, Bg_Pending → `/budgets?phase=Pending`, Bg_Create → `/budgets/new`)
- [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
### B. PE/Contract → Budget integration — done ✅
- [ ] **PE form** thêm field `Ngân sách` select Budget (filter Phase=DaDuyet, NamNganSach=current, ProjectId match)
- [ ] **Contract form** tương tự — link sang Budget cho đối chiếu chi phí
- [ ] PE Detail tab thêm cột "So với ngân sách" — compute từ BudgetDetail tương ứng (match GroupCode + ItemCode)
- [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. Budget refinement (when needed)
### C. PE Workflow Designer admin UI — done ✅
- [ ] Budget MaNganSach atomic sequence — chốt format chính thức rồi thêm `AddBudgetCodeSequences` migration + `IBudgetCodeGenerator` SERIALIZABLE pattern (mirror Contract/PE)
- [ ] Budget versioned workflow — admin config UI nếu Solutions cần custom step approver beyond default 3-step
- [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 + CI gate — 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] CI gate `.gitea/workflows/deploy.yml` — 2 step `dotnet test` trước build, fail → no deploy
- [x] Total 71 test pass / ~2s
- [x] Session log + commit + push
## 📝 Phase 9 — UAT + Ops + carry over (Session 6+ active)
### 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
- [ ] fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi"
### 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)
@ -142,7 +195,7 @@ Quy tắc: `docs/rules.md §9`. Audit định kỳ mỗi đầu tháng — workf
Cron task `solution-erp-skill-audit-monthly` fire 9:00 AM ngày 1 mỗi tháng.
## 📦 Post-launch (Phase 9+ — future)
## 📦 Post-launch (Phase 10+ — future)
- [ ] **Email outbox** (MailKit + SMTP) — blocked chờ SMTP config
- [ ] E-signature integration (VNPT CA hoặc FPT CA)

View File

@ -0,0 +1,266 @@
# Session log — 2026-04-29 (Phase 8 — Budget FE complete + PE feature gap đóng + Tests Phase 1-2)
**Topic:** FE Budget pages 3-panel + PE/HD-Budget integration + PE Detail UI restructure + PE Workflow Designer + Ý kiến 4 phòng ban + Tests Phase 1-2 (Domain + Infra) + CI gate.
**Commits (6 total session 5):**
- `df12fb1` — FE Budget pages 3-panel cả 2 app
- `61e5d4d` — PE/Contract → Budget integration + cột "So với ngân sách" PE matrix
- `7e36241` — PE Detail UI restructure theo spec PHIẾU TRÌNH KÝ (4 section đánh số)
- `5d94bb4` — PE Workflow Designer admin UI + Ý kiến 4 phòng ban (Migration 15)
- `d3f9346` — Tests Phase 1: Domain unit tests + CI gate
- `df5988b` — Tests Phase 2: Code generator format + sequence (SQLite in-memory)
- (Session log này — commit chốt MD session 5)
**Migration:** 15 `AddPurchaseEvaluationDepartmentOpinions` — 1 bảng UNIQUE PEId+Kind cho 4 phòng ban sign-off.
## A. FE Budget pages 3-panel (commit `df12fb1`)
Mirror pattern PE 3-panel cho cả `fe-admin` + `fe-user`:
| File | Mô tả |
|---|---|
| `types/budget.ts` | BudgetPhase 5-state enum + DTO types (BudgetListItem/DetailRow/Approval/Bundle/DetailBody) |
| `pages/budgets/BudgetsListPage.tsx` | 3-panel `[340px_1fr_360px]` + filter Phase + Năm + `?phase=Pending` alias filter ChoCCM/ChoCEO + readOnly mode (menu Duyệt) + BudgetDetailPage fullpage mobile |
| `pages/budgets/BudgetCreatePage.tsx` | Form Header (Tên/Năm/Dự án/Phòng ban/Mô tả). Edit mode khóa Project+Department |
| `components/budgets/BudgetDetailTabs.tsx` | Section Thông tin Header + Section Hạng mục table CRUD inline (Add/Edit/Delete dialog auto-compute ThanhTien=KL×ĐG). Export `BudgetApprovalsSection` + `BudgetHistorySection` cho Panel 3 reuse |
| `components/budgets/BudgetWorkflowPanel.tsx` | Panel 3 timeline activePhases visual + nextPhases buttons (Approve xanh / Reject đỏ / Trả về vàng) + Dialog xác nhận có comment + Approvals + Changelog |
App.tsx 3 route mới `/budgets`, `/budgets/new`, `/budgets/:id` cả 2 app. Layout resolver `Bg_List`/`Bg_Create`/`Bg_Pending` + root `Budgets` icon Wallet.
## B. PE/Contract → Budget integration (commit `61e5d4d`)
### BE (6 file)
- `Budgets/Dtos/BudgetDtos.cs`: + `BudgetSummaryDto` (compact header snapshot reuse cho PE & Contract)
- `PE Dtos + Features`: + `BudgetId?` + `Budget?` vào DetailBundle. Create/Update PE commands + validate cùng Project + Phase=DaDuyet
- `Contract Dtos + Features`: same pattern
- `CreateContractFromEvaluation`: carry forward `pe.BudgetId → contract.BudgetId`
### FE (10 file)
- PE & Contract Create form: Select **"Ngân sách"** filter Phase=DaDuyet & Project match (BE-side filter qua `/budgets?projectId=&phase=4`)
- PE InfoTab + Contract Edit: hiển thị Budget link clickable `→ /budgets?id=`
- PE ItemsTab matrix: cột **"NS link · Δ"** chỉ hiện khi `ev.budgetId`, match per-row qua key `groupCode|itemCode` (fetch `/budgets/{id}` riêng), footer aggregate (xanh dưới / đỏ vượt / xám khớp)
## C. PE Detail UI restructure (commit `7e36241`)
Match form chính thức **PHIẾU TRÌNH KÝ CHỌN TP/NCC** với 4 section đánh số:
```
1. Thông tin gói thầu (a Tên + b Dự án + Địa điểm/Mô tả compact)
2. Chọn NCC / TP (a NCC chọn / b Ngân sách / c Giá chào thầu auto-compute / d Bản so sánh)
3. NCC / TP tham gia ({n}) (table 5 cột UX web)
4. Hạng mục + Báo giá ({n}) (matrix + cột "NS link · Δ")
5. Ý kiến 4 phòng ban (sign-off) (2x2 grid OpinionBox — Section thêm trong commit kế)
```
**Section 2.c "Giá chào thầu"** auto-compute: lookup `winnerSupplierRowId` qua `selectedSupplierId`, sum quotes có `purchaseEvaluationSupplierId === winnerRowId`. Hiện "—" khi chưa chọn NCC hoặc chưa nhập báo giá.
**Section 2.d "Bản so sánh"** dùng filter `purchaseEvaluationSupplierId === null` (file tổng, không gắn NCC cụ thể).
`FormRow` helper mới (label 176px + value flex) thay cho dl grid 2-col cũ. Drop helper `Field` cũ.
## D. PE Workflow Designer admin UI + Ý kiến 4 phòng ban (commit `5d94bb4`)
### PE Workflow Designer
- BE `Application/PurchaseEvaluations/PeWorkflowAdminFeatures.cs` ~250 LOC mirror Contract WorkflowAdminFeatures (GetOverview + CreateNewVersion deactivate cũ atomic)
- BE `Api/Controllers/PeWorkflowsController.cs` 2 endpoint reuse policy `Workflows.Read` + `Workflows.Create`
- FE `pages/system/PeWorkflowsPage.tsx` ~500 LOC: Landing 2-card grid → TypePanel khi pick code → DefinitionCard active+history + PeWorkflowDesigner dialog (clone từ existing + edit Code/Name/Description + add/remove steps + +Role / +User approvers)
- App.tsx route `/system/pe-workflows` + `/system/pe-workflows/:typeCode`. Resolver `PeWf_<Code>` từ session 3 đã trỏ.
### Ý kiến 4 phòng ban PE
**Migration 15** `AddPurchaseEvaluationDepartmentOpinions`:
```sql
CREATE TABLE PurchaseEvaluationDepartmentOpinions (
Id, PurchaseEvaluationId FK Cascade,
Kind INT NOT NULL, -- 1=PheDuyet, 2=Ccm, 3=MuaHang, 4=SmPm
Opinion NVARCHAR(2000) NULL,
SignedAt DATETIME2 NULL,
UserId UNIQUEIDENTIFIER NULL,
UserName NVARCHAR(200) NULL,
-- AuditableEntity columns
);
CREATE UNIQUE INDEX ON (PurchaseEvaluationId, Kind); -- max 1 row mỗi loại phòng ban per phiếu
```
- Domain entity `PurchaseEvaluationDepartmentOpinion` + enum `PeDepartmentKind`
- Application `PeDepartmentOpinionFeatures.cs`: Upsert pattern (sign=true → set SignedAt+UserId+UserName denorm, sign=false → giữ chữ ký cũ chỉ update text), Delete admin override
- API `POST /api/purchase-evaluations/{id}/opinions` + `DELETE .../opinions/{kind}`
- FE Section "5. Ý kiến 4 phòng ban (sign-off)" 2x2 grid `OpinionBox`:
- Read mode (menu Duyệt readOnly): hiển thị text + chữ ký
- Edit mode: textarea + 2 button "Lưu text" / "Lưu & Ký"
- Badge "Đã ký" emerald + tên người ký + ngày
## E. Tests Phase 1 — Domain unit tests + CI gate (commit `d3f9346`)
```
tests/SolutionErp.Domain.Tests/
├── Contracts/WorkflowPolicyTests.cs (~17 test)
├── PurchaseEvaluations/PurchaseEvaluationPolicyTests.cs (~17 test)
├── Budgets/BudgetPolicyTests.cs (~13 test)
└── SolutionErp.Domain.Tests.csproj (xUnit 2.9.3 + FluentAssertions 7.2)
```
**Coverage:**
- Standard 9-phase + SkipCcm 7-phase (HĐ): role transitions, CCM check, BOD signing, terminal phase, reject paths
- Registry: DefaultPolicyName per ContractType (7 type) + bypass flag override Standard→SkipCcm
- FromDefinition versioned: build từ ordered steps + reject path back to draft + TuChoi auto-add + UserKindApprover populate UserTransitions
- NccOnly (PE-A) 3-step + NccWithPlan (PE-B) 5-step: skip ChoDuAn/ChoCEODuyetPA chỉ ở B
- BudgetPolicy Default 3-step + DaDuyet/TuChoi terminal + SLA spec verify (5d/3d/2d)
**CI gate** `.gitea/workflows/deploy.yml`:
```yaml
- name: Run unit tests (Domain)
run: dotnet test tests/SolutionErp.Domain.Tests/...
# Fail → exit $LASTEXITCODE → no deploy
```
54 test pass / 6.4s. Verify local: `dotnet test SolutionErp.slnx`.
## F. Tests Phase 2 — Code generator format + sequence (commit `df5988b`)
```
tests/SolutionErp.Infrastructure.Tests/
├── Common/SqliteDbFixture.cs (Test fixture + TestApplicationDbContext)
├── Services/ContractCodeGeneratorTests.cs (~10 test)
├── Services/PurchaseEvaluationCodeGeneratorTests.cs (~7 test)
└── SolutionErp.Infrastructure.Tests.csproj (+ EF Sqlite 10)
```
### Setup SQLite in-memory
- `TestApplicationDbContext` subclass override `OnModelCreating` — iterate model, replace bất kỳ column type chứa "max" → "TEXT" (SQLite không support `nvarchar(max)`)
- `FixedDateTime` stub `IDateTime` (control year boundary deterministic)
- Shared `SqliteConnection` open trong fixture, EnsureCreated() từ DbContext model
### Coverage
- Format RG-001 cho 5 ContractType (HĐTP/HĐGK/NCC/HĐDV/MB) — `{Project}/{TypeCode}/SOL&{Supplier}/{Seq}`
- Framework HĐ (NguyenTacNCC + NguyenTacDV) đặc biệt — `{Year}/{TypeCode}/SOL&{Supplier}/{Seq}` (year scope thay vì project)
- Sequence increment per prefix (`/01``/02``/03`)
- Different prefixes (project / supplier khác) có sequence độc lập
- Year boundary cho framework — sang 2027 prefix mới reset seq
- PE format `PE/{YYYY}/{A|B}/{Seq:D3}` — 3-digit padding test với 12 phiếu
- PE Type A và B sequence độc lập trong cùng năm
- PersistsSequenceRow LastSeq + UpdatedAt verification
**CI gate** thêm step thứ 2 — fail-fast tương tự Domain.
17 test pass / 2s. Total session 5 test count = **71** (54 Domain + 17 Infra).
## G. Stats sau session 5
| | Trước S5 | Sau S5 |
|---|---:|---:|
| BE LOC | ~11750 | **~13050** (+1300 PE WF Designer + Opinion + Budget integration) |
| DB tables | 51 | **52** (+1 PEDeptOpinions) |
| API endpoints | ~124 | **~128** (+2 PE WF + 2 Opinion) |
| Migrations | 14 | **15** |
| FE pages | ~26 | **~31** (+5 Budget + 1 PeWorkflowsPage) |
| **Tests** | 0 | **71** (54 Domain + 17 Infra) |
| CI gate | 1 (build) | **2 step test gate + build** |
| Commits | ~75 | **~82** (+6 session 5 + chốt MD) |
## H. Cảnh báo session 6
1. **CI test gate active** — mỗi commit push trigger 2 step `dotnet test`. Test fail → NO deploy. Workflow mới: code → `dotnet test SolutionErp.slnx` local → commit → push.
2. **Chưa xóa binding cũ `.huypham.vn`** — fallback active. Sau verify stable: `.\migrate-domains.ps1 -RemoveOld -SkipCert`.
3. **win-acme scheduled task "unhealthy"** — fix trước cert expire 2026-06-18.
4. **Login email default** vẫn `admin@solutionerp.local` (chỉ rebrand demo user).
5. **Atomic sequence Budget chưa chốt** — Random.Shared race risk thấp nhưng không zero.
6. **Versioned workflow Budget chưa có** — hardcoded `BudgetPolicy.Default` 3-step.
7. **Export phiếu PDF/Excel PE** pending vô thời hạn (user nói không quan trọng lắm).
## I. Lessons learned session 5
1. **Test pyramid bottom-heavy hợp solo dev** — 71 test (54 Domain pure + 17 Infra SQLite) chạy < 3s. Bỏ E2E, tránh maintenance overhead.
2. **SQLite in-memory cho code generator test** đủ test format + sequence + year boundary, KHÔNG đủ test race condition thực (cần SQL Server thật cho `IsolationLevel.Serializable` strict). Workaround tốt cho CI runner không LocalDB.
3. **TestApplicationDbContext subclass override** pattern sạch nhất để adapt SQL Server-specific model (`nvarchar(max)`) cho SQLite không touch production DbContext.
4. **CI gate ROI cao cho solo dev** bug Domain logic recurring (workflow phase, role transition) sẽ block deploy ngay. ~3.5 ngày upfront cho 5 phase, thể stop Phase 2 (1.5 ngày) nếu Phase 3-5 ROI chưa .
5. **PE Detail UI restructure theo spec form giấy** match 4 section đánh số PHIẾU TRÌNH giúp UX consistent giữa fill web print PDF (tương lai).
6. **Sign-off 4 phòng ban dùng table riêng thay vì 12 column header** UNIQUE(PEId, Kind) bảo vệ max 1 row mỗi phòng, dễ query "phòng nào chưa ", dễ extend nếu thêm kind.
7. **Workflow user mới end-of-task** = `dotnet test SolutionErp.slnx` commit push. CI tự run test gate trước build/deploy.
## J. Files touched session 5
```
src/Backend/SolutionErp.Domain/PurchaseEvaluations/
├── PurchaseEvaluation.cs (mod: + DepartmentOpinions navigation)
└── PurchaseEvaluationDepartmentOpinion.cs (NEW — entity + enum PeDepartmentKind)
src/Backend/SolutionErp.Application/Contracts/
├── ContractFeatures.cs (mod: + BudgetId in commands + GetQuery load Budget)
└── Dtos/ContractDtos.cs (mod: + BudgetId + Budget? trong ContractDetailDto)
src/Backend/SolutionErp.Application/PurchaseEvaluations/
├── PurchaseEvaluationFeatures.cs (mod: + BudgetId + DepartmentOpinions trong bundle)
├── PeWorkflowAdminFeatures.cs (NEW — ~250 LOC mirror Contract pattern)
├── PeDepartmentOpinionFeatures.cs (NEW — Upsert + Delete)
├── CreateContractFromEvaluationFeatures.cs (mod: carry forward BudgetId)
└── Dtos/PurchaseEvaluationDtos.cs (mod: + BudgetId + DepartmentOpinions list)
src/Backend/SolutionErp.Application/Budgets/
└── Dtos/BudgetDtos.cs (mod: + BudgetSummaryDto)
src/Backend/SolutionErp.Application/Common/Interfaces/
└── IApplicationDbContext.cs (mod: + DbSet PEDepartmentOpinions)
src/Backend/SolutionErp.Infrastructure/Persistence/
├── ApplicationDbContext.cs (mod: + DbSet)
├── Configurations/PurchaseEvaluationConfiguration.cs (mod: + DepartmentOpinions cascade + UNIQUE)
└── Migrations/20260429041117_AddPurchaseEvaluationDepartmentOpinions.{cs,Designer.cs} (NEW)
src/Backend/SolutionErp.Api/Controllers/
├── PurchaseEvaluationsController.cs (mod: + 2 opinion endpoints)
└── PeWorkflowsController.cs (NEW)
fe-admin/src/ (FE mod files mirror lên fe-user)
├── types/budget.ts (NEW)
├── types/contracts.ts (mod: + BudgetSummary)
├── types/purchaseEvaluation.ts (mod: + Budget + DepartmentOpinion + Kind enum)
├── pages/budgets/BudgetsListPage.tsx (NEW)
├── pages/budgets/BudgetCreatePage.tsx (NEW)
├── pages/contracts/ContractCreatePage.tsx (mod: + Budget select)
├── pages/pe/PurchaseEvaluationCreatePage.tsx (mod: + Budget select)
├── pages/system/PeWorkflowsPage.tsx (NEW)
├── components/budgets/BudgetDetailTabs.tsx (NEW)
├── components/budgets/BudgetWorkflowPanel.tsx (NEW)
├── components/pe/PeDetailTabs.tsx (mod: 4 section restructure + Budget col + Opinion section)
├── App.tsx (mod: + 5 route Budget + 2 PE WF)
└── components/Layout.tsx (mod: + Bg_* resolver)
tests/SolutionErp.Domain.Tests/ (NEW Phase 1)
├── SolutionErp.Domain.Tests.csproj
├── Contracts/WorkflowPolicyTests.cs (~17 test)
├── PurchaseEvaluations/PurchaseEvaluationPolicyTests.cs (~17 test)
└── Budgets/BudgetPolicyTests.cs (~13 test)
tests/SolutionErp.Infrastructure.Tests/ (NEW Phase 2)
├── SolutionErp.Infrastructure.Tests.csproj (+ EF Sqlite 10)
├── Common/SqliteDbFixture.cs (TestApplicationDbContext + FixedDateTime)
├── Services/ContractCodeGeneratorTests.cs (~10 test)
└── Services/PurchaseEvaluationCodeGeneratorTests.cs (~7 test)
SolutionErp.slnx (mod: + folder /tests/ với 2 project)
.gitea/workflows/deploy.yml (mod: + 2 step dotnet test gate)
docs/STATUS.md (mod: header + 6 row Recently Done + cumulative cột S5)
docs/HANDOFF.md (mod: TL;DR + Cảnh báo S6 + Priority 0 S6 + Phase status)
docs/changelog/migration-todos.md (mod: Phase 8 done + Phase 9 active S6)
docs/architecture.md (mod: + §11 Testing strategy + renumber §12 Liên quan)
docs/database/schema-diagram.md (mod: Migration 15 row + 52 tables + §13 PEDeptOpinions)
docs/CLAUDE.md (root + docs/) (mod: modules table + Tests row + Roadmap S5/S6 + Tests section)
.claude/skills/ef-core-migration/SKILL.md (mod: 15 migrations + 52 tables + Phase 8 update)
docs/changelog/sessions/2026-04-29-chot-session-5-budget-fe-pe-tests.md (NEW — file này)
~/.claude/projects/.../memory/project_solution_erp.md (mod: Phase 8 summary + Session 6 priority)
```

View File

@ -483,8 +483,9 @@ COMMIT;
| **12** | **`AddPurchaseEvaluations`** | **10 bảng module Duyệt NCC: PurchaseEvaluations + Suppliers + Details + Quotes + Approvals + Changelogs + Attachments + WorkflowDefinitions + WorkflowSteps + WorkflowStepApprovers** |
| **13** | **`AddPurchaseEvaluationCodeSequences`** | **PurchaseEvaluationCodeSequences (Prefix PK, atomic seq mirror ContractCodeSequences). Format MaPhieu PE/{YYYY}/{A\|B}/{Seq:D3}** |
| **14** | **`AddBudgets`** | **4 bảng module Ngân sách: Budgets + BudgetDetails + BudgetApprovals + BudgetChangelogs. + nullable FK index Contract.BudgetId & PurchaseEvaluation.BudgetId** |
| **15** | **`AddPurchaseEvaluationDepartmentOpinions`** | **1 bảng `PurchaseEvaluationDepartmentOpinions` (UNIQUE PEId+Kind, max 1 row mỗi loại phòng ban per phiếu — UPDATE in-place, audit qua Changelog)** |
Tổng: **51 bảng** (+ `__EFMigrationsHistory` hệ thống).
Tổng: **52 bảng** (+ `__EFMigrationsHistory` hệ thống).
## 8bis. Bảng mới sau Migration 9-11
@ -681,7 +682,39 @@ Không trigger DB → app layer tính lại sau mỗi mutation Detail. Đơn gi
- **`AddBudgetCodeSequences`** — atomic SERIALIZABLE sequence khi format chốt chính thức (mirror Contract/PE pattern).
- **`AddBudgetVersionedWorkflow`** — nếu Solutions cần admin config UI thay vì hardcoded policy. Pattern: `BudgetWorkflowDefinitions` + `Steps` + `StepApprovers` + `Budget.WorkflowDefinitionId?` pinned at create.
## 13. Liên quan
## 13. PE Department Opinion (Migration 15 — 1 bảng mới)
Section "Ý kiến 4 phòng ban" trên PHIẾU TRÌNH KÝ CHỌN TP/NCC — sign-off block 4 box (Phê duyệt / P.CCM / P.MuaHàng / SM-PM). Lưu thành table riêng (1:N với PE) thay vì bloat 12 column nullable trong PE header.
### Core (1 bảng):
| Bảng | Mục đích |
|---|---|
| `PurchaseEvaluationDepartmentOpinions` | PEId FK Cascade + **Kind** enum `PeDepartmentKind` (1=PheDuyet, 2=Ccm, 3=MuaHang, 4=SmPm) + Opinion text(2000) + SignedAt? + UserId? + UserName denorm. UNIQUE(PEId, Kind) — max 1 row mỗi phòng ban per phiếu. AuditableEntity. |
### Upsert behavior:
- **Lần đầu (Add):** entity mới với Kind = X, Opinion = text, SignedAt = `Sign ? UtcNow : null`
- **Lần sau (Update):** UPDATE in-place (gốc giữ Id), Opinion = new text. Sign=true → cập nhật SignedAt+UserId; Sign=false → giữ chữ ký cũ (chỉ update text)
- Audit qua `PurchaseEvaluationChangelog` mỗi lần Upsert/Delete
```sql
CREATE TABLE PurchaseEvaluationDepartmentOpinions (
Id UNIQUEIDENTIFIER PRIMARY KEY,
PurchaseEvaluationId UNIQUEIDENTIFIER NOT NULL,
Kind INT NOT NULL,
Opinion NVARCHAR(2000) NULL,
SignedAt DATETIME2 NULL,
UserId UNIQUEIDENTIFIER NULL,
UserName NVARCHAR(200) NULL,
-- audit
CreatedAt, CreatedBy, UpdatedAt, UpdatedBy, IsDeleted, DeletedAt, DeletedBy,
CONSTRAINT FK_... FOREIGN KEY (PurchaseEvaluationId) REFERENCES PurchaseEvaluations(Id) ON DELETE CASCADE
);
CREATE UNIQUE INDEX IX_PEDeptOpinions_PEId_Kind ON PurchaseEvaluationDepartmentOpinions (PurchaseEvaluationId, Kind);
```
## 14. Liên quan
- [`database-guide.md`](database-guide.md) — conventions + migration workflow + cheatsheet đầy đủ
- [`../architecture.md`](../architecture.md) — layered architecture + data flow