Files
solution-erp/docs/HANDOFF.md
pqhuy1987 12daa7f6b0
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m16s
[CLAUDE] Docs: Session 17 schema mới ApprovalWorkflowsV2 (Chunk D)
Update STATUS row mới + HANDOFF brief Session 17 + CLAUDE.md count
22 migration / 58 bảng.

Tóm tắt session:
- User chốt sau S16: schema flat Mig 21 vẫn không đúng intent → yêu
  cầu viết lại + thêm Menu mới "Duyệt NCC (Mới)" với cấu trúc explicit
  Quy trình > Bước (Phòng) > Cấp (NV cụ thể).
- 4 commit (3 chunk per-commit + docs): Mig 22 + 3 bảng mới +
  Application CQRS + API + FE Designer mới.
- PE/Contract Service CHƯA wire — vẫn pin Mig 21 legacy.
- Sau UAT UIUX OK → Session sau pin ApprovalWorkflowId song song +
  Service rewrite + migrate data + drop legacy.
- Backward compat 100%, 77 test pass no regression.
2026-05-08 12:48:06 +07:00

64 KiB
Raw Blame History

HANDOFF — Brief 5 phút cho session tiếp theo

Last updated: 2026-05-08 (Session 17 — 🎯 SCHEMA MỚI ApprovalWorkflowsV2 + Menu "Duyệt NCC (Mới)" UAT. 4 commit. Schema riêng trên Mig 22. Cấu trúc Bước (Phòng) > Cấp (NV cụ thể qua ApproverUserId — KHÔNG OR-of-many). Chỉ admin Designer xong, Service PE/Contract chưa wire qua schema mới — UAT UIUX trước.)

TL;DR Session 17 (08/05 — Schema mới UAT trước khi drop legacy)

User chốt sau Session 16: schema flat Mig 21 vẫn không đúng intent. Yêu cầu viết lại toàn bộ chỗ Quy trình Duyệt + thêm Menu mới "Duyệt NCC (Mới)" với cấu trúc rõ ràng:

Mã Quy trình - Tên Quy trình
  * Bước 1 - Phòng A
    * Cấp 1 - NV X        ← 1 user CỤ THỂ qua ApproverUserId
    * Cấp 2 - NV Y
  * Bước 2 - Phòng B
    * Cấp 1 - NV Z

Khác Mig 21: mỗi Cấp = 1 NV chính xác, KHÔNG OR-of-many group Dept+PositionLevel/Role/User.

4 commit (3 chunk per-commit + docs):

Chunk A (c847dc0) — Domain + EF + Mig 22 + Menu

  • Domain ApprovalWorkflowsV2/ApprovalWorkflow.cs — 3 entity (ApprovalWorkflow + Step + Level) + enum ApprovalWorkflowApplicableType (DuyetNcc=1 / DuyetNccPhuongAn=2 / Contract=3)
  • EF ApprovalWorkflowConfiguration.cs — UNIQUE (Code, Version), FK Cascade Step→Workflow + Level→Step, FK Restrict Department + ApproverUserId
  • ApplicationDbContext +3 DbSet
  • Migration 22 AddApprovalWorkflowsV2 — 3 CREATE TABLE + 1 UNIQUE + 4 INDEX. Applied cả _Design + _Dev LocalDB
  • DbInitializer SeedMenusAsync: +menu ApprovalWorkflowsV2 root dưới System (icon Workflow) + leaf AwV2_DuyetNcc (icon FileCheck, label "Duyệt NCC (Mới)")
  • MenuKeys.cs +2 const trong All array

Chunk B (f6047d5) — Application CQRS + API

  • Application/ApprovalWorkflowsV2/ApprovalWorkflowV2AdminFeatures.cs:
    • GetAwAdminOverviewQuery(ApplicableType?) — load 3-level Include + dept/user names map
    • CreateAwDefinitionCommand + Validator — auto-increment Version theo Code, deactivate active version cùng ApplicableType
    • DeleteAwDefinitionCommand — UAT helper unconditional (chưa pin)
    • DTO AwDefinition/AwStep/AwLevel + AwTypeSummary
  • IApplicationDbContext +3 DbSet
  • Api/Controllers/ApprovalWorkflowsV2Controller — route /api/approval-workflows-v2, GET ?applicableType=N | POST | DELETE/{id}, reuse policy Workflows.Read + Workflows.Create

Chunk C (2781c7e) — FE Designer

  • fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx (~480 LOC)
    • Overview cards Active+History per ApplicableType
    • DefinitionCard read-only: Bước (badge phòng emerald) → Cấp (badge violet C1/C2 + tên NV + email)
    • Designer dialog: Mã/Tên/Mô tả + Add/Remove Step + reorder (chevron up/down) + Add/Remove Level + Select Phòng + Select NV duyệt
    • Validate: mỗi Step ≥1 Level, mỗi Level phải có ApproverUserId
    • Auto-assign code mặc định theo type: QT-DN-V2-001 / QT-DN-PA-V2-001 / QT-HD-V2-001
  • Layout.tsx resolver +ApprovalWorkflowsV2 root → /system/approval-workflows-v2, +AwV2_ leaf → /system/approval-workflows-v2/<code>
  • App.tsx +2 route
  • menuKeys.ts +2 const sync với BE

Chunk D — Docs (current)

STATUS + HANDOFF + project_solution_erp.md memory.

⚠️ Điều quan trọng cho Session 18+

  1. PE/Contract Service CHƯA wire qua schema mới — vẫn pin WorkflowDefinitionId từ Mig 21 (legacy). Sau khi user UAT UIUX OK, Session sau cần:
    • Thêm pinning ApprovalWorkflowId vào PE/Contract entity (nullable, song song WorkflowDefinitionId)
    • Service rewrite: nếu pin V2 → iterate ApprovalWorkflowSteps OrderBy Order → ApprovalWorkflowLevels OrderBy Order → match actor.Id == level.ApproverUserId (chính xác 1-1)
    • Migration data từ legacy → V2 (manual hoặc seed sample)
    • Drop legacy WorkflowDefinition + WorkflowStep + Approver tables sau khi không phiếu nào pin
  2. Backward compat 100% — schema mới chỉ thêm vào, không sửa cũ. Phiếu PE/Contract đang chạy không bị ảnh hưởng.
  3. 77 test pass giữ nguyên — không thêm test mới (Designer UI chưa critical, defer khi Service wire xong → test policy + match approver).

TL;DR Session 16 (08/05 — Drastic refactor flat workflow EXECUTE)

Resume từ Session 15 defer plan. Per memory feedback_drastic_refactor_scope.md: dedicated session, fresh context, conservative buffer.

Spec: Workflow flat list (Phòng × Cấp × Approvers). Mỗi step = 1 (Phòng × Cấp). Service iterate steps OrderBy Order, advance pointer. Phase enum simplify ChoDuyet=10. Pin WorkflowDefinitionId.

2 chunk per-commit (5-6 chunk plan rút gọn vì BE tightly coupled):

Chunk A (dbb0089) — All BE: Domain + Mig 21 + Service + Tests

Domain entities:

  • Phase enum (PE + Contract): + ChoDuyet=10 generic intermediate. Legacy 2-6 + 98 deprecated (giữ enum cho data cũ).
  • WorkflowStep + DepartmentId Guid? FK Restrict + PositionLevel int?
  • PurchaseEvaluation/Contract + CurrentWorkflowStepIndex int? + RejectedAtStepIndex int?
  • DROP class WorkflowStepInnerStep + nav (PE + Contract)
  • DROP *DepartmentApproval.InnerStepId column

EF Configurations:

  • DROP InnerStep config (PE + Contract) → table dropped
  • WorkflowStep config + DeptId/PositionLevel + FK Restrict
  • DepartmentApprovals: restore simple unique non-filtered (Mig 19/20 filtered split reverse)

ApplicationDbContext: DROP DbSet<*WorkflowStepInnerStep> × 2

Migration 21 RefactorWorkflowToFlatModel GỘP:

  • 4 ALTER (PE/Contract +CurrentStepIndex +RejectedAtStepIndex)
  • 2 ALTER (WorkflowStep +DepartmentId +PositionLevel) PE + Contract
  • DROP TABLE × 2 (PEWorkflowStepInnerSteps + WorkflowStepInnerSteps Mig 18+20)
  • DROP COLUMN × 2 (*DeptApproval.InnerStepId)
  • DROP filtered indexes Mig 19/20
  • RESTORE simple UNIQUE (TargetId, Phase, Dept, Stage) non-filtered × 2

Service rewrite (PE + Contract WorkflowService.TransitionAsync):

  • DangSoanThao → ChoDuyet (Drafter trình, init idx=0)
  • ChoDuyet → ChoDuyet (advance idx per approve)
  • ChoDuyet → DaDuyet/DaPhatHanh (idx ≥ steps.Count → terminal, gen mã HĐ Contract)
  • ChoDuyet → DangSoanThao (Trả lại — save RejectedAtStepIndex)
  • ChoDuyet → TuChoi (Từ chối — khoá vĩnh viễn)
  • Resume Drafter (DangSoanThao + RejectedAtStepIndex≠null) → ChoDuyet jump-back
  • Match approver: actor.Dept == step.Dept AND actor.PositionLevel >= step.PositionLevel (OR-of-many cùng cấp/dept) OR Approvers.Kind=User|Role match
  • Admin role bypass policy

App CQRS: WorkflowStepDto + WorkflowStepInput drop InnerStep, add DepartmentId/DepartmentName/PositionLevel (PE + Contract mirror).

Tests:

  • DROP PeNStageApprovalTests.cs (6) + ContractNStageApprovalTests.cs (6) + PeTwoStageApprovalTests.cs (7) — legacy
  • UPDATE PeWorkflowAdminTests signature for new flat input
  • 96 → 77 test pass (-19 legacy)

3-file rule Mig 21 commit đủ (.cs + Designer + Snapshot).

Chunk B (88a5be1) — FE Designer + types

PeWorkflowsPage + WorkflowsPage rewrite (~210 LOC each):

  • Drop InnerStepDto + EditInnerStep types
  • Drop PHASE_OPTIONS (auto-assign ChoDuyet=10 behind scenes)
  • StepDto + EditStep + departmentId, departmentName, positionLevel
  • Designer step UI rewrite: Tên + Phòng Select + Cấp Select + SLA + Approvers (Role/User optional fallback). Drop InnerSteps sub-section.
  • DefinitionCard view: badge Phòng emerald + Cấp NV/PP/TP violet
  • Save payload: phase=10 (ChoDuyet)
  • Hint amber: "User cùng Phòng + Cấp ≥ step → duyệt được (OR-of-many)"

types/purchaseEvaluation.ts (fe-admin + fe-user mirror): + ChoDuyet=10 enum + label "Đang duyệt" + color amber. Legacy 2-6 + 98 keep.

Chunk C (FE PeWorkflowPanel) SKIPPED — existing UI compatible (workflow.nextPhases BE-driven, 3-button Trả lại/Từ chối Session 14 reuse với target=DangSoanThao/TuChoi pattern).

Verify

  • dotnet build SolutionErp.slnx 0 error
  • dotnet ef database update Mig 21 LocalDB applied OK
  • dotnet test 77 pass (54 Domain + 23 Infra)
  • npm build fe-admin + fe-user pass

Cumulative sau Session 16

Trước S16 Sau S16
BE LOC ~15800 ~15500 (-300 service simplified)
Migrations 20 21
DB tables 57 55 (-2 InnerStep tables)
Tests 96 77 (-19 legacy N-stage/2-stage)
FE pages 32 32 (rewrite existing 2 designer)

⚠️ CẢNH BÁO Session 17+

  1. UAT live test — workflow flat ready. Tạo new workflow definition qua /system/pe-workflows/:typeCode với 3 phòng × N cấp setup. Verify Drafter trình → cấp 1 phòng A → cấp 2 phòng A → cấp 1 phòng B → ... → DaDuyet flow.

  2. Old PE/HĐ pinned legacy workflow definitions (phase=ChoPurchasing/ChoCCM/etc) — service rewrite chỉ handle ChoDuyet=10 + DangSoanThao/DaDuyet. Old data ở phase 2-6 sẽ stuck (admin manual transition required). Recommend: data migration script convert old workflow → new flat model (defer).

  3. Approver explicit (Role/User Approvers list) — fallback nếu user không match Dept+PositionLevel của step. Cho phép user external (không thuộc dept) duyệt qua Role match (vd Admin) hoặc User explicit.

  4. Bypass cấp dưới cùng dept — User TP với CanBypassReview=true cùng dept và PositionLevel cao hơn step.PositionLevel → duyệt qua. KHÔNG batch upsert NV+PP rows như Mig 18 N-stage trước (đơn giản hóa: 1 step approve = 1 row).

  5. N-stage tests dropped — 19 test legacy (Mig 18, 20 N-stage + Mig 16 2-stage). Có thể viết test mới cho flat workflow flow nếu UAT phát sinh bug. Defer.

  6. Sample data N-stage seed task vẫn pending (Session 14). Block trên DesignTime vs Runtime DB gotcha + DbInitializer seed flow.

  7. Budget N-stage vẫn defer (cần versioned WF migration trước).

  8. schema-diagram §17-21 update defer cron audit 2026-06-01.

TL;DR Session 15 (07/05 — Tooltip diagnose + drastic refactor DEFER)

User UAT live screenshot báo button "Lưu & Gửi Duyệt" KHÔNG hoạt động + suy đoán "trùng ID" giữa các phiếu.

Diagnose (commit 835cc7f):

  • Root cause: button silent disabled khi evaluation.workflow.nextPhases không có forward phase (chỉ TuChoi/TraLai). Cause khả năng: workflow definition pinned thiếu adjacent step → policy.NextPhasesFrom(DangSoanThao) return empty.
  • Improvement: tooltip + dialog hiển thị reason rõ ràng:
    • submitDisabledReason text: "Phiếu đã ở phase X — chỉ Bản nháp/Trả lại mới sửa+gửi" / "Workflow không có phase tiếp theo từ X. Liên hệ admin kiểm tra cấu hình"
    • Button title attribute → hover show reason hoặc forward phase label
    • Dialog confirm show forward phase explicit ("Sẽ chuyển sang Chờ Purchasing")
  • Mirror fe-admin + fe-user. Build pass cả 2. KHÔNG đụng BE — chỉ FE diagnostic UX.
  • "Trùng ID" KHÔNG phải bug FE — PurchaseEvaluationWorkspacePage URL state đúng, mỗi PE row unique GUID + MaPhieu. Suy đoán user do button silent.

Plan drastic refactor → DEFER:

User confirm "bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking" — refactor workflow từ phase-based + InnerStep nested model sang flat WorkflowStep model (mỗi step = Phòng × Cấp + Approvers users).

Edit working tree 12 files (Domain entities + EF Configurations + DbContext):

  • Phase enum +ChoDuyet=10, legacy values 2-6 deprecated
  • WorkflowStep +DepartmentId, +PositionLevel
  • Drop class WorkflowStepInnerStep + nav (PE + Contract)
  • PE/Contract +CurrentWorkflowStepIndex int?, +RejectedAtStepIndex int?
  • *DepartmentApproval drop InnerStepId column
  • EF Configurations: drop InnerStep config + nav, restore simple unique non-filtered
  • DbContext: drop DbSet × 2

Reality check scope realistic ~8-10h:

  1. Domain + EF + DbContext (~50min) ✓ done in working tree
  2. PolicyRegistry rewrite PE+Contract (~45min)
  3. App CQRS DTOs rewrite (~45min)
  4. Service rewrite PE+Contract (~2-3h)
  5. Tests rewrite — drop 12 N-stage tests + update remaining (~1.5h)
  6. Migration 21 + LocalDB apply + verify (~30min)
  7. FE Designer rewrite (~1.5h)
  8. FE PeWorkflowPanel + workflow timeline (~1h)
  9. Docs/Skill update (~45min)

Vượt session boundary + risk session deep ~30 commits → REVERT working tree về 835cc7f clean state. Test 96 pass intact.

Decision memorized: add memory feedback_drastic_refactor_scope.md — drastic refactor cần dedicated session, scope estimation conservative (2x buffer), tránh mid-session big refactor.

⚠️ CẢNH BÁO Session 16+

  1. Drastic refactor flat workflow chưa làm — DEFER với plan chi tiết. Khi resume:
    • Plan kỹ 6 chunk per-commit
    • Buffer 2x estimate (~16h thực tế)
    • Tests rewrite biggest risk
    • Hoặc fall back Approach Y (FE Designer flat UI giới hạn 5 phòng) ROI 1-2h nếu user OK trade-off
  2. Task 2 sample data seed N-stage vẫn pending (block trên DesignTime vs Runtime DB gotcha + DbInitializer seed flow)
  3. schema-diagram §17-19 Mig 18-20 vẫn defer cron audit 2026-06-01
  4. Hard blockers Ops giữ nguyên 6 task

TL;DR Session 14 (07/05 — PE 3-button approval workflow)

User chỉ thị thay 2-button approval (Duyệt + Reject mơ hồ) bằng 3 hành động rõ ràng cho approver:

  • Duyệt = forward phase tiếp theo (decision=Approve)
  • Trả lại = về DangSoanThao + Drafter sửa (decision=Reject + target=DangSoanThao). Smart reject pattern Mig 16 + clear N-stage rows + Drafter resume jump-back tới phase đã reject.
  • Từ chối = phase=TuChoi (decision=Reject + target=TuChoi). Phiếu khoá vĩnh viễn (17 handler Mig 16 lock edit). Drafter phải tạo phiếu mới.

1 commit (0d77698):

  • Domain PurchaseEvaluationPolicy.cs: NccOnly + NccWithPlan thêm (X → TuChoi) transition cho mọi phase trung gian (ChoPurchasing/ChoDuAn/ChoCCM/ChoCEODuyetPA/ChoCEODuyetNCC) với roles của phase đó. FromDefinition expand: mỗi step (trừ DangSoanThao) thêm (step.Phase → TuChoi) với roles step.
  • Service PurchaseEvaluationWorkflowService.TransitionAsync — Reject branch tách 2 case:
    if (decision == Reject) {
      if (target != TuChoi) {  // Trả lại
        RejectedFromPhase = fromPhase
        target = DangSoanThao  // force
        clear N-stage rows tại fromPhase
      }
      // else target=TuChoi: giữ nguyên, KHÔNG set RejectedFromPhase, KHÔNG clear
    }
    
  • FE PeWorkflowPanel (admin + user mirror): render 3 button rõ:
    • "✓ Duyệt → " brand
    • "← Trả lại (về Drafter sửa)" red khi target=DangSoanThao && fromPhase != DangSoanThao
    • "✗ Hủy / Từ chối" red khi target=TuChoi
    • Decision logic FE: isReject = target=TuChoi || isSendBack
  • Dialog confirm: title rõ theo loại, Cancel case warning đỏ "Phiếu sẽ bị khoá hoàn toàn", SendBack case hint amber "Phiếu về DangSoanThao, Drafter sửa rồi trình lại — workflow tự jump tới phase này".
  • Tests update + add 1:
    • rename Reject_Sets_RejectedFromPhase_And_Forces_DangSoanThaoReject_To_DangSoanThao_Sets_RejectedFromPhase_TraLai (change target TuChoi→DangSoanThao)
    • NEW Reject_To_TuChoi_Locks_Permanently_No_RejectedFromPhase (Phase=TuChoi + RejectedFromPhase null)
    • update NStage_Reject_Clears_InnerStep_Rows_At_Phase target → DangSoanThao
  • 95 → 96 test pass (+1 Từ chối).

Task 2 sample seed in-progress (defer Session 15+):

  • Phát hiện gotcha: DesignTimeDbContextFactory hardcoded SolutionErp_Design connection — dotnet ef database update từ session 12-13 thực ra apply lên _Design DB, KHÔNG phải _Dev runtime DB. Khi user run API → DbInitializer auto-MigrateAsync apply lên _Dev.
  • Đã apply Mig 9-20 lên SolutionErp_Dev qua dotnet ef database update --connection.
  • Start API để DbInitializer auto-seed 30 demo user. API exit 255 sớm khi log buffer full (~100 lines) — seeding dở dang (chỉ seed Roles, chưa Users).
  • Defer Task 2 cho session sau. Cần: user manual dotnet run API 1 lần hoàn thành seed, hoặc Claude resume với output redirect to file.

⚠️ CẢNH BÁO Session 15+

  1. Phase TraLai = 98 (orphan) — Domain enum đã có từ S11+++++++ nhưng KHÔNG dùng (user chốt Session 14 không cần phase trung gian, chỉ DangSoanThao + TuChoi). Để giữ enum không phá nhưng ko wire vào policy → orphan. Có thể remove migration sau (không gây hại).

  2. Task 2 sample seed pending: 2 việc:

    • Update Users.PositionLevel (1=NV, 2=PP, 3=TP) cho 30 demo user đã seed (mapping email pattern: tpb./ccm./pro./fin./act./equ./hra. → 3 (TP), pp.* → 2 (PP), nv./qs./ else → 1 (NV), bod./pm./admin./huy.duong/chau.le → null)
    • INSERT N-stage WorkflowDefinition v2 cho DuyetNcc với InnerSteps (PRO + CCM × 3 cấp NV/PP/TP)
  3. DesignTime vs Runtime DB distinction: dev có 2 DB:

    • SolutionErp_Designdotnet ef migrations add/update (DesignTimeDbContextFactory hardcode)
    • SolutionErp_Dev — runtime API (appsettings.Development.json) Khi muốn apply migrations lên Dev: dotnet ef database update --connection "...SolutionErp_Dev..." hoặc dotnet run API (DbInitializer auto-MigrateAsync).
  4. Budget N-stage defer — chưa wire vì Budget chưa có versioned WorkflowDefinition. Cần migration AddBudgetVersionedWorkflow + AddBudgetWorkflowInnerSteps. User quyết riêng (feature mở rộng module lớn).

  5. schema-diagram §17-19 Mig 18-20 chưa update — defer cron audit 2026-06-01.

TL;DR Session 13 (07/05 — Mirror N-stage workflow sang Contract)

User chỉ thị mirror N-stage từ PE (Mig 18+19) sang Contract. Budget defer (cần migration AddBudgetVersionedWorkflow trước — hardcoded BudgetPolicy.Default chưa có WorkflowDefinition entity).

5 chunk per-commit (Chunk E skipped — WorkflowsController auto-bind record qua [FromBody]):

  • Chunk A (951ffa3) Domain WorkflowStepInnerStep + nav WorkflowStep.InnerSteps + ALTER ContractDeptApproval.InnerStepId + EF config + Migration 20 GỘP 1 (CREATE TABLE + ALTER + DropIndex old + Recreate filtered legacy/N-stage)
  • Chunk B (04cf2a0) Application CQRS DTO/Input/Validator/Handler mirror PE Chunk B (default null backward compat)
  • Chunk C (e247b67) ContractWorkflowService refactor — load InnerSteps eager + reject clear N-stage rows + dept block split hasInnerSteps→N-stage / else→legacy 2-stage
  • Chunk D (7c0772a) ContractNStageApprovalTests 6 test mirror PE + helper SeedWorkflowDefinitionAsync 2 step adjacent + FakeChangelogService + FakeContractCodeGenerator stubs. 89→95 test pass
  • Chunk E SKIP — auto-bind no code change
  • Chunk F (current) FE WorkflowsPage Designer extend InnerSteps sub-section mirror PeWorkflowsPage + Docs

Verify: dotnet build pass + dotnet ef database update Mig 20 LocalDB applied + dotnet test 95 pass + npm build fe-admin pass.

Cumulative sau Session 13:

Trước S13 Sau S13
BE LOC ~15300 ~15700 (+400)
Migrations 19 20 (+Mig 20)
DB tables 56 57 (+1 WorkflowStepInnerSteps)
DB columns mới +1 (ContractDeptApproval.InnerStepId)
API endpoints ~134 ~134 (no change — auto-bind)
FE pages 32 32 (extend existing WorkflowsPage)
Tests 89 95 (+6 Contract N-stage)
Commits (after S12) +5 (A→F per-chunk, E skipped)

⚠️ CẢNH BÁO Session 14+

  1. Budget N-stage defer — Budget chưa có versioned WorkflowDefinition entity (hardcoded BudgetPolicy.Default). Để mirror N-stage Budget cần migration AddBudgetVersionedWorkflow trước (4 bảng BudgetWorkflowDefinitions/Steps/StepApprovers/InnerSteps + Budget.WorkflowDefinitionId?). Defer cho user quyết riêng — feature mở rộng module Budget lớn.

  2. PE-only deploy first — Contract N-stage giờ đã có nhưng PE đã được test trước. UAT cả 2 module song song để verify pattern reusable.

  3. Sample data N-stage — pending (Task 2). Khi user yêu cầu sẽ seed PositionLevel cho 30 demo user + 1 N-stage workflow definition active cho DuyetNcc + 1 cho ContractType (ví dụ HopDongThauPhu).

  4. TraLai BE wire — pending (Task 4). Phase TraLai = 98 enum đã có ở Session S11+++++++ FE side, nhưng BE workflow service chưa wire button "Trả lại" cho approver. Khi user yêu cầu sẽ thêm decision SendBack (or reuse Reject với target=TraLai) + Service logic + FE button trong Workflow Panel.

  5. schema-diagram §17 Mig 20 chưa update — defer cron audit 2026-06-01.

  6. Skill ef-core-migration Mig 20 row chưa add — defer cron audit.

  7. Skill contract-workflow N-stage cross-ref chưa add — defer cron audit.

TL;DR Session 12 (07/05 — N-stage workflow approval per phase × dept × cấp)

User yêu cầu mở rộng từ 2-stage Mig 16 sang N-stage cấu hình động: 1 phase (WorkflowStep cha) có thể cấu hình chuỗi InnerSteps con theo Department × PositionLevel sequential. Mỗi phòng có NV → PP → TP duyệt thứ tự, có thể bypass cùng dept khi role cao + CanBypassReview.

6 câu spec defaults chốt:

  • Q1 enum PositionLevel { NhanVien=1, PhoPhong=2, TruongPhong=3 } + User.PositionLevel int?
  • Q2 Sequential pure (Order asc, mỗi inner step = 1 cấp duyệt)
  • Q3 TP có CanBypassReview → skip NV+PP cùng dept (audit IsBypassed=true cho cấp dưới)
  • Q4 Smart reject về DangSoanThao + RejectedFromPhase + clear N-stage rows tại phase reject
  • Q5 PE first (Contract+Budget mirror sau khi PE stable UAT)
  • Q6(a) Designer UI 1 sub-section "Cấp duyệt nhỏ trong phòng" drag-list per step

6 chunk per-commit:

  • Chunk A (13ab533) Domain + Migration 18 — enum PositionLevel + entity InnerStep + ALTER User.PositionLevel + ALTER PEDeptApproval.InnerStepId + EF config Cascade/Restrict + 3-file rule
  • Chunk B (0e56bd0) Application CQRS DTO — extend PeWorkflowStepDto + InnerStepDto + Validator + Handler atomic batch + UserDto +PositionLevel + SetUserPositionLevelCommand (default null backward compat existing PeWorkflowAdminTests)
  • Chunk C (0c62e24) Service N-stage logic + Migration 19 filtered unique (drop UNIQUE Mig 16 → recreate WHERE InnerStepId IS NULL legacy + new WHERE InnerStepId IS NOT NULL N-stage). Refactor TransitionAsync: load InnerSteps eager + reject clear rows + hasInnerSteps→N-stage / else→legacy 2-stage. Match firstPending Order asc, exact match upsert 1 row, bypass batch upsert NV+PP+TP audit IsBypassed
  • Chunk D (3d76c6b) 6 test PE N-stage (FirstInner blocks / 3-level sequential pass / TP bypass skips / wrong dept 403 / reject clears rows / legacy fallback no inner). IdentityFixture extend +positionLevel arg. Helper SeedWorkflowDefinitionAsync 2 step adjacent. 83→89 test pass
  • Chunk E (83ffabd) API PATCH /users/{id}/position-level mirror SetBypassReview
  • Chunk F (current) FE-Admin types/users.ts +positionLevel + PositionLevel const + Label/Short. PeWorkflowsPage Designer + sub-section InnerSteps drag-list { Phòng × Cấp + required } + button "+ Thêm cấp duyệt" emerald + departmentsList query + payload include. UsersPage column "Cấp" badge + cycle button. KHÔNG đụng fe-user (admin-only). Docs/Skill update.

Verify: dotnet build pass + dotnet ef database update Mig 18+19 LocalDB applied + dotnet test 89 pass + npm build fe-admin pass.

Cumulative sau Session 12:

Trước S12 Sau S12
BE LOC ~14850 ~15300 (+450 — Domain enum + entity + EF config + Service N-stage + App CQRS)
Migrations 17 19 (+Mig 18 + Mig 19)
DB tables 55 56 (+1 PEWorkflowStepInnerSteps)
DB columns mới +2 (User.PositionLevel + PEDeptApproval.InnerStepId)
API endpoints ~133 ~134 (+1 PATCH /users/{id}/position-level)
FE pages 32 32 (no change — extend existing 2 page)
Tests 83 89 (+6 N-stage PE)
Commits (after S11+++) +6 (A→F per-chunk)

⚠️ CẢNH BÁO Session 13+

  1. N-stage chỉ áp PE first — Contract + Budget vẫn 2-stage Mig 16 cũ. Mirror sau khi UAT PE 2-3 tuần ổn (giữ pattern, lặp lại Domain entity + Service logic + Tests).

  2. User.PositionLevel chưa seed cho 30 demo user hiện có — admin phải set tay qua UsersPage cycle button. Hoặc seed migration sau (mapping Position text → PositionLevel int).

  3. Designer InnerSteps optional — workflow definition KHÔNG có InnerSteps fallback logic 2-stage Mig 16 cũ. Backward compat 100%. Khi muốn enable N-stage cho 1 phase: clone version + add inner steps + Save.

  4. Bypass kế thừa cùng dept only — TP với CanBypassReview chỉ skip NV+PP CÙNG dept của TP. KHÔNG cross-dept (NV.A bypass không skip NV.B).

  5. Reset N-stage rows khi reject — clear chỉ rows tại phase reject. Phase trung gian khác vẫn giữ approvals nếu có. Resume sẽ jump tới RejectedFromPhase + cần re-approve N-stage tại phase đó.

  6. Wrong-level error message — đã localize "phòng X cấp Y không khớp". UAT user cần feedback nếu confused.

  7. schema-diagram §15 Mig 18 + §16 Mig 19 chưa update — defer cho audit cron 2026-06-01 (small drift, không major).

  8. Skill ef-core-migration Mig 18+19 row chưa add — defer audit 2026-06-01.

TL;DR Session phase 2 (08/05 — B12-B14 polish iterate sau wrap-up 6e7a6db)

User UAT live tiếp tục, áp rule strict verify khi rename/remove (lesson hotfix CI 0ae3fe2).

  • B12 (378c993) PE detail polish 5 changes:
    • "Lưu" no-close (chỉ toast + invalidate sync, KHÔNG đóng workspace)
    • "Xóa phiếu" red bottom button CHỈ Bản nháp (soft-delete IsDeleted=true)
    • Bỏ header bar workspace mode "Sửa header"/"Xóa"/"Đóng" (chuyển hết xuống bottom action bar)
    • Section 4 column header: s.supplierName (master NCC name) thay displayName
    • Section 3 row chặn xóa NCC khi đã có quotes + tooltip "xóa báo giá trước"
  • B13 (e320027) InfoTab auto re-edit + pencil active visual:
    • useEffect watch [autoEdit, canEdit, ev.id, ...] → re-trigger edit khi pencil click phiếu khác
    • Sync values từ ev mới (tránh stale state)
    • Pencil "sáng lên" active state (bg-brand-100 + ring) khi editingRowId === p.id
  • B14 (d2306b8) QuoteDialog + winner column highlight + loading feedback:
    • Bỏ checkbox "Chọn NCC này cho hạng mục" (consolidate winner ở Section 2.a)
    • Winner column Section 4 matrix LUÔN xanh emerald (header prefix + cells full column)
    • QuoteDialog full overlay loading + spinner khi save có delay
    • NccSelectorRow inline spinner "Đang chọn NCC + sync cột giá Section 4…"

Verify: npm run build × 2 app pass · dotnet test 83 pass (KHÔNG regression). Push gitea OK.

⚠️ CẢNH BÁO Session 12+ (cập nhật)

Bổ sung từ wrap-up trước (xem TL;DR S10-11+++++++ phía dưới):

  1. isSelected per-quote BE field — vẫn còn trong API payload nhưng UI ẩn. Nếu sau này muốn cho user chọn winner per-item (khác với winner per-phiếu) → re-enable checkbox QuoteDialog. Hiện tại chỉ gửi existing?.isSelected ?? false để giữ legacy data nếu có.

  2. Section 4 cell winner highlight dùng ev.selectedSupplierId === s.supplierId (per-supplier check). Nếu BE workflow có multi-winner per-item (ít khả năng), cần điều chỉnh logic.

  3. Loading overlay QuoteDialog chỉ áp QuoteDialog. Các dialog khác (AddSupplierDialog/EditSupplierDialog/DetailDialog) chỉ có button text feedback. Nếu UAT thấy delay → mở rộng pattern overlay sang các dialog khác.

  4. InfoTab re-edit useEffect deps include 4 ev fields (tenGoiThau/diaDiem/moTa/paymentTerms). Có thể trigger re-set values nếu BE cập nhật ev (vd auto-save từ chỗ khác). Trade-off: chấp nhận để sync state. Nếu UAT thấy "values mất" → debug deps.

TL;DR Session S10-11+++++++ (07/05 — PE Workspace UX overhaul đầy đủ)

User UAT live mode iterate liên tục. Áp rule feedback_uat_skip_verify skip dotnet test sau mỗi chunk, push ngay. Lesson hotfix CI 0ae3fe2: rename/remove → BẮT BUỘC npm run build trước commit.

11 batch deliverable (23 commit total — xem STATUS Recently Done row đầu tiên cho narrative đầy đủ):

  • B1 PE Thao tác 2-panel workspace (S10)
  • B2 Migration 17 manual budget fields PE + HĐ (S11)
  • B3 BudgetFieldRow inline editor Section 2.b
  • B4 InfoTab inline edit Section 1 + PeListPanel pencil hover
  • B5 Workspace "new" sectioned create view 5 sections
  • B6 Danh sách view disable toàn bộ tương tác
  • B7 Workspace "new" lock Loại quy trình + payment preset Select
  • B8 PE display status meta (Bản nháp / Đã gửi duyệt / Trả lại / Đã duyệt / Từ chối)
  • B9 Phase TraLai = 98 + pencil always visible + edit gating
  • B10 Hotfix CI TS errors (forcedPhase rename + unused import)
  • B11 PE detail polish — NCC selector dropdown + Section 3 winner protect + Bottom action bar Lưu/Gửi Duyệt

Stats sau wrap-up

Trước S10 Sau S11+++++++
BE LOC ~14400 ~14850 (+450 — Mig 17 + Domain TraLai + App CQRS)
Migrations 16 17 (+1 Mig 17)
DB columns mới +4 (PE+HĐ × Name+Amount manual budget)
FE pages 31 32 (+1 Workspace 2-panel)
FE components mới +5 (PeListPanel, PeHeaderForm, PeWorkspaceCreateView, BudgetFieldRow, NccSelectorRow inline)
PE phase enum 8 9 (+TraLai = 98)
PE display status 5 (gom phase chi tiết)
Tests 83 83 (no test added — UAT iteration, áp rule §7 test-after defer)
Commits S10-S11+++++++ +23

⚠️ CẢNH BÁO Session 12+

  1. Workflow transition vào TraLai — Domain enum đã có (98), FE label/color/badge sẵn. NHƯNG chưa wire button "← Trả lại" trong PeWorkflowPanel approver view + workflow service BE chưa transition logic vào TraLai. Khi UAT cần → thêm: button "Trả lại" trong Chuyển tiếp (hoặc Reject Dialog), workflow rule Decision=3 (TraLai) set phase=TraLai + ghi RejectedFromPhase. FE đã ready accept hiển thị TraLai phase.

  2. "Đã gửi duyệt" multi-phase filter — display status "Đã gửi duyệt" gom 5 phase trung gian (ChoPurchasing/ChoDuAn/ChoCCM/ChoCEODuyetPA/ChoCEODuyetNCC). BE chưa hỗ trợ multi-phase param trong list endpoint → FE filter dropdown ẩn option này (chỉ exact-match Bản nháp/Đã duyệt/Từ chối). TODO: BE thêm ?phases=2,3,4,5,6 query param hoặc derive displayStatus field.

  3. feedback_uat_skip_verify exception — khi commit có rename/remove, BẮT BUỘC npm run build 1 lần trước push (lesson hotfix 0ae3fe2). Chỉ skip verify khi pure ADD (props, JSX, new file).

  4. Workspace + Danh sách + Duyệt matrix:

    • Workspace (Pe_*_Create): 2-panel, Section 5 disabled, edit only Bản nháp+Trả lại, pencil bright/disabled, bottom bar 2 nút Lưu/Gửi Duyệt
    • Danh sách (Pe_*_List): 3-panel, readOnly=true toàn bộ (no edit/transition)
    • Duyệt (Pe_*_Pending): 3-panel, PeDetailTabs readOnly + PeWorkflowPanel có Chuyển tiếp + Section 5 nhập opinion (chỗ này hiện vẫn disabled do code path cũ — verify nếu UAT user cần sign opinions ở đây)
  5. Mig 17 columns nullable — backward compat OK. Cả 2 (BudgetId + manual fields) cùng null acceptable. Validation BE chưa XOR.

  6. CreateContractFromEvaluation carry-forward manual fields PE→HĐ — đã wire. Verify UAT.

  7. CI deploy status — sau hotfix 0ae3fe2 các run từ commit 4c0625c trở đi sẽ xanh + deploy. User UAT live trên prod ngay sau push.

TL;DR Session 11++ housekeeping (07/05 — InfoTab inline edit + pencil hover)

User feedback after BudgetFieldRow: muốn thêm nút edit kế bên row trong Panel 1 list, click sáng nội dung Section 1 lên cho sửa header inline.

  • InfoTab inline edit — display mode + button "✎ Sửa" góc phải Section 1. Editing mode: card border brand-200 + 4 input (Tên / Dự án locked / Địa điểm / Mô tả / Payment) + Save (PUT /pe/:id) / Hủy.
  • PeListPanel pencil hover — icon absolute right-2 top-2 mỗi row, group-hover opacity-100. Click → callback onEditClick(id).
  • URL flag ?editHeader=1 — set bởi pencil click → autoEditHeader prop chain xuống PeDetailTabs → InfoTab tự open editing mode mount-time.
  • fe-admin Chunk 1 (5a89dd2) · fe-user mirror Chunk 2 (this).

Defer (chưa làm): Refactor workspace "new" mode wrap PeHeaderForm trong sectioned layout giống detail view (5 sections visible). PeHeaderForm hiện tại single-card đủ dùng — chỉ làm thêm khi user feedback rõ.

TL;DR Session 11+ housekeeping (07/05 — inline budget editor)

User feedback after Session 11: muốn toggle + 2 fields hiển thị trực tiếp trong Section 2 "b. Ngân sách" của PeDetailTabs, không chỉ ở "Sửa header" page. Empty values cứ empty.

  • BudgetFieldRow component (~125 LOC) thay FormRow tĩnh cũ ở b. Ngân sách. canEdit=!readOnly && isDraft. Save dùng existing PUT endpoint (KHÔNG cần BE thay đổi).
  • fe-admin Chunk 1 (commit 19712d8) · fe-user mirror Chunk 2 (commit d5c6f12).
  • Verify: 2 build pass + 0 TS error.

UX:

  • Workspace + Danh sách + isDraft → editable inline: toggle + Select OR 2 input + nút Lưu khi dirty
  • Duyệt + !isDraft → display only (link card / manual values / "—" empty)
  • Empty state: hiển thị "—" thay vì "(chưa link)" verbose (per user)

TL;DR Session 11 (07/05 — Migration 17 manual budget fields)

Output session 11 — UX improvement cho user nhập ngân sách không cần Budget entity approved:

  • Migration 17 AddManualBudgetFieldsToPeAndContract — 4 ALTER (PE + HĐ × BudgetManualName nvarchar(200) + BudgetManualAmount decimal(18,2)). Applied LocalDB OK. Mirror logic PE ↔ HĐ (Q3 user chốt).
  • BE Domain 2 entity (PE + Contract) +2 property + EF config HasMaxLength/HasPrecision. App CQRS Create/Update commands + DTO + Validator + diff log + carry-forward CreateContractFromEvaluation pe→contract.
  • FE Toggle checkbox "Nhập tay (không link)" cạnh Label Ngân sách trong 4 chỗ: PeHeaderForm (workspace + /new page wrap), ContractCreatePage NewForm + EditForm. Khi ON: hide Select, show 2 input field grid 2-col (Tên + Số tiền formatted VND). Khi OFF (default): Select Budget approved cũ. Auto-detect manual mode khi load existing có manual data + !budgetId. Display fallback ở PeDetailTabs Section 1 "b. Ngân sách" + Contract EditForm read-only branch.
  • 5 chunk per-commit (build + 83 test pass mỗi chunk): C1 Domain+Infra Migration 17 (ecd5f7e) · C2 App CQRS (0f7901c) · C3 FE-Admin (bab5031) · C4 FE-User mirror (14f8d9d) · C5 Docs (current).

Validation Q2 chốt: cả BudgetId + manual fields có thể cùng null (PE/HĐ chưa có ngân sách gì). KHÔNG XOR enforce — BE prefer link nếu có (Phase=DaDuyet guarantee), manual fallback only.

KHÔNG đụng: Budget entity / Phase=DaDuyet validation (giữ invariant "PE/HĐ link Budget approved only"). Manual fields chỉ là note/display, KHÔNG join với Budget.Details cho per-row comparison ở PE matrix Section 4 (cột "So với ngân sách" vẫn require ev.budgetId — không có detail rows để compare).

⚠️ CẢNH BÁO session tiếp (Session 12+)

  1. UAT manual budget flow — toggle ON/OFF + save + reload + verify auto-detect mode. Đặc biệt edit existing PE/HĐ có manual data → toggle phải auto-checked. Edit existing có Budget link → toggle auto-unchecked.
  2. Section 4 PE matrix "So với ngân sách" vẫn require Budget link — manual amount KHÔNG render comparison column. Document giới hạn này nếu UAT thắc mắc.
  3. docs/database/schema-diagram.md chưa cập nhật count 16→17 migration + 4 column mới. Defer cho audit định kỳ 2026-06-01 (per §6.4 cron) — nhỏ enough không phải selective rewrite.
  4. CreateContractFromEvaluation đã carry forward manual fields PE→Contract. Verify khi UAT: PE có manual budget → tạo HĐ từ phiếu → HĐ inherit luôn.
  5. No new test added (rule §7 — feature mới = test-after, soak UAT 2-3 tuần ổn → viết happy path).
  6. Schema fields nullable — không phá HĐ/PE cũ (legacy null OK). Backward compatible.

TL;DR Session 10 (07/05 — PE workspace 2-panel)

Output session 10 — restructure leaf "Thao tác" PE từ page Create header riêng sang workspace 2-panel mirror HĐ Thầu phụ pattern:

  • Spec chốt 5 câu trước code — Q1 Panel 2 chỉ data entry (KHÔNG Workflow/Approvals/History); Panel 1 pure picker (no inline edit/delete); Q2 mirror HĐ Thầu phụ (sticky "+ Thêm mới" + new→edit Panel 2); Q3 leaf "Danh sách" + "Duyệt" giữ 3-panel hiện tại; Q4 route mới /workspace; Q5 Section 5 Ý kiến 4PB disable trong workspace (nhập khi duyệt).
  • Chunk 1 fe-admin (commit ee0d360) — 3 file mới: PeListPanel.tsx (~180 LOC pure picker reuse-able + sticky "+ Thêm mới"), PeHeaderForm.tsx (~210 LOC extract header form từ CreatePage), PurchaseEvaluationWorkspacePage.tsx (~120 LOC 2-panel [320px_1fr]). 3 file sửa: PeDetailTabs.tsx thêm prop mode?: 'detail' \| 'workspace' + Section 5 hint amber khi workspace + force opinionsReadOnly. Layout.tsx resolver Pe_*_Create/workspace?type=N. App.tsx route mới.
  • Chunk 2 fe-user mirror (commit ecf3c59) — 6 file y hệt content (rule §3.9 duplicate có chủ đích).
  • Verify: 2 build pass + dotnet test 83 vẫn pass mỗi chunk.

KHÔNG đụng BE / migration / schema / endpoint. Route /new cũ giữ tồn tại cho deep-link "Sửa header" button.

⚠️ CẢNH BÁO session tiếp (Session 11+)

  1. UAT live workspace với anh Kiệt + 2-3 user — feature mới UX khác (page Create cũ thành Dialog implicit qua sticky button + transition new→edit auto).
  2. Section 5 Ý kiến 4PB hint banner amber ở workspace mode chỉ là text gợi ý — KHÔNG block. User vẫn thấy existing opinions read-only. Nếu UAT thấy confused → có thể đổi thành collapsible section.
  3. Mobile: workspace KHÔNG có fallback — màn hình <lg (< 1024px) Panel 2 ẩn (lg:block). Cần test mobile redirect → /purchase-evaluations/:id nếu UAT phát hiện. Hiện chỉ admin desktop dùng workspace.
  4. Pe_*_Pending vẫn /purchase-evaluations?pendingMe=1 — leaf "Duyệt" 3-panel (giữ Panel 3 Workflow + readOnly Section 5 cho phép sign). UAT verify Drafter trình → TPB duyệt thấy Section 5 enable đúng.
  5. PE WorkflowPanel duplicate ở 2 chỗ — leaf "Danh sách" + "Duyệt" Panel 3 (3-panel). KHÔNG có ở "Thao tác" workspace. Verify route resolver active state highlight đúng (queryMatches helper từ session 3).
  6. Sửa header trong workspace — vẫn navigate sang /new?id= (button "Sửa header" trong PeDetailTabs). Chưa wire inline edit qua Dialog. Nếu UAT yêu cầu → thêm prop onEditHeader cho PeDetailTabs trigger Dialog reuse PeHeaderForm.

Housekeeping today (sau Session 9)

  • Audit định kỳ 2026-05 — combined skill + doc drift (commit 7dc0233). 5 drift patch (count 77→83 + 52→55 bảng), 1 skill content patch (contract-workflow Phase 9 cross-ref). KHÔNG tạo skill mới. Log docs/changelog/skill-audit-2026-05.md.
  • Optional polish — fe-user Inbox PE section (commit 332a90f). useQuery /purchase-evaluations/inbox + Panel 1 chia 2 section sticky header (HĐ + PE). PE click → navigate page riêng.
  • User Manual 7 file rewrite compact (commit 16c2c9c). User feedback "ko cần quá đầy đủ chi tiết, cho end-user họ làm". Bỏ field validation table + error troubleshoot table + FAQ chi tiết. Giữ tổng quan ngắn + numbered steps đơn giản. ~86 KB / 7 file (cũ ~123 KB ↓30%). Setup package.json + npm install docx@9.5.0 + npm run gen:all. Lần sau sửa: edit _gen-*.js → run gen:all.

Cron audit định kỳ: Claude SDK CronCreate auto-expire 7 days → KHÔNG fit monthly cadence. User cần setup OS Task Scheduler .bat script ping API hoặc manual trigger mỗi đầu tháng. Lần kế: 2026-06-01.

User Manual style rule (mới chốt session này): end-user docs BỎ field/error tables, FAQ chi tiết, phím tắt — GIỮ tổng quan ngắn + numbered steps + note/warn/tip critical. Phân biệt với rule §6.5 (KHÔNG cắt narrative) — đây là rule cho audience end-user, không phải agent dev đọc rationale.

TL;DR Session 9 (04/05 — Chunk E-bis sau Session 8)

Output session 9 — đóng tất cả Chunk E-bis defer từ session 8:

  • FE PE WorkflowPanel — Section "Tiến trình duyệt 2-cấp phòng ban" group by phase × dept, highlight amber chờ TPB confirm, badge fuchsia bypass (cả fe-admin + fe-user).
  • FE UsersPage UserManager — Column "Bypass" + button ShieldCheck toggle CanBypassReview, badge fuchsia khi enabled. UserDto thêm field.
  • HĐ 2-stage logicContractWorkflowService thêm UserManager DI + mirror logic từ PE service. ContractDepartmentApprovalFeatures.cs List query. Endpoint GET /contracts/{id}/department-approvals. FE WorkflowHistoryPanel section mới.
  • Budget 2-stage logicTransitionBudgetCommandHandler thêm INotificationService + IDateTime DI + 2-stage. BudgetDepartmentApprovalFeatures.cs + endpoint + FE BudgetWorkflowPanel section.
  • 6 test PE 2-stageIdentityFixture setup full Identity stack (DbContext SQLite + AddIdentityCore + AddRoles) reusable. 6 scenario: NV_Review_Blocks / TPB_Confirm_Allows / NV_Bypass / Admin_Skip / Reject_Sets / Resume_Jumps_Back.
  • Verify: Build pass + 83 test pass mỗi commit (54 Domain + 29 Infra: 17 codegen + 6 PE WF Application + 6 PE 2-stage).
  • 5 commit pushed Gitea (E2 → E6).

⚠️ CẢNH BÁO session tiếp (Session 10+)

  1. UAT live ngay với anh Kiệt + 2-3 user — feature 2-stage đầy đủ cả 3 module + UX.
  2. Tests Contract + Budget 2-stage skipped — logic identical PE (cùng pattern, cùng entity shape). Pattern PeTwoStageApprovalTests reusable nếu UAT phát hiện regression riêng.
  3. Bypass toggle audit — chưa log Changelog khi admin toggle CanBypassReview. Audit qua Identity standard column UpdatedAt only. Có thể cần thêm audit row riêng nếu UAT yêu cầu.
  4. Notify TPB cùng dept dùng UserManager.GetRolesAsync filter DeptManager — verify production có user role DeptManager đúng (data already seeded).
  5. fe-user KHÔNG có UsersPage — admin-only function. Bypass toggle chỉ ở fe-admin.
  6. 3 endpoint mới List dept-approvals PE/HĐ/Budget cùng pattern, reuse policy authz *Read.
  7. Cron audit định kỳ 2026-05-01 vẫn EMPTY (No scheduled jobs). Có thể recreate khi user yêu cầu.

TL;DR Session 8 (04/05 — code lớn, 5 commit per-chunk)

TL;DR Session 8 (04/05 — code lớn, 5 commit per-chunk)

Output session 8 — đóng bug anh Kiệt + thêm 3 ràng buộc workflow:

  • Migration 16 AddTwoStageDeptApprovalAndSmartReject — 4 ALTER (3 RejectedFromPhase int + Users.CanBypassReview bit) + 3 CREATE TABLE (Contract/PE/Budget DepartmentApprovals UNIQUE (TargetId, Phase, Dept, Stage)) + 12 indexes.
  • Lock edit 17 handler thêm guard Phase != DangSoanThao (Contract Detail × 15 qua helper, PE Detail × 5 qua helper mới, Budget Detail × 3 inline).
  • Smart reject + Resume 3 module — Reject = lưu phase nguồn + force về DangSoanThao. Resume = jump straight tới phase đã reject (skip phase trung gian, bypass policy guard).
  • PE 2-stage logic trong PurchaseEvaluationWorkflowService — TPB/CanBypass → Confirm; NV → Review only, BLOCK transition cho đến khi TPB confirm.
  • 3 endpoint mới: GET /pe/{id}/department-approvals (List), PATCH /users/{id}/bypass-review (toggle), Notify TPB cùng dept khi NV review.
  • Verify: Build + 77 test pass mỗi commit. Migration applied LocalDB OK. Schema verified.
  • 6 commit pushed (2 docs S7 + 5 code S8).

⚠️ CẢNH BÁO session tiếp (Session 9+)

  1. Bug fix anh Kiệt chỉ áp PE workflow. HĐ + Budget 2-stage scope DEFER cho khi UAT PE OK.
  2. FE Workflow Panel chưa update — workflow vẫn block đúng (BE), nhưng UX chưa hiển thị 2-stage progress. User test sẽ thấy phase không đổi mà không hiểu tại sao "stuck". Phải UAT với hint cho user trước khi code FE.
  3. FE UserManager toggle CanBypassReview chưa làm — tạm thời SET qua HTTP PATCH:
    PATCH /api/users/{userId}/bypass-review
    Authorization: Bearer <admin>
    { "canBypassReview": true }
    
  4. Test thực tế bug fix flow:
    • Login phuong.nguyen (NV.PRO, role=Procurement, DeptId=PRO) tạo phiếu PE type A
    • Trình DangSoanThao → ChoPurchasing
    • phuong.nguyen click Duyệt phase ChoPurchasing → expect: phase KHÔNG đổi, có row Stage=Review
    • tra.bui (TPB.PRO, role=DeptManager) click Duyệt → expect: phase chuyển ChoCCM
  5. Notify TPB cùng dept dùng UserManager.GetRolesAsync filter DeptManager. Best effort, fail OK.
  6. Cron audit định kỳ 2026-05-01 đã quá hạn 3 ngày, vẫn EMPTY runtime. Cần manual trigger.
  7. Smart reject test: Reject phase ChoCCM → DangSoanThao + RejectedFromPhase=ChoCCM. Drafter sửa Detail + trình lại → jump straight tới ChoCCM (skip ChoPurchasing).
  8. Lock edit test: HĐ ở Phase=DangGopY → cố sửa Detail → expect 409 Conflict "đã trình duyệt, không thể chỉnh sửa".

TL;DR Session 6 (30/04 — không code, chỉ docs)

Output session 6 — pure docs work, không thay đổi code/test:

  • Compact 3 file core: STATUS -27%, HANDOFF -32%, migration-todos -35% (-288 dòng tổng). Archive 51 row Recently Done Phase 0-7 → changelog/recently-done-archive-2026-04.md.
  • Refresh 3 skill stale: form-engine (Phase 2 MVP → Tier 3 feature-complete), permission-matrix (12 menu → ~60 + inheritance roots), ef-core-migration (24 DbSet → 52 bảng).
  • Rule mới §7 Timing test: 1 bảng 5-row compact — feature mới = test-after, bug = test-before, critical algorithm = test-before, spec change = update test cũ, skip 5 loại.
  • Rule mới §6.4 Audit + compact MD định kỳ: cadence + checklist + anti-pattern. Cross-ref §9.4 skill audit. KHÔNG rewrite toàn bộ.
  • 77 test vẫn pass (Domain 54 + Infra 23). 0 thay đổi code.
  • Cron 2026-05-01 fire mai — combined audit (skill + doc drift) theo §6.4 + §9.4.

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

  • 4 entity: Budget (Header) + BudgetDetail (flat row) + BudgetApproval (history) + BudgetChangelog (audit log).
  • Enum BudgetPhase 5 state (DangSoanThao→ChoCCM→ChoCEO→DaDuyet + TuChoi).
  • BudgetPolicy.Default hardcoded simple 3-step (Drafter→CCM→CEO) — chưa versioned (TODO khi user cần admin config UI).
  • NS-YYYYMM-XXXX Random.Shared (chưa atomic — TODO khi format chính thức).
  • Link nullable: Contract.BudgetId? + PE.BudgetId? đã có FK + index, FE chưa wire form.
  • Menu seed Budgets root + 3 leaf (Bg_List/Bg_Create/Bg_Pending) order=27 icon Wallet.
  • Application: 11 CQRS handler ~340 LOC (Create/UpdateDraft/Transition/List/GetDetail/Delete + Detail CRUD auto-recompute TongNganSach + ListChangelogs).
  • Api: BudgetsController 11 endpoint REST.
  • FE chưa làm — Priority 0 session 5.

B. 14 demo user Solutions thật

  • PRO 5 (TPB tra.bui + 4 NV) + CCM 7 (TPB ngocanh.huynh + 6 NV) + ISO 1 (chau.le) + CEO 1 (huy.duong).
  • 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 cumulative: 52 DB tables, ~128 endpoints, 15 migrations, 41 gotchas (+3 CI fixes), 77 unit test (+6 PE WF Application), 11+ commit session 5 push lên Gitea.

🆕 CI/CD optimize (29/04 tối)

  • Manual checkout bypass github.com — fix #108/#109 transient TCP timeout 21s. Run #110 pass.
  • Path filter docs-only skippaths-ignore trong on:push, commit MD-only KHÔNG trigger CI (verify 512880c → no run #113).
  • ⏸️ npm junction cache — thử ở #111 fail tsc not found, rollback. Cần debug session sau (gotcha #40 doc rồi).
  • Tests Phase 3 mini (PE Workflow Designer) — 6 test versioning logic. Total 77.

⚠️ CẢNH BÁO session tiếp (Session 7+)

  1. CI test gate active — code → dotnet test SolutionErp.slnx local → commit → push. Test fail = NO deploy. Workflow user §7 rules.md.
  2. Timing test rule live (§7): feature mới = test-after, bug = test-before BẮT BUỘC, critical algorithm = test-before merge, spec change = update test cũ + code chung commit.
  3. Doc audit cadence live (§6.4): cuối phase compact, đầu tháng cron audit, KHÔNG rewrite toàn bộ. Cron fire mai (2026-05-01) — combined skill + doc drift theo checklist.
  4. Login email admin@solutionerp.local / Admin@123456 — domain default chưa đổi sang @solutions.com.vn (chỉ demo user rebrand).
  5. Chưa xóa binding cũ .huypham.vn — vẫn fallback. Sau verify stable → .\migrate-domains.ps1 -RemoveOld -SkipCert trên VPS.
  6. win-acme scheduled task "unhealthy" — fix trước cert expire 2026-06-18.
  7. Export phiếu PDF/Excel PE — pending vô thời hạn (user nói không quan trọng).
  8. 3 feature mới chưa test (PE Opinion Upsert / Budget validate / Contract BudgetId carry) — đợi UAT phát sinh bug → áp rule §7 (regression test before fix). Hoặc soak 2-3 tuần ổn → viết happy path.
  9. G-084: VPS shared VietReport — mọi reverse proxy mới phải 127.0.0.1 + bind loopback IPv4 explicit.

Skills (.claude/skills/) — PHẢI dùng khi task khớp

Domain (3) Ops (3)
contract-workflow — state machine + versioned WF dependency-audit-erp — npm/dotnet CVE scan
form-engine — render docx/xlsx + PDF ef-core-migration — EF migration + 3-file rule
permission-matrix — role × menu × CRUD iis-deploy-runbook — 3 site IIS + win-acme + runner

Audit cron: 0 9 1 * * (9:00 AM ngày 1 mỗi tháng). Workflow: docs/rules.md §9.4. Lần kế: 2026-05-01.

Quy tắc: KHÔNG bulk-clone repo skill 3rd party. Chỉ skill PROJECT-SPECIFIC. Đầy đủ: docs/rules.md §9.

Ở đâu rồi?

Phase Trạng thái
0 Draft Done
1 Alpha Core (foundation + đợt 2 CRUD + Permission) Done
2 Form Engine MVP + iter 2 (upload UI + .doc auto-convert + PDF export) Done
3 Workflow MVP (9 phase + code gen) + iter 2 (SLA job + attachment + notify) Done
4 Report MVP (Dashboard + Excel) + user-specific dashboard Done
5 Prep + 5.1 Security + Users Mgmt Done
5 Deploy prod (3 domain HTTPS live) Done
Tier 3 (Attach + Realtime + Form builder + PDF + Versioned WF + Nested menu + Permission 3-panel) Done
Skill governance (6 skill project-level + audit cron 1/tháng) Done
3-panel List/Inbox/Thao tác + sidebar accordion + UserDashboard Done
4-bảng data model (Header + 7 Details + Workflow + Changelog audit) Done
Mã HĐ gen tại Create + backfill legacy Done
4 master catalogs (Units/Materials/Services/WorkItems) + datalist autocomplete Done
Roles VN labels (ShortName) + Users dept/position + 13 demo users Done
RolesPage CRUD /system/roles + custom role admin Done
7 demo HĐ varied phases + details + comments + approvals workflow Done
User-kind approver runtime guard + Warning 20% SLA Done
Edit detail row inline (7 typed Update commands + EditRowDialog) Done
Master expand 15 NCC + 8 Project + backfill demo HĐ diverse Done
Deps audit script (scripts/deps-audit.ps1) Done
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 + 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)
CI manual checkout bypass github.com + Path filter docs-only skip Done (S5)
Tests Phase 3 mini — 6 test PE Workflow Designer versioning (total 77) Done (S5)
Export phiếu PDF/Excel PEIDocumentConverter + template 📝 Pending (không quan trọng)
Tests Phase 3 full — Application handlers (UpsertOpinion + Budget link validation, cần UserManager setup) 📝 Pending
Tests Phase 4-5 — API smoke + FE Vitest 📝 Pending (làm khi cần)
npm cache CI optimize (debug junction Move-Item issue #40) 📝 Pending
9+ Post-launch (E-signature, Bravo/SAP, Mobile, AI) 📝 Future

Run nhanh

# Terminal 1 — API (auto seed 12 role + 9 dept + 5 supplier + 3 project + 8 template + 7 workflow definition + 28 ContractType menu + 7 workflow menu)
dotnet run --project src\Backend\SolutionErp.Api

# Terminal 2 — Admin FE
cd fe-admin && npm run dev  # → http://localhost:8082

# Terminal 3 — User FE
cd fe-user && npm run dev   # → http://localhost:8080

Login: admin@solutionerp.local / Admin@123456

Quick sanity-check

Admin (:8082):

  • /dashboard → "Của tôi" row 4 card + KPI cards + charts
  • /contracts → list toàn bộ, filter phase/supplier/project
  • /contracts/new?type=5 → tạo HĐ Mua bán, pre-select type từ URL
  • /contracts/{id} → timeline + action dialog + attachments drag-drop + WorkflowSummaryCard
  • /system/workflows → 7-card landing (Thầu phụ/Giao khoán/NCC/Dịch vụ/Mua bán/NguyenTacNcc/NguyenTacDv)
  • /system/workflows/MuaBan → DefinitionCard active + history + "Tạo phiên bản mới" modal với Steps + Approvers (+Role / +User)
  • /system/permissions → 3-panel layout (Role list | Menu×CRUD matrix | Granted stats)
  • /system/users → Users CRUD + assign roles
  • /forms → upload .docx/.xlsx + render dialog Form↔JSON + Tải PDF

User (:8080):

  • /inbox?type=5 → HĐ Mua bán chờ role mình
  • /my-contracts?type=2 → HĐ Thầu phụ của tôi
  • /contracts/new?type=3 → tạo HĐ NCC
  • Sidebar nested: 📄 Hợp đồng → expand 7 type → expand "HĐ Mua bán" → Danh sách / Thao tác / Duyệt

Realtime check:

  • Login 2 tab (admin + user) → user tạo comment / transition → admin nhận toast + bell +1

Cần làm kế tiếp

🔥 Priority 0 — Session 6 (Ops + UAT focus)

Đa số feature gap đã đóng. Còn lại chủ yếu Ops/UAT + optional polish.

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 dailyscripts/backup-sql.ps1 chưa schedule Task Scheduler

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. 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"

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)

  1. UAT thật 1 tuần với 2-3 user — hard requirement từ roadmap. Kiến nghị:
    • User A: Drafter (QS/NV.PB) — tạo 3 HĐ mỗi type, đi hết 9 phase
    • User B: CCM — duyệt phase 6
    • User C: BOD — duyệt phase 7
    • Ghi bug / friction / đề xuất → backlog iter 2
  2. SMTP config để bật Email outbox:
    "Email": {
      "Host": "smtp.gmail.com",
      "Port": 587,
      "Username": "...",
      "Password": "...",
      "From": "noreply@solutionerp.local"
    }
    
    Khi có → thêm MailKit, IEmailSender, hook vào NotificationService.CreateAsync ngay trước khi enqueue realtime push.
  3. Rotate credentials — SA SQL password, vrapp password, JWT secret prod, Gitea runner registration token
  4. Schedule SQL backupschtasks /create /tn "SolutionErp Backup" /tr "powershell -File C:\...\scripts\backup-sql.ps1" /sc DAILY /st 03:00

A1. Định kỳ — Skill audit

Cadence: Mỗi đầu tháng (4 tuần). Lần audit kế tiếp: 2026-05-01.

Workflow xem docs/rules.md §9.4. Tóm tắt:

  1. Cross-check 6 skill hiện có với STATUS / gotchas / migration-todos
  2. Check repo nguồn 3rd party (alirezarezvani/claude-skills) có gì mới
  3. Update / archive / add skill nếu cần
  4. Log vào docs/changelog/skill-audit-2026-05.md

Trigger: user nói "audit skill" hoặc tự chạy đầu Phase mới.

B. Polish iterations (optional — khi UAT phát sinh)

  • Roles CRUD — admin tạo custom role ngoài 12 hardcoded (Domain.Identity.AppRoles)
  • User-kind approver runtime — data model WorkflowStepApprover.Kind=User + AssignmentValue=userId đã có, chỉ cần:
    // ContractWorkflowService.TransitionAsync (bổ sung):
    var userApprovers = step.Approvers.Where(a => a.Kind == ApproverKind.User)
                                      .Select(a => Guid.Parse(a.AssignmentValue));
    if (userApprovers.Any() && !userApprovers.Contains(actorUserId))
        throw new ForbiddenException();
    
  • Grant Workflows.Read cho non-admin role trong PermissionsPage → menu Wf_* auto-visible (inheritance đã có)
  • Warning notification 20% SLA — job emit khi SlaDeadline - now < sla * 0.2 && !SlaWarningSent, set flag
  • Reject → DangSoanThao E2E test với 3 role khác nhau
  • Deps scan CIdotnet list package --vulnerable + npm audit --audit-level=high

C. Non-goals / parked

  • E-signature (VNPT CA / FPT CA) — Phase 6
  • Bravo/SAP import NCC — Phase 6
  • Mobile app — Phase 6
  • AI OCR scan HĐ — Phase 6+

Lưu ý kỹ thuật quan trọng

Đọc gotchas.md (41 bẫy) trước khi:

  • Thêm package mới → .NET 10 compat (MediatR 14 fail → dùng 12.4.1)
  • Debug TS enum error → dùng const-object pattern (erasableSyntaxOnly)
  • Expression tree lỗi → tách switch ra ngoài LINQ
  • Deploy Windows Feature (WebSockets, etc.) → unlock section ở applicationHost (gotcha #25)
  • Workflow transition 403 → check Contract.WorkflowDefinitionId pin đúng không
  • Migration lỗi → 3 file đầy đủ (Designer + Migration + Snapshot)

Versioned workflow — quick ref

Contract.WorkflowDefinitionId pin tại Create. ContractWorkflowService.LoadAsync resolve order: pinned def → admin override → hardcoded Registry.For(type). Invariant: HĐ cũ giữ policy cũ qua FK Restrict. Detail: workflow-contract.md §7bis.

File đang active

Cấu trúc thư mục + file map: PROJECT-MAP.md. Git state: git log --oneline -10.

Credentials + URLs

admin@solutionerp.local / Admin@123456    ← Admin (QTV)

Demo users — 30 user (User@123456):

  ── 16 sample @solutionerp.local (giữ cho test legacy) ──
  bod.huynh, bod.le              Tổng GĐ + Phó GĐ NĐUQ
  pm.nguyen                      GĐ Dự án FLOCK 01 (PM)
  ccm.tran, pro.pham, fin.do     TPB CCM/PRO/FIN
  act.vu, equ.bui, hra.dang      Kế toán trưởng / TPB EQU / TPB HRA
  qs.hoang, qs.ngo               QS công trường (NV.PB)
  nv.cao, nv.dinh, nv.truong     NV Cung ứng/Tài chính/CCM (NV.PB)
  bod.tran, pm.le                Bonus NĐUQ + PM thứ 2

  ── 14 Solutions thật @solutions.com.vn (session 4) ──
  PRO 5:  tra.bui (TPB) + phuong.nguyen, binh.lethanh, danh.huynh, dat.tran (NV)
  CCM 7:  ngocanh.huynh (TPB) + ha.dao, cuong.do, long.le, ha.nguyen,
                                dung.nguyen, anh.nguyen (NV)
  ISO 1:  chau.le
  CEO 1:  huy.duong

⚠ Rotate ALL passwords trước UAT thật

Đánh giá nhanh

Tốt:

  • 3 domain HTTPS prod live, CI/CD xanh
  • Tier 3 feature-complete: attachment, realtime, form builder (upload + DynamicForm + PDF), versioned workflow (admin-configurable per ContractType, pin per contract), nested menu per type, 3-panel permissions
  • Clean-arch 3-project split đúng cho 2 cross-cutting service (realtime + document-converter)
  • 26 gotchas tích lũy, 8 session log, 40 docs — agent onboard nhanh
  • Invariant critical: "HĐ cũ giữ quy trình cũ" guaranteed by pinning (reference-based immutability, không snapshot copy)

Rủi ro còn:

  • UAT thật chưa chạy → có thể phát hiện edge case missing
  • SMTP chưa có → notification chỉ in-app (toast + bell), không email
  • User-kind approver chưa enable guard runtime (designer cho pick, nhưng transition dùng Role fall-back)
  • Credentials chưa rotate
  • SQL backup chưa schedule Task Scheduler