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

16 KiB
Raw Blame History

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:

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:

- 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 có LocalDB.

  3. TestApplicationDbContext subclass override — pattern sạch nhất để adapt SQL Server-specific model (nvarchar(max)) cho SQLite mà 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, có thể stop ở Phase 2 (1.5 ngày) nếu Phase 3-5 ROI chưa rõ.

  5. PE Detail UI restructure theo spec form giấy — match 4 section đánh số PHIẾU TRÌNH KÝ giúp UX consistent giữa fill web và 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 ký", 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)