Files
solution-erp/docs/changelog/sessions/2026-04-29-chot-session-5-budget-fe-pe-tests.md
pqhuy1987 52999f33fa
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Failing after 22s
[CLAUDE] Docs: chốt session 5 — Budget FE + PE feature complete + Tests Phase 1-2 + CI gate
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>
2026-04-29 13:50:54 +07:00

267 lines
16 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.

# 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)
```