Wrap-up docs cho 4 chunk code đã push: -2ea2d27Chunk A — Mig 27 MenuItem +IsVisible +DisplayLabel + 3-file rule -ef394f8Chunk B — BE PATCH /menus/{key} + extend DTOs + UpdateMenuItemCommand -059bfcbChunk C — FE Admin MenuVisibilityPage ~210 LOC + menu key + seed -1ed6530Chunk D — FE User Layout filter !isVisible + render effectiveLabel Files updated: - docs/STATUS.md — Last updated + Recently Done row S20 turn 7 trên cùng (giữ S20 PE Detail UI row nguyên văn §6.5) - docs/HANDOFF.md — Last updated + TL;DR Session 20 turn 7 trên đầu + pending S21+ + carry blockers (giữ TL;DR Session 20 + 19 nguyên §6.5) - docs/changelog/migration-todos.md — Phase 9 Session 20 turn 7 done section + 3 defer item S21+ (giữ S20 + S19 nguyên §6.5) - docs/changelog/sessions/2026-05-11-1700-menu-visibility-mig27.md (NEW) — session log đầy đủ Q&A + 4 chunk + verify chain + stats KHÔNG đụng rules / architecture / PROJECT-MAP / workflow-contract / forms-spec / database-guide / schema-diagram / CLAUDE.md per §6.5 (drift S20 turn 7 defer cron audit 2026-06-01 — Mig 27 + 1 endpoint + 1 menu key sẽ check chung lúc đó). Path filter CI sẽ skip (docs-only commit). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
61 KiB
Migration To-dos — Atomic Roadmap
Tick
[x]khi xong. Phase 0-8 đã DONE — collapsed. Detail xem session logs trongdocs/changelog/sessions/. Active work: Phase 9 (UAT + Ops + carry over PE PDF export + Tests Phase 3-5).
✅ Phase 0-5 + Tier 3 — Done (2026-04-21..22)
| Phase | Focus | Trạng thái |
|---|---|---|
| 0 Draft | Scaffold .NET 10 + 2 Vite + parse FORM/QT + docs | ✅ |
| 1 Alpha Core | Auth + 12 role + Permission Matrix + 3 master CRUD + Contract draft | ✅ |
| 2 Form Engine | OpenXml + ClosedXML render + LibreOffice PDF + DynamicForm | ✅ |
| 3 Workflow | State machine 9 phase + RG-001 code gen + SLA job + attachments + SignalR realtime | ✅ |
| 4 Reporting | Dashboard KPI + Excel export + MyDashboard role-aware + brand identity #1F7DC1 | ✅ |
| 5 Production | CI/CD Gitea Actions + 3 IIS site + Let's Encrypt + Security headers + Users CRUD | ✅ |
| Tier 3 | Versioned workflow + 3-panel layout + 4-bảng overhaul + 4 master catalogs + 16 demo users + RolesPage CRUD + 7 demo HĐ varied | ✅ |
Detail chi tiết: docs/changelog/sessions/2026-04-21-*.md + 2026-04-22-0300-tier3-feature-complete.md + 2026-04-23-*.md.
✅ Phase 6 — Module Duyệt NCC (tiền-HĐ) — Done
Iter 1 (2026-04-23)
- Migration 12
AddPurchaseEvaluations— 10 bảng (Header/Suppliers/Details/Quotes/Approvals/Changelogs/Attachments/WorkflowDefinitions/Steps/StepApprovers) - Domain — 2 enum (Type A/B, Phase 7-state) + Policy record + Registry + FromDefinition builder
- Seed — 13 menu Pe_/PeWf_ + 2 WorkflowDefinition v01 (QT-DN-A 3-step, QT-DN-B 5-step)
- Application CQRS ~900 LOC — Create/Update/Transition/List/Inbox/Get/Delete + Supplier CRUD + Detail CRUD + Quote Upsert + SelectWinner + Changelog
- PurchaseEvaluationWorkflowService — policy guard + approval + notification + changelog
- PurchaseEvaluationsController — 17 endpoint REST
- FE 2 app — Types + PurchaseEvaluationsListPage 3-panel + Create page + PeDetailTabs + PeWorkflowPanel + Menu resolver Pe_*
- Kế thừa HĐ —
CreateContractFromEvaluationCommand(guard DaDuyet + SelectedSupplier + !ContractId) → Contract draft. FE CreateContractDialog pick ContractType. - Migration 13
AddPurchaseEvaluationCodeSequences— atomic MaPhieu sequencePE/{YYYY}/{A|B}/{Seq:D3} - Demo PE seed — 4 phiếu varied phase (A-001/A-002/A-003/B-001) + Pe_* permission defaults 7 role × 9 menu key
Session log: 2026-04-23-2300-purchase-evaluations.md + 2026-04-24-1030-pe-polish-demo-maphieu-perms.md.
Iter 2 — UX polish (2026-04-24)
- Rename menu "Phương Án" → "Giải pháp" + backfill DB (zero breaking change)
- Menu tree inheritance extend Pe_/PeWf_ (
GetMyMenuTreeQuery+ 4 root) - Accordion mutex Pe_* groups + sidebar w-72 + label nowrap
- NavLink active check query string (queryMatches helper) — fix 2 leaf cùng highlight
- PE detail flat layout: Panel 2 = 4 section (Thông tin/NCC/Hạng mục/Bảng so sánh), Panel 3 += Approvals + Changelog
- Upload file đính kèm per-NCC (SupplierAttachmentsCell) + Bảng so sánh tổng (GeneralAttachmentsSection, supplierRowId=null) + enum
ComparisonTable=4 - readOnly mode menu "Duyệt" (pendingMe=1) — hide Sửa/Xóa/Thêm/Edit/Upload/Delete, giữ download + transition + comment
- Contract: move Lịch sử điều chỉnh Panel 2 → Panel 3 (Chi tiết HĐ full-width)
- Demo email rebrand
@solutionerp.local→@solutions.com.vn+BackfillUserEmailDomainAsync(idempotent rename 4 field Email/NormalizedEmail/UserName/NormalizedUserName)
Session log: 2026-04-24-chot-session-3-pe-polish.md.
✅ Domain rebrand .huypham.vn → .solutions.com.vn (2026-04-24)
- 18 file repo (FE env + scripts + CI/CD + docs + skill + code comments)
scripts/migrate-domains.ps1(ASCII-only #30) — 3 IIS binding + 3 cert Let's Encrypt + auto HTTPS + redirect- CI/CD auto rebuild BE CORS + FE bundle VITE_API_BASE_URL
- E2E verified 3 domain live + preflight OK
Sub: api.solutions.com.vn · admin.solutions.com.vn · eoffice.solutions.com.vn. Old .huypham.vn vẫn fallback (chưa remove — Phase 9 Ops).
📝 Phase 7 — PE feature gap + Budget BE (Session 4 partial done)
A. PE feature gap (3 task — phần lớn đóng ở Phase 8 S5)
- PE Workflow admin designer UI
/system/pe-workflows/:typeCode— done S5 (5d94bb4)- BE
Application/PurchaseEvaluations/PeWorkflowAdminFeatures.cs(mirrorWorkflowAdminFeatures.cs) Api/Controllers/PeWorkflowsController.cs- FE
fe-admin/src/pages/system/PeWorkflowsPage.tsx+PeWorkflowDesigner.tsx - Route
/system/pe-workflows/:typeCode(menu PeWf_* + resolver đã sẵn)
- BE
- Ý kiến 4 phòng ban (Phê duyệt / P.CCM / P.MuaHàng / SM-PM) ở tab Thông tin — done S5 (
5d94bb4, Migration 15)- Option A: 4 text field + signoff date + UserId vào header
- Option B: dùng
PurchaseEvaluationApprovalsvới roleKind extra field - Chốt: dùng Migration 15
AddPurchaseEvaluationDepartmentOpinions(separate table UNIQUE PEId+Kind, 4 box sign-off 2x2 grid OpinionBox như Excel mẫu) — tốt hơn Option A/B vì audit qua Changelog + Upsert preserve chữ ký cũ khi text-only edit.
- Export phiếu PDF/Excel — tái dùng
IDocumentConverter+ templatePE-TrinhDuyet.docx→ carry over Phase 9 (user pending — không quan trọng lắm)
B. Optional polish (carry over Phase 9 — làm khi UAT phát sinh)
- Auto-map PE Details → Contract per-type Details khi gen HĐ (phức tạp vì 7 schema khác nhau)
- Payment terms tách field từ JSON → 6 column (Tạm ứng/TT tạm/Quyết toán/Bảo hành/Hạn mức/Đánh giá)
- Matrix Quotes bulk paste từ Excel
- fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi"
C. Ops (carry over Phase 9 — Hard blockers)
- Remove binding cũ
.huypham.vnsau verify stable:ssh vietreport-vps ; cd C:\solution-erp\scripts ; .\migrate-domains.ps1 -RemoveOld -SkipCert - win-acme scheduled task fix unhealthy (cert expire 2026-06-18)
- UAT thật 1 tuần với 2-3 user (30 demo user — 16 sample + 14 Solutions thật)
- SMTP config → Email outbox
- Rotate credentials (admin + 30 demo + SA + vrapp + JWT)
- Schedule SQL backup Task Scheduler
D. Module Ngân sách (Budget) — Session 4 ✅ partial done
- Migration 14
AddBudgets— 4 bảng (Budgets/BudgetDetails/BudgetApprovals/BudgetChangelogs) + index BudgetId nullable trên Contract & PurchaseEvaluation - Domain —
Budget(Header) +BudgetDetail(flat row) +BudgetApproval+BudgetChangelog+ enumBudgetPhase5-state +BudgetEntityTypeHeader/Detail/Workflow BudgetPolicy.Defaulthardcoded simple 3-step (Drafter→CCM→CEO + Reject từ ChoCCM/ChoCEO về DangSoanThao)- Application CQRS ~340 LOC — Create + UpdateDraft + Transition + List + GetDetail + Delete (only DangSoanThao/TuChoi) + Detail CRUD (auto-recompute TongNganSach) + ListChangelogs
BudgetsController11 endpoint REST- Menu seed
Budgetsroot + 3 leaf (Bg_List/Bg_Create/Bg_Pending) order=27 icon Wallet - 14 demo user Solutions thật — PRO 5 + CCM 7 + ISO 1 + CEO 1 (pwd
User@123456). Reconcile pattern (gotcha #38 4-field rename). Tổng 30 user (16 sample cũ + 14 Solutions thật mới).
Session log: 2026-04-28-chot-session-4-budget.md.
E. Pending migrations
AddPePaymentTermFields(nếu chốt UX tách field — JSON blob → 6 column)AddPurchaseEvaluationDepartmentOpinions✅ migration 15 (S5)AddBudgetCodeSequences(nếu chốt format MaNganSach atomic — hiện Random.Shared)AddBudgetVersionedWorkflow(nếu user cần admin config UI thay vì hardcodedBudgetPolicy.Default)
✅ Phase 8 — Budget FE + PE/HD integration (Session 5 done)
A. FE Budget pages — done ✅
fe-admin/src/types/budget.ts(BudgetPhase 5-state enum + DTO types)fe-admin/src/pages/budgets/BudgetsListPage.tsx(3-panel[340px_1fr_360px]+ filter Phase/Năm + ?phase=Pending alias + readOnly mode + BudgetDetailPage fullpage mobile)fe-admin/src/pages/budgets/BudgetCreatePage.tsx(form Header — Tên/Năm/Dự án/Phòng ban/Mô tả)fe-admin/src/components/budgets/BudgetDetailTabs.tsx(Section Thông tin Header + Section Hạng mục table CRUD inline auto-compute ThanhTien=KL×ĐG)fe-admin/src/components/budgets/BudgetWorkflowPanel.tsx(Panel 3 timeline activePhases + nextPhases buttons + Dialog comment + Approvals/Changelog)- Mirror tất cả sang
fe-user/ - App.tsx routes
/budgets,/budgets/new,/budgets/:idcả 2 app - Menu resolver
Bg_*(Bg_List →/budgets, Bg_Pending →/budgets?phase=Pending, Bg_Create →/budgets/new)
B. PE/Contract → Budget integration — done ✅
- PE form + Select "Ngân sách" filter Phase=DaDuyet, ProjectId match, BE validate
- Contract form (Header + Edit) tương tự, EditForm read-only link card khi !isDraft
- PE Detail Hạng mục thêm cột "NS link · Δ" — match per-row qua
groupCode|itemCode+ footer aggregate (xanh dưới / đỏ vượt / xám khớp) - PE Detail UI restructure 4 section đánh số match form spec PHIẾU TRÌNH KÝ
- BE: BudgetSummaryDto shared + Create/Update PE+Contract commands + BudgetId? + GetQueries load Budget
- CreateContractFromEvaluation carry forward pe.BudgetId → contract.BudgetId
C. PE Workflow Designer admin UI — done ✅
- BE
PeWorkflowAdminFeatures.cs~250 LOC mirror Contract pattern - BE
PeWorkflowsController2 endpoint reuse policyWorkflows.* - FE
PeWorkflowsPage.tsx~500 LOC + designer dialog (clone/edit/+Role/+User) - App.tsx route
/system/pe-workflows/:typeCode
D. Ý kiến 4 phòng ban — done ✅
- Migration 15
AddPurchaseEvaluationDepartmentOpinions(UNIQUE PEId+Kind) - Domain entity + enum
PeDepartmentKind(PheDuyet/Ccm/MuaHang/SmPm) - BE Upsert (sign=true → set SignedAt+UserId, sign=false giữ chữ ký cũ) + Delete + 2 endpoint
- FE Section "5. Ý kiến 4 phòng ban (sign-off)" 2x2 grid OpinionBox
E. Tests Phase 1-2-3mini + CI optimize — done ✅
- Phase 1 —
tests/SolutionErp.Domain.Tests/(xUnit + FluentAssertions 7.2): 54 test policy state machine (Contract WF + PE WF + Budget) + Registry + FromDefinition versioned + UserKindApprover - Phase 2 —
tests/SolutionErp.Infrastructure.Tests/(EF SQLite + TestApplicationDbContext overridenvarchar(max) → TEXT): 17 test code generator format + sequence + year boundary + persistence verify - Phase 3 mini —
tests/.../Application/PeWorkflowAdminTests.cs: 6 test CreatePeWorkflowDefinitionCommand versioning (auto-increment + deactivate cũ + EvaluationType independence + steps/approvers persistence) - CI gate
.gitea/workflows/deploy.yml— 2 stepdotnet testtrước build, fail → no deploy - Total 77 test pass / ~3s
- CI manual checkout bypass github.com — fix gotcha #39 (act_runner TCP timeout 21s)
- CI path filter docs-only skip — gotcha #41 (paths-ignore behavior)
- Tests Phase 3 full — Opinion Upsert + Budget link validation (cần Identity UserManager setup helper)
- npm junction cache CI optimize (rollback ở
a21790d— gotcha #40 chưa debug)
📝 Phase 9 — UAT + Ops + carry over (Session 6+ active)
✅ Session 20 turn 7 done (2026-05-11) — Admin Ẩn/Hiện + Đổi tên menu eOffice (Mig 27, 5 chunk 2ea2d27 → ef394f8 → 059bfcb → 1ed6530 → Chunk E Docs)
User UAT yêu cầu admin quản lý menu eOffice (fe-user) — Ẩn/Hiện + Đổi tên. Confirm "chưa có" → tạo mới. User Q2=b clarify quan trọng: DisplayLabel CHỈ áp fe-user, admin sidebar giữ Label gốc.
-
Chunk A (
2ea2d27) Schema + Mig 27 — Domain MenuItem +IsVisible bool=true +DisplayLabel string?(200). EF config HasDefaultValue + HasMaxLength. Migration 27AddVisibilityAndDisplayLabelToMenuItems(2 AddColumn) — 3-file rule. Apply LocalDB_Dev+_Designqua --connection override (memoryfeedback_designtime_runtime_db). -
Chunk B (
ef394f8) BE API — DTO MenuNodeDto + MenuItemDto +isVisible +displayLabel. GetMyMenuTreeQueryHandler pass through (KHÔNG filter server-side, 2 FE tự quyết). NEW UpdateMenuItemCommand + Validator + Handler (whitespace → null). MenusController +PATCH /api/menus/{key} [Authorize Policy="Permissions.Update"] body{isVisible, displayLabel}. -
Chunk C (
059bfcb) FE Admin — Domain MenuKeys +MenuVisibility + All[]. DbInitializer +leaf "Menu eOffice" Icon=Eye Order=94 (Workflows shift 94→95). Manual seed Mig 27 LocalDB Dev (INSERT MenuItems + Permissions Admin). FE Admin types/menu.ts mirror, menuKeys.ts +const, Layout resolver +/system/menu-visibility, App.tsx +Route. NEW pages/system/MenuVisibilityPage.tsx ~210 LOC: PageHeader + 4 StatCard + Search + Table 5 cột (Key mono + parentKey↳ / Tên gốc / Input "Tên hiển thị" inline / Toggle Eye-EyeOff emerald-amber / Save dirty + Khôi phục custom). onSuccess invalidate ['menus','all'] + ['my-menu'] live update sidebar. -
Chunk D (
1ed6530) FE User — fe-user types/menu.ts mirror. Layout.tsx filterForUser 2 tầng (USER_HIDDEN_KEYS hardcode structural + !isVisible dynamic). Helper effectiveLabel(n) = displayLabel?.trim() || label. Replace 3 callsite {node.label} → {effectiveLabel(node)}. USER_FIXED_TOP "__inbox" +isVisible:true cho type check. fe-admin Layout KHÔNG đụng — admin sidebar render Label gốc + show hết menu (user Q2=b). -
Chunk E Docs (current) — STATUS Recently Done top + Last updated S20 turn 7. HANDOFF TL;DR Session 20 turn 7 trên đầu (giữ S20 prev nguyên §6.5). migration-todos done section (file này) + pending S21+. Session log
2026-05-11-1700-menu-visibility-mig27.md. KHÔNG đụng rules / architecture / PROJECT-MAP / workflow-contract / forms-spec / database-guide / schema-diagram / CLAUDE.md (defer cron audit 2026-06-01).
Stats Session 20 turn 7: 26→27 mig (+1 AddVisibilityAndDisplayLabelToMenuItems), 59 DB tables (no change), ~141→142 endpoints (+1 PATCH /menus/{key}), 33→34 FE pages (+1 MenuVisibilityPage), ~60→61 menu key (+1 MenuVisibility), 81 test pass (Q4 UAT defer), 44 gotcha (no new). Memory entries 14 (no new).
Defer Session 21+ (mới sau S20 turn 7):
- Test PATCH /api/menus/{key} validate Key required + DisplayLabel trim
- Skill
permission-matrixcross-ref section "menu visibility" — defer cron audit 2026-06-01 - UX verify trong UAT: admin ẩn menu cha → children có ẩn theo không? (FE filter check per-node
!n.isVisible, parent vẫn hiện thì children render. Có thể cần propagate hidden tree-level nếu UAT phản hồi)
✅ Session 20 done (2026-05-11) — PE Detail UI restructure 3 yêu cầu UX user (4 chunk 9dee00d → 2bba851 → f2f01f4 → Chunk D Docs)
User UAT live feedback: "Logic khá OK rồi, điều chỉnh UI Duyệt NCC 1 tý". 3 yêu cầu cụ thể chốt qua Q&A 4 câu (Q1=a giữ Section "Chọn NCC TP" / Q2=a NCC shared + 1 hạng mục demo / Q3=a chỉ hiện NV đã ký / Q4 public luôn skip dotnet test). FE-only restructure (1 hook BE nhẹ auto-seed Detail).
-
Chunk A (
9dee00d) BE auto-seed + FE reorder section —CreatePurchaseEvaluationCommandHandlerthêm 1 PurchaseEvaluationDetail mặc định khi tạo phiếu: GroupCode="01", GroupName="Hạng mục chính", NoiDung=TenGoiThau, DonGiaNganSach=ThanhTienNganSach=Budget.TongNganSach (nếu link) hoặc BudgetManualAmount fallback hoặc 0; Changelog Insert audit. FE reorder PeDetailTabs section (mirror 2 app): 1.Thông tin / 2.Hạng mục lên #2 / 3.Chọn NCC / 4.NCC tham gia / 5.Ý kiến. Verify dotnet build pass. -
Chunk B (
2bba851) Nested grid Hạng mục → NCC expand — ItemsTab restructure thành listHangMucCard(1 card / 1 hạng mục, expanded=true mặc định cho 1 hạng mục demo). Header: GroupCode + NoiDung + 3 stat (KL/ĐG/TT) + NS link Δ + Pencil/Trash + ▼/▶ toggle. Expand body: NCC inline table 8 cột (NCC/Liên hệ/Điều khoản TT/File báo giá/ĐG chưa VAT/ĐG có VAT/Thành tiền/Action). Click cell quote → QuoteDialog reuse. + Thêm NCC / Sửa NCC reuse 2 dialog cũ. Winner ✓ button per row. DropSuppliersTabfunction dead code ~134 LOC. Giữ AddSupplierDialog + EditSupplierDialog + SupplierAttachmentsCell (HangMucCard call lại). Section 4 NCC tham gia cũ bỏ → 4 section final (1.Thông tin / 2.Hạng mục nested / 3.Chọn NCC TP thắng thầu / 4.Ý kiến). Verify npm build × 2 app pass sau khi catch TS6133 SuppliersTab + SupplierAttachmentsCell unused. -
Chunk C (
f2f01f4) Section Ý kiến gộp đồng cấp cùng Phòng — FE-only KHÔNG đụng Mig 26 schema (vẫn UPSERT 1 row / Level qua Service). LevelOpinionsSectionV2 forEach step → 1StepOpinionsBox(replace grid-cols-2 N approvers). Header: "Bước N — Tên" + dept badge emerald + "X/Y đã duyệt" counter. Body filter opinions theo step.order → sort levelOrder asc + signedAt asc → renderStepOpinionEntryper signed (tên NV + Cấp badge slate + admin override badge amber nếu có + emerald rounded-full timestamp + comment whitespace-pre-wrap). NV chưa duyệt KHÔNG hiển thị (Q3=a). DropLevelOpinionBoxfunction. Mirror fe-admin + fe-user. Verify npm build × 2 app pass. -
Chunk D Docs (current) — STATUS Recently Done top + header narrative · HANDOFF TL;DR Session 20 + pending S21+ + hard blockers ops carry · migration-todos Phase 9 Session 20 done section (file này) · Session log mới
2026-05-11-1100-pe-ui-restructure-s20.md. KHÔNG đụng rules/architecture/PROJECT-MAP/workflow-contract/forms-spec/database-guide/schema-diagram/CLAUDE.md (defer cron audit 2026-06-01 — per §6.5 không cố sửa khi không cần; S20 không thêm migration / gotcha mới nên count drift không đổi từ S19).
Stats final Session 20: 26 mig (no new), 59 DB tables (no new), ~141 endpoints (no new — reuse + 1 BE hook trong existing CreatePE handler), 33 FE pages (no new), 81 test pass (no change — Phase 9 UAT iteration skip test mỗi chunk Q4), 44 gotcha (no new). LOC delta net ~+25 FE (gross ~+700 / ~−725).
Defer Session 21+:
- Test V2 Service wire (Chunk B Service hook S19 + Section gộp Chunk C S20) — defer khi UAT user confirm + có sample data Production.
- Test regression B4 silent 403 S18 (HIGH §7 priority — vi phạm rule test-before bug fix) — per-action
[Authorize(Policy=...)]ApprovalWorkflowsV2Controller. - Test Mig 25 PATCH user-selectable endpoint (MED — admin scope hẹp).
- Contract V2 wire (Mig 27/28 mirror PE pattern) — biggest pending Plan. Mig 27 Contract.ApprovalWorkflowId + CurrentApprovalLevelOrder; Mig 28 ContractLevelOpinions; Service ApproveV2Async; ContractCreatePage Workspace Select V2; pin V2 mặc định cho ContractType; ContractDetailContent Section "Ý kiến cấp duyệt" V2 dynamic mirror S20 Chunk C.
- Phân quyền strict V2 — vẫn loose UAT. Sau confirm V2 flow → list/inbox/detail filter actor scope.
- Drop legacy V1 cleanup sau UAT chốt (drop tables WorkflowDefinitions/Steps/Approvers + drop column RejectedAtStepIndex/RejectedFromPhase deprecated S17 + drop ApproveV1LegacyAsync branch).
- Drop Mig 15 cho V2 phiếu cleanup sau UAT confirm (Mig 30 drop PurchaseEvaluationDepartmentOpinions, hoặc giữ cả 2 backward compat — Q3 user chốt giữ legacy).
- schema-diagram §16 PE Level Opinions V2 + §17-21 Mig 18-21 — defer cron audit 2026-06-01.
- Skill
ef-core-migrationfrontmatter "21 migration" stale (thực 26) +dependency-audit-erpcount gotcha 41 stale (thực 44) — defer cron audit 2026-06-01.
✅ Session 19 done (2026-05-09) — PE Section 5 V2 dynamic theo ApprovalWorkflowLevel + Mig 26 (4 commit 873e7a1 → Chunk D Docs)
User UAT live tiếp Session 18. 1 polish nhỏ + 1 feature lớn (Section 5 dynamic). Spec chốt 5 câu Q&A trước code (Q1=1B sync auto / Q2=2A+Admin / Q3=V2 hết / Q4=4C+placeholder / Q5=5A grid-cols-2).
-
Polish 3 button (
873e7a1) Hành động Workflow Panel — rút gọn label "✓ Duyệt / ← Trả lại / ✗ Từ chối" (bỏ "→ Chờ X" / "(về Drafter sửa)" / "Hủy /") + 3 màu phân biệt (emerald/amber/red) + font-medium → font-bold. Phase đích vẫn hiện qua tooltip title hover. Mirror fe-admin + fe-user. -
Chunk A (
77a3058) Domain + Mig 26 + EF — EntityPurchaseEvaluationLevelOpinion : AuditableEntity(PEId+LevelId UNIQUE composite, Comment nvarchar(2000), SignedAt datetime2, SignedByUserId Guid, SignedByFullName nvarchar(200) denorm). EF FK Cascade Pe + Restrict Level. Migration 26AddPeLevelOpinionsForV2(1 CREATE TABLE + 2 FK + 2 index — UNIQUE composite + IX LevelId). 3-file rule. Apply LocalDB SolutionErp_Dev OK (Mig 25 + 26 catchup). -
Chunk B (
90baa8e) Service V2 hook + DTO + GET include — ServiceApproveV2Asyncsau line log approval → UPSERT row LevelOpinion cho Cấp hiện tại (match level theo ApproverUserId == actorUserId, fallback first khi Admin override). Reject KHÔNG sync. Comment empty → "(duyệt — không ý kiến)" placeholder. HelperResolveActorFullNameAsyncdenorm SignedByFullName. DTOPurchaseEvaluationLevelOpinionDto15 fields. GET handler Include LevelOpinions + helperBuildLevelOpinionsAsyncJOIN Steps/Levels + Departments + Users → denorm DTO list. Empty cho V1 / V2 chưa có cấp duyệt. -
Chunk C (
6e913b3) FE Section 5 V2 dynamic mirror 2 app — TypePeLevelOpinion+PeDetailBundle.levelOpinions[]. Section 5 conditional:evaluation.approvalWorkflowIdset →<LevelOpinionsSectionV2/>(dynamic), else<DepartmentOpinionsSection readOnly/>(V1 legacy fallback Mig 15).LevelOpinionsSectionV2: forEach Step (header "Bước N — Phòng X" badge emerald + hint số người duyệt) → grid-cols-2 chostep.levels.flatMap(level => level.approvers.map(approver => <LevelOpinionBox/>)).LevelOpinionBoxread-only: title "Cấp N — " + badge amber "⚠ Admin duyệt thay" khi override + badge emerald "✓ Đã duyệt" + empty "— chưa duyệt" + footer signedAt. Mirror fe-admin + fe-user (rule §3.9). -
Chunk D Docs (current) — STATUS Recently Done top + header narrative · HANDOFF TL;DR Session 19 + 7 cảnh báo Session 20+ (giữ S18 nguyên văn theo §6.5) · CLAUDE.md (root) count 25→26 mig + 58→59 tables + Mig 26 description block · migration-todos Phase 9 Session 19 done section + Defer Session 20+ checklist · Session log mới
2026-05-09-0400-pe-section-5-v2-dynamic-mig26.md. KHÔNG đụng rules/architecture/PROJECT-MAP/workflow-contract/forms-spec/database-guide/schema-diagram (defer cron audit 2026-06-01) — per §6.5 không cố sửa khi không cần.
Stats final Session 19: 26 mig (+1), 59 DB tables (+1), ~141 endpoints (no new — UPSERT auto qua Service hook không endpoint riêng vì Q1=1B), 33 FE pages, 81 test pass (no change — feature mới UAT defer test §7), 44 gotcha (no new). Memory entries 14 (no new).
Defer Session 20+:
- Test V2 Service wire mới (Chunk B Service hook) — defer khi UAT user confirm + có sample data Production. Domain test ApproveV2 + UPSERT opinion match logic + Admin override match firstLevel + comment empty placeholder.
- Drop Mig 15 cho V2 phiếu (cleanup sau UAT confirm) — sau khi không còn phiếu V2 dùng
PurchaseEvaluationDepartmentOpinions. Mig 27 cleanup drop bảng + entity. Hoặc giữ cả 2 backward compat. - Migrate phiếu V1 cũ sang V2 (data migration) — admin tool chuyển ApprovalWorkflowId. Hiện chưa làm (Q3 user nói chuyển V2 hết = phiếu MỚI dùng V2, V1 cũ giữ legacy).
- Contract V2 wire (Mig 27/28) — mirror PE pattern: Contract.ApprovalWorkflowId + ContractLevelOpinions Mig 28 + Service ApproveV2Async + ContractDetailContent Section 5 V2. Audit-reuse pattern.
- Phân quyền strict V2 — vẫn loose UAT. Sau confirm V2 flow → list/inbox/detail filter actor scope.
- schema-diagram §16 PE Level Opinions V2 + §17-21 Mig 18-21 — defer cron audit 2026-06-01.
- Skill
ef-core-migrationfrontmatter "21 migration" stale (thực 26). Defer cron audit 2026-06-01. - Skill
dependency-audit-erpcount stale. Defer cron audit 2026-06-01.
✅ Session 18 done (2026-05-08 19:45) — PE V2 polish + Clone B + Mig 25 IsUserSelectable + 4 bug fix UAT (7 commit aaa1c6c → 32a8d4d)
User UAT live tiếp Session 17, chuỗi polish nhỏ + clone V2 cho type B. Áp memory feedback_uat_skip_verify (skip dotnet test mỗi chunk, push ngay) + lesson rename/remove → bắt buộc npm run build.
-
B1 (
aaa1c6c) Pe Duyệt filter cứng "Đã gửi duyệt" — bỏ dropdown trạng thái + filter cứng client-sidegetPeDisplayStatus === DaGuiDuyet. Hint amber "Lọc cố định". Workaround BE /inbox loose UAT trả phiếu Nháp (phân quyền strict V2 pending). -
B2 (
917446d) HistoryTab filter Trả lại / Gửi duyệt lại — FE filter (BE keep audit data đầy đủ): chỉ events Workflow Transition về TraLai (phaseAtChange=98) + từ TraLai (summary "TraLai →") + sửa nội dung khi phaseAtChange=TraLai. -
B3 (
937eb24) Clone V2 cho B (DuyetNccPhuongAn) — Audit reuse trước thay vì duplicate. Schema chung qua ApplicableType discriminator → chỉ 3 file ~60 LOC: MenuKeys.cs +const + All array, DbInitializer.SeedMenusAsync +leaf B (Order=2) + new SeedSampleApprovalWorkflowsV2Async (idempotent skip nếu admin đã tạo workflow B), fe-admin/menuKeys.ts +const. Memoryfeedback_audit_reuse_before_clone.mdcapture pattern. -
B4 (
f77ea38) Fix silent 403 ApprovalWorkflowsV2 — Drafternv.testWorkspace dropdown empty silent. Root: class-level[Authorize(Policy = "Workflows.Read")]→ non-admin 403, TanStack Query catch silent. Fix: class-level[Authorize]only, GET cho any authenticated; POST/DELETE giữWorkflows.Createadmin-only. Gotcha #44. -
B5 (
a9c0857) Fix sidebar highlight queryMatches transient keys — Click row → URL có id transient → exact-set mismatch → menu unhighlight. Fix:TRANSIENT_QUERY_KEYS = {id, q, editHeader, page, phase, awId}strip trước compare. Mirror fe-admin + fe-user Layout.tsx. -
B6 (
2a53107) Mig 25 + Designer pin toggle + bỏ "(clone)" + Workspace filter — Migration 25AddIsUserSelectableToApprovalWorkflows: ALTERIsUserSelectable bit+ Sql backfillWHERE IsActive=1 SET 1. Domain +property. DTO +field. CreateAwDefinitionCommand set default true. New SetAwUserSelectableCommand + Handler. API PATCH/api/approval-workflows-v2/{id}/user-selectable. DbInitializer SeedSample +IsUserSelectable=true. FE Designer +badge "Cho user chọn" + button Ghim/Bỏ ghim + mutation toggleSelectable. Designer name auto-fill bỏ "(clone)" suffix. FE Workspace fetch filterw.isUserSelectable === true(cả fe-admin + fe-user). -
B7 (
32a8d4d) Cleanup orphan zip —.claude.zip + docs.ziplỡ tay vào commit B6 (git add -A). Untrack + add*.ziprule .gitignore.
Stats final Session 18: 25 mig (+1), 58 DB tables (no new — Mig 25 chỉ ALTER cột), ~141 endpoints (+1 PATCH), 33 FE pages, 81 test pass (no change — feature mới UAT defer test §7), 44 gotcha (+1 #44 silent 403). Memory +1 entry.
Defer Session 19+:
- Contract V2 wire (Mig 26) — mirror PE pattern: thêm
Contract.ApprovalWorkflowId+CurrentApprovalLevelOrder+ContractWorkflowService.ApproveV2Async+ Workspace Select V2 trong ContractCreatePage. Pin V2 mặc định cho ContractType. - Phân quyền strict V2 — hiện loose UAT (mọi authenticated thấy mọi phiếu V2). List = Drafter + approver any-Step + Admin. Cũng giải quyết bug "/inbox loose trả phiếu Nháp" — sau khi BE filter strict, B1 FE filter có thể relax.
- Drop legacy V1 (Mig 27 cleanup) sau khi không còn phiếu pin
WorkflowDefinitionId(V1): dropWorkflowDefinitions+WorkflowSteps+WorkflowStepApprovers+ drop deprecated columnsRejectedAtStepIndex/RejectedFromPhase. DropApproveV1LegacyAsyncbranch trong Service. - Test V2 Service wire (defer khi UAT confirm + có sample data thật) — Domain test ApproveV2Async + match logic + TraLai entry → Cấp 1 reset.
- Budget V2 wire (defer xa hơn — sau Contract V2)
- Sample seed B — sau UAT có thể remove (admin đã tạo workflow thật), hoặc giữ làm fallback. Idempotent skip không clobber.
- schema-diagram §17-21 Mig 18-21 vẫn chưa update (defer cron audit 2026-06-01)
- Skill
ef-core-migrationfrontmatter "21 migration" stale (thực 25). Defer cron audit 2026-06-01. - Skill
dependency-audit-erp"26+/41 bẫy" stale (thực 44). Defer cron audit 2026-06-01.
✅ Session 17 done (2026-05-08) — PE Workflow V2 schema + Service wire end-to-end (Mig 22-24, 13 commit c847dc0 → de0f38d)
User chốt sau Session 16 drastic refactor flat (Mig 21) vẫn chưa đúng intent. Yêu cầu schema riêng + Menu mới "Duyệt NCC (Mới)" — Quy trình > Bước (Phòng) > Cấp (NV cụ thể qua ApproverUserId). State machine 5 trạng thái với Trả lại = Phase RIÊNG (Option A user chốt diagram).
-
Mig 22
AddApprovalWorkflowsV2— 3 entity ApprovalWorkflow/Step/Level + ApplicableType enum (DuyetNcc/DuyetNccPhuongAn/Contract). 3 CREATE TABLE + UNIQUE (Code, Version) + FK Cascade Step→Workflow + Level→Step + FK Restrict Department + ApproverUserId. DbInitializer +menu V2. Designer page/system/approval-workflows-v2/:typeCode~480 LOC. (c847dc0/f6047d5/2781c7e/12daa7f) -
Designer iter 2 đúng intent: max 3 cấp × N NV/cấp + sequential gating C2/C3 disabled khi prev empty + filter NV theo Phòng + no-dup same level. Validator BE Order∈{1,2,3} + HaveSequentialOrders + HaveNoDuplicateApproverInSameLevel. (
9712778iter 1 sai →f3bea3citer 2 đúng) -
State machine 5 trạng thái Nháp / Đã gửi duyệt / Trả lại / Từ chối / Đã duyệt. TraLai = Phase RIÊNG (98), KHÔNG revert DangSoanThao + KHÔNG jump-back step. Drafter từ TraLai sửa+gửi lại chạy LẠI từ Cấp 1 Bước 1. ContractPhase + BudgetPhase +TraLai. PE/Contract/Budget Policy + Service Reject branch trỏ → TraLai. RejectedAtStepIndex/RejectedFromPhase deprecated (giữ DB column). 4 test mới TraLai entry point. FE rename "Bản nháp" → "Nháp". (
ff21120) -
Mig 23
AddApprovalWorkflowIdToPurchaseEvaluation— pin V2 vào PE entity. CreatePurchaseEvaluationCommand +Validate ApplicableType match PE.Type. UpdateDraft cho phép sửa Phase=Nháp/TraLai. Workspace Select bắt buộc workflow lúc create + display "QT-DN-V2-001 v01 — Tên (đang áp dụng)". (0a40c65) -
Mig 24
AddCurrentApprovalLevelOrderToPe+ Service V2 wire. PE Service branch theoApprovalWorkflowIdset or null: V2ApproveV2Asyncgroup Levels by Order = Cấp (OR-of-N approvers cùng cấp), matchactor.Id ∈ ApproverUserId, advance levelOrder++ → idx++ + reset levelOrder=1 → DaDuyet. V1ApproveV1LegacyAsyncgiữ logic cũ. Synthetic PolicyForV2Schema()cho FE nextPhases. (b41484b) -
UX V2-aware disable button + banner. DTO
CurrentApproval+ApprovalFlow(full Steps/Levels Status Done/Current/Pending). Banner emerald "Đến lượt bạn" / amber "Không phải lượt bạn — chỉ {NV X / Y} duyệt được". Button Duyệt forward disabled khi non-approver + tooltip. Trả lại + Từ chối vẫn enabled. Inbox V2-awareResolveV2InboxIdsAsync. 2 dropdown filter "Quy trình" + "Trạng thái" (chỉ ở Duyệt). Panel 3 thay 4 phase cards bằng flow workflow thực tế. (d814429/9e63e2d/d250ae4/74745a7/de0f38d) -
Test setup: SQL
clean-transactional-uat.sqlclean prod (9 PE + 11 HĐ + 19 Notif xóa) giữ master via SSH VPS. Test usernv.test@solutions.com.vn/TestUser@123456(Drafter, CCM) tạo qua API admin. (ac41d5e)
Stats final Session 17: 24 mig (+3), 58 DB tables (+3), ~140 endpoints (+5), 33 FE pages (+1 Designer V2), 81 test pass (+4 TraLai entry point Domain).
Defer Session 18+:
- Contract V2 wire (Mig 25) — mirror PE pattern: thêm
Contract.ApprovalWorkflowId+CurrentApprovalLevelOrder+ContractWorkflowService.ApproveV2Async+ Workspace Select V2 trong ContractCreatePage - Phân quyền strict V2 — hiện loose UAT (mọi authenticated user thấy phiếu V2). List chỉ Drafter + approver any-Step + Admin
- Drop legacy V1 sau khi không còn phiếu pin
WorkflowDefinitionId: drop tables + cleanup migration dropRejectedAtStepIndex/RejectedFromPhasecolumns - Admin role bypass decision prod — option C có audit log "[Admin override]" nếu cần (hiện UAT bypass không log riêng)
- Test V2 Service wire (defer khi UAT confirm + có sample data) — Domain test cho ApproveV2Async match logic
- Budget V2 wire (defer xa hơn — sau Contract V2)
✅ Session 16 done (2026-05-08) — DRASTIC REFACTOR flat workflow Phòng × Cấp (Mig 21, 2 commit Chunk A+B)
User chốt drastic refactor: bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking. Workflow flat list (Phòng × Cấp × Approvers). Pin WorkflowDefinitionId. Per memory feedback_drastic_refactor_scope.md: dedicated session + conservative buffer.
-
Chunk A (
dbb0089) All BE — Domain enum simplify (ChoDuyet=10, legacy 2-6+98 deprecated giữ data cũ) + WorkflowStep +DepartmentId/PositionLevel + PE/Contract +CurrentWorkflowStepIndex/RejectedAtStepIndex + drop InnerStep entity/nav (PE+Contract) + drop *DeptApproval.InnerStepId + EF Configurations restore simple unique non-filtered + DbContext drop DbSets. Migration 21RefactorWorkflowToFlatModelGỘP (4 ALTER cols PE/Contract + 2 ALTER WorkflowStep + DROP TABLE × 2 + DROP COLUMN InnerStepId × 2 + restore simple unique × 2). Service rewrite TransitionAsync flat logic (Drafter trình → init idx=0, advance idx per approve, last step → DaDuyet/DaPhatHanh, Trả lại save RejectedAtStepIndex, Resume jump-back, Match approver Dept+PositionLevel OR Approvers Role/User). App CQRS DTOs simplified. Tests DROP PeNStageApprovalTests + ContractNStageApprovalTests + PeTwoStageApprovalTests (19 test legacy). UPDATE PeWorkflowAdminTests signature. 96 → 77 test pass. 3-file rule Mig 21 commit đủ. -
Chunk B (
88a5be1) FE Designer — PeWorkflowsPage + WorkflowsPage rewrite (~210 LOC each): drop InnerStep types + PHASE_OPTIONS, auto-assign ChoDuyet=10, step UI Tên + Phòng Select + Cấp Select + SLA + Approvers Role/User optional fallback, drop InnerSteps sub-section, DefinitionCard view badge Phòng/Cấp. types/purchaseEvaluation.ts (fe-admin + fe-user mirror) + ChoDuyet=10 enum + label "Đang duyệt" + color amber. KHÔNG đụng PeWorkflowPanel (Chunk C SKIP — existing UI compatible). -
[⊘] Chunk C (FE PeWorkflowPanel + workflow timeline) SKIP — existing UI dùng
workflow.nextPhasesBE-driven, 3-button Trả lại/Từ chối Session 14 reuse với target=DangSoanThao/TuChoi pattern. KHÔNG cần đụng.
Defer Session 17+:
- UAT live test workflow flat (3 phòng × N cấp setup)
- Old PE/HĐ data migration (pinned legacy workflow phase 2-6 stuck) — admin manual transition hoặc data migration script
- Sample data seed N-stage (block DesignTime vs Runtime DB)
- Budget N-stage (cần versioned WF migration)
- schema-diagram §17-21 update (cron audit 2026-06-01)
- Skill ef-core-migration + contract-workflow refresh (cron audit)
- Tests cho flat workflow flow (làm khi UAT bug)
✅ Session 15 done (2026-05-07) — Tooltip diagnose "Lưu & Gửi Duyệt" + drastic refactor flat workflow DEFER (1 commit)
User UAT live báo button "Lưu & Gửi Duyệt" KHÔNG hoạt động + suy đoán "trùng ID". Diagnose: silent disabled khi nextPhases không có forward phase. Add tooltip + dialog warning. "Trùng ID" KHÔNG phải bug FE.
- Tooltip diagnose (commit
835cc7f) — fe-admin + fe-user PeDetailTabs: computeforwardPhaseonce +submitDisabledReasonreason string + button title attribute hover + dialog confirm show forward phase label explicit. Build pass × 2. KHÔNG đụng BE. - [⏸] Drastic refactor flat workflow — User chốt "bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking". Attempt edit working tree 12 file Domain/Configurations/DbContext, realize scope realistic ~8-10h (PolicyRegistry + Service + App CQRS + 12 tests + FE Designer + Migration 21 + Docs) vượt session. REVERT working tree về
835cc7fclean. Defer Session 16+ dedicated session.
Decision memorized: add memory feedback_drastic_refactor_scope.md — drastic refactor cần dedicated session với context fresh, scope estimation conservative (2x buffer), tránh mid-session big refactor (risk session context deep + breaking states giữa chunk).
Defer Session 16+:
- Drastic refactor flat workflow (Mig 21, ~8-10h, dedicated session) — flat WorkflowStep với DepartmentId+PositionLevel, drop InnerStep, PE/Contract CurrentWorkflowStepIndex tracking, Phase enum simplify ChoDuyet=10, Service rewrite, Designer FE rewrite.
- Hoặc fallback Approach Y — FE Designer flat UI giới hạn 5 phòng (auto-assign Phase behind scenes), ROI 1-2h, KHÔNG drastic.
- Task 2 sample data seed N-stage (block trên DesignTime vs Runtime DB)
✅ Session 14 done (2026-05-07) — PE 3-button workflow Duyệt/Trả lại/Từ chối (1 commit)
User chỉ thị thay 2-button approval (Duyệt + Reject mơ hồ) bằng 3 hành động rõ:
-
Duyệt = forward
-
Trả lại = về DangSoanThao + Drafter sửa (smart reject Mig 16 + clear N-stage rows + Drafter resume jump-back)
-
Từ chối = Phase=TuChoi, phiếu khoá vĩnh viễn (17 handler Mig 16 lock edit)
-
Domain
PurchaseEvaluationPolicy.cs— NccOnly + NccWithPlan thêm (X → TuChoi) transition cho mọi phase trung gian + FromDefinition expand step (trừ DangSoanThao) thêm (step.Phase → TuChoi). -
Service
PurchaseEvaluationWorkflowService.TransitionAsync— Reject branch tách 2 case: target=TuChoi giữ nguyên (no override / no RejectedFromPhase / no N-stage clear). target khác (DangSoanThao) → smart reject pattern. -
FE PeWorkflowPanel (admin + user mirror) — render 3 button "✓ Duyệt → X" / "← Trả lại" / "✗ Hủy / Từ chối" + decision logic + dialog confirm với warning red (Cancel) / amber hint (SendBack).
-
Tests — rename Reject test target TuChoi→DangSoanThao + NEW Reject_To_TuChoi_Locks_Permanently + update NStage_Reject_Clears target. 95 → 96 test pass.
Defer Session 15+:
- Task 2 sample seed N-stage (block trên DesignTime vs Runtime DB gotcha + API exit sớm khi DbInitializer seeding).
- Phase TraLai = 98 enum orphan — có thể remove migration nếu cleanup, ko gây hại.
✅ Session 13 done (2026-05-07) — Mirror N-stage Contract (Mig 20, 5 commit per-chunk + skip Chunk E auto-bind)
User chỉ thị mirror N-stage từ PE sang Contract. Budget defer (cần migration AddBudgetVersionedWorkflow trước — hardcoded BudgetPolicy.Default chưa có WorkflowDefinition entity). Pattern reusable đầy đủ từ PE.
- Chunk A (
951ffa3) Domain entityWorkflowStepInnerStep(Domain/Contracts/) + nav WorkflowStep.InnerSteps + ALTER ContractDeptApproval.InnerStepId + EF config FK Cascade Step / Restrict Dept+InnerStep + Migration 20AddContractWorkflowInnerStepsAndAlterDeptApprovalUniqueGỘP 1 (CREATE TABLE WorkflowStepInnerSteps + ALTER InnerStepId + DropIndex old + Recreate filtered legacyWHERE InnerStepId IS NULL+ new filtered N-stageWHERE InnerStepId IS NOT NULL). - Chunk B (
04cf2a0) Application CQRS DTO mirror PE Chunk B —WorkflowStepInnerStepDto+ extendWorkflowStepDto+CreateWorkflowStepInnerStepInput+ extendCreateWorkflowStepInput(default null backward compat) + Validator child rules + Handler atomic batch insert. - Chunk C (
e247b67) ContractWorkflowService refactor mirror PE — load InnerSteps eager + reject branch clear N-stage rows + dept block split hasInnerSteps→N-stage logic / else→legacy 2-stage. N-stage flow giống PE: match firstPending Order asc + (exact level OR canBypass + level≥), exact upsert / bypass batch upsert, recheck stillPending → BLOCK. - Chunk D (
7c0772a) ContractNStageApprovalTests 6 test mirror PE pattern + helper SeedWorkflowDefinitionAsync 2 step adjacent (DangGopY + DangDamPhan) + SeedContractAsync với Project + Supplier + FakeChangelogService + FakeContractCodeGenerator stubs. Bug fix legacy fallback test: switched phase pair sang DangKiemTraCCM → DangTrinhKy + role CostControl khớp Standard.Transitions. 89→95 test pass. - Chunk E SKIP — WorkflowsController auto-bind
[FromBody] CreateWorkflowDefinitionCommandrecord qua JSON, no code change cần. - Chunk F (current) FE WorkflowsPage Designer extend mirror PeWorkflowsPage Chunk F: InnerStepDto + EditInnerStep types + copyFromDefinition include + departmentsList query + sub-section "Cấp duyệt nhỏ trong phòng" drag-list + button "+ Thêm cấp duyệt" emerald + payload include Order asc. Empty state hint fallback 2-cấp legacy. KHÔNG đụng fe-user (admin-only).
Backward compat 100%: workflow Contract no InnerSteps configured → service fallback legacy 2-stage Mig 16. Data legacy InnerStepId=null vẫn enforce unique cũ qua filtered index.
Defer Session 14+:
- Budget N-stage — cần migration
AddBudgetVersionedWorkflowtrước (4 bảng + ALTER Budget.WorkflowDefinitionId), sau đó migrationAddBudgetWorkflowInnerSteps. User quyết riêng (feature mở rộng module lớn). - schema-diagram.md §17 Mig 20 update — defer cron audit 2026-06-01.
- Skill ef-core-migration row Mig 20 — defer cron audit.
✅ Session 12 done (2026-05-07) — N-stage workflow approval (Mig 18+19, 6 commit per-chunk, PE-only)
User yêu cầu: workflow level cha (= phase) cấu hình được level con (Phòng × Cấp NV/PP/TP) sequential, mỗi cấp = 1 inner step duyệt riêng, có bypass cùng dept. 6 spec defaults chốt + 6 chunk per-commit.
- Chunk A (
13ab533) Domain + Migration 18AddPeWorkflowInnerStepsAndPositionLevel— enum PositionLevel (NV/PP/TP), entity PurchaseEvaluationWorkflowStepInnerStep + nav, User.PositionLevel int? + PEDeptApproval.InnerStepId Guid?. EF config FK Cascade Step / Restrict Dept+InnerStep. 3-file rule. - Chunk B (
0e56bd0) Application CQRS DTO — PeWorkflowStepInnerStepDto + extend PeWorkflowStepDto + CreatePeWorkflowStepInnerStepInput (default null backward compat) + Validator child rules + Handler atomic batch insert + UserDto +PositionLevel + SetUserPositionLevelCommand mirror SetBypassReview pattern. - Chunk C (
0c62e24) Service N-stage logic + Migration 19AlterPeDeptApprovalsUniqueFilteredForInnerSteps(filtered unique legacyWHERE InnerStepId IS NULL+ new N-stageWHERE InnerStepId IS NOT NULL). PurchaseEvaluationWorkflowService refactor — load InnerSteps eager, reject clear N-stage rows tại fromPhase, dept block split hasInnerSteps→N-stage / else→legacy 2-stage. N-stage: match firstPending (Order asc IsRequired) same dept + (exact level OR canBypass + level≥), exact upsert 1 row InnerStepId, bypass batch upsert NV+PP+TP cùng dept ≤ actor (audit IsBypassed cho cấp dưới), recheck stillPending → BLOCK + log "duyệt cấp X (còn Y pending)". - Chunk D (
3d76c6b) Tests N-stage 6 test mới (FirstInner_NV_blocks / All_3_levels_sequential_pass / TP_bypass_skips_lower / Wrong_dept_403 / Reject_clears_rows / Legacy_fallback_no_inner) + IdentityFixture extend+positionLevel+ helper SeedWorkflowDefinitionAsync 2 step adjacent. 83→89 test pass. - Chunk E (
83ffabd) APIPATCH /users/{id}/position-levelmirror SetBypassReview + body{positionLevel:int?}+ Authorize Users.Update. - Chunk F (current) FE-Admin types/users.ts + positionLevel field + PositionLevel const + Label/Short maps. PeWorkflowsPage Designer extend InnerStep DTO + EditInnerStep type + sub-section "Cấp duyệt nhỏ trong phòng" drag-list { Phòng × Cấp + required } + button "+ Thêm cấp duyệt" emerald + departmentsList query + payload include. UsersPage column "Cấp" badge NV/PP/TP emerald + action button cycle null→1→2→3→null. KHÔNG đụng fe-user (admin-only).
Backward compat 100%: workflow no InnerSteps configured → service fallback legacy 2-stage Mig 16. Data legacy rows InnerStepId=null vẫn enforce unique cũ qua filtered index.
Defer Session 13+:
- Mirror Contract + Budget N-stage (sau khi UAT PE 2-3 tuần ổn). Pattern lặp lại Domain entity + Service logic + Tests reusable từ PE.
- Seed/migrate User.PositionLevel cho 30 demo user (hiện chỉ admin set qua UsersPage cycle).
- schema-diagram.md §15 Mig 18 + §16 Mig 19 update (defer cron audit 2026-06-01 — small drift).
- Skill ef-core-migration row Mig 18+19 (defer cron audit).
- Skill contract-workflow N-stage cross-ref section (defer cron audit).
✅ Session phase 2 done (2026-05-08 00:30) — B12-B14 PE detail polish iterate (3 commit FE-only)
User UAT iteration tiếp sau wrap-up 6e7a6db. Áp rule strict verify khi rename/remove (lesson hotfix CI).
- B12 (
378c993) — "Lưu" no-close + "Xóa phiếu" red bottom (CHỈ Bản nháp soft-delete) + bỏ header bar workspace "Sửa header"/"Xóa"/"Đóng" + Section 4 column headers.supplierName+ Section 3 chặn xóa NCC khi có quotes - B13 (
e320027) — InfoTabuseEffectre-trigger edit khi pencil click phiếu khác + sync values + Pencil "sáng lên" active state (bg-brand-100 + ring) khieditingRowId === p.id+ wireeditingRowIdtừ Workspace → PeListPanel - B14 (
d2306b8) — QuoteDialog bỏ checkboxisSelected(consolidate winner ở Section 2.a) + winner column Section 4 LUÔN highlight emerald (header✓prefix + cells full column) + QuoteDialog full overlay loading + spinner + NccSelectorRow inline spinner "Đang chọn NCC + sync cột giá Section 4…"
Verify: npm run build × 2 app pass · dotnet test 83 pass · push OK.
✅ Session S10-11+++++++ done (2026-05-07) — PE Workspace UX overhaul đầy đủ (23 commit)
User UAT live mode iterate liên tục. Áp rule feedback_uat_skip_verify (memory): skip dotnet test + npm build sau mỗi chunk, push ngay. Lesson hotfix CI 0ae3fe2: rename/remove → BẮT BUỘC npm run build 1 lần trước commit.
11 batch deliverable (chi tiết narrative đầy đủ xem STATUS.md Recently Done row đầu tiên + session log 2026-05-07-2359-pe-workspace-ux-overhaul.md):
- B1 (S10) PE Thao tác 2-panel workspace mirror HĐ Thầu phụ pattern (4 commit
ee0d360→d04bd88) - B2 (S11) Migration 17
AddManualBudgetFieldsToPeAndContract— 4 cột manual budget cho PE + HĐ + App CQRS + FE (5 commitecd5f7e→bf17740) - B3 (S11+) BudgetFieldRow inline editor Section 2.b — toggle + Select OR 2 input + auto-detect mode (3 commit
19712d8→7f38c02) - B4 (S11++) InfoTab inline edit Section 1 + PeListPanel pencil hover + URL
?editHeader=1(3 commit5a89dd2→cb0598d) - B5 (S11+++) Workspace "new" sectioned create view 5 sections + LockedHint S3-5 (1 commit
66fa469) - B6 (S11++++) Pe_*_List Danh sách disable toàn bộ tương tác (2 commit
7dfeb1a+a1665ee) - B7 (S11+++++) Workspace "new" lock Loại quy trình theo URL + Select preset Điều khoản TT (1 commit
18ebfa1) - B8 (S11++++++) PE Display status meta — Bản nháp / Đã gửi duyệt / Đã duyệt / Từ chối (1 commit
0c5db13) - B9 (S11+++++++) Phase TraLai = 98 + pencil always visible + edit gating + editableOnly filter (1 commit
d15398f) - B10 (hotfix CI) TS errors
forcedPhaserename + unusedPurchaseEvaluationTypeimport (1 commit0ae3fe2) - B11 (last) PE detail polish — NCC selector dropdown + Section 3 winner protect + Bottom action bar Lưu/Gửi Duyệt (1 commit
4c0625c)
Defer cho Session 12+ (cần explicit UAT trigger):
- Workflow transition vào TraLai — BE workflow service chưa wire button "Trả lại" cho approver. FE đã ready accept.
- BE multi-phase filter param
?phases=2,3,4,5,6— cho FE display status "Đã gửi duyệt" precision filter. - Section 5 Opinion sign trong Duyệt mode — verify nếu UAT user cần sign opinions ở leaf "Duyệt" (hiện code path opinionsReadOnly = readOnly khi mode='detail').
✅ Session 11 done (2026-05-07) — Migration 17 manual budget fields PE + HĐ
(Note: Session 11 + 11+ + 11++ + ... đã merge vào batch S10-11+++++++ ở trên — đây là sub-row chronological, KHÔNG cắt narrative cũ.)
✅ Session 9 done (2026-05-04) — Chunk E-bis complete (FE 2-stage + HĐ/Budget mirror + 6 test)
User chỉ thị "làm hết cho xong tính năng luôn" sau Session 8 close bug fix anh Kiệt phía BE PE. Session 9 đóng toàn bộ pending Chunk E-bis (defer từ session 8).
- Chunk E2 — FE Workflow Panel PE hiển thị progress 2-stage timeline per phase × dept (commit
f8eebd5). ComponentDeptApprovalsSectiongroup by phase × dept, highlight amber khi current phase có Review nhưng chưa Confirm, badge fuchsia "bypass" khi NV.CanBypassReview. Cả fe-admin + fe-user (rule §3.9). - Chunk E3 — FE UserManager toggle CanBypassReview (commit
4380bdc). UserDto BE thêm fieldCanBypassReview. UsersPage column "Bypass" + button ShieldCheck (icon highlight fuchsia khi enabled). Endpoint backend đã có sẵn từ Session 8 Chunk E1. fe-user KHÔNG có UsersPage (admin-only). - Chunk E4 — HĐ 2-stage logic mở rộng (commit
b6f5a16). ContractWorkflowService thêmUserManager<User>DI + mirror toàn bộ logic 2-stage từ PE service (sau policy guard, trước gen mã HĐ). ContractDepartmentApprovalFeatures.cs (List query mirror PE pattern). EndpointGET /contracts/{id}/department-approvals. FE WorkflowHistoryPanel section "Tiến trình duyệt 2-cấp phòng ban" insert giữa WorkflowSummaryCard và Lịch sử duyệt. - Chunk E5 — Budget 2-stage logic mở rộng (commit
1fc439b). TransitionBudgetCommandHandler thêmINotificationService+IDateTimeDI + mirror 2-stage logic. BudgetDepartmentApprovalFeatures.cs + endpoint. FE BudgetWorkflowPanel section "Tiến trình duyệt 2-cấp phòng ban". Note: low-priority cho Budget (ít user duyệt budget per dept) nhưng giữ consistent UX 3 module. - Chunk E6 — 6 test 2-stage + IdentityFixture helper (commit
8353fe8). IdentityFixture (Common/) setup ServiceProvider với Identity stack đầy đủ — DbContext SQLite shared connection + AddIdentityCore + AddRoles + AddEntityFrameworkStores. Single shared scope cho fixture lifetime đảm bảo DbContext + UserManager đồng instance. HelperCreateUserAsync(email, name, deptId, roles, canBypassReview)reusable. 6 test PeTwoStageApprovalTests: NV_Review_Blocks_Phase_Transition (đóng bug anh Kiệt — test chính xác) / TPB_Confirm_After_NV_Review_Allows_Transition / NV_With_BypassReview_Allows_Transition_With_IsBypassed_True / Admin_Skips_TwoStage_Logic_Entirely / Reject_Sets_RejectedFromPhase_And_Forces_DangSoanThao / Resume_After_Reject_Jumps_Back_To_RejectedPhase. Tests Contract + Budget 2-stage skipped — logic identical PE, ROI thấp; pattern reusable nếu UAT phát hiện regression riêng. Total 77→83 test pass.
Stats sau session 9:
- BE LOC: ~13750 → ~14400 (+650, gồm Contract + Budget 2-stage logic + 2 List feature + Test fixture)
- API endpoints: ~131 → ~133 (+2 List dept-approvals HĐ/Budget)
- Tests: 77 → 83 (+6 PE 2-stage)
- Schema không đổi (Migration 16 đã có sẵn từ session 8)
- 5 commit per-chunk pushed → 1 CI run trigger qua HEAD =
8353fe8
Pending còn lại Phase 9: chỉ Hard blockers Ops (UAT thật / SMTP / Rotate creds / SQL backup). Feature 2-stage đầy đủ.
✅ Session 8 done (2026-05-04) — Migration 16: 2-stage dept approval + smart reject + lock edit
Bối cảnh: Anh Kiệt (FDC) báo bug PE workflow: NV.PRO tạo phiếu → duyệt được hết phase. Phân quyền sai vì policy chỉ check role, không check Stage 2-cấp.
3 ràng buộc gộp 1 migration:
-
Lock edit khi Phase != DangSoanThao — 17 handler thêm guard (Contract Detail × 15 qua helper
EnsureContractType, PE Detail × 5 qua helper mớiPurchaseEvaluationDraftGuard, Budget Detail × 3 inline). KHÔNG lock Comment + Attachment + Opinion (workflow design intent). -
Smart reject + Resume —
Decision=Reject→entity.RejectedFromPhase = currentPhase+ forcetargetPhase=DangSoanThao. Resume:Drafter trình từ DangSoanThao + RejectedFromPhase != null → jump tới phase đã reject + clear field. Bypass policy guard ở resume. -
2-stage dept approval (PE only v1) — User.DepartmentId != null + role guard:
- DeptManager (TPB) → Stage=Confirm trực tiếp
- User.CanBypassReview=true → Stage=Confirm + IsBypassed=true
- Else (NV) → Stage=Review only, BLOCK transition cho đến khi TPB confirm
- Schema: 3 bảng
*DepartmentApprovalsUNIQUE (TargetId, Phase, Dept, Stage)
-
Migration 16
AddTwoStageDeptApprovalAndSmartReject— 4 ALTER + 3 CREATE TABLE + 12 indexes + FK Cascade -
Endpoint mới:
GET /api/purchase-evaluations/{id}/department-approvals(List),PATCH /api/users/{id}/bypass-review(toggle) -
Notify TPB cùng dept khi NV review (best effort, fail non-critical)
-
Verify: Build pass + 77 test pass + Migration applied LocalDB OK + schema verified qua sqlcmd
-
5 commit per-chunk:
5fe61cc(A) ·14f3c9f(B) ·9747f8c(C) ·a532ba6(D) · current (E1)
Session log: 2026-05-04-1230-chot-session-8-2-stage-dept-approval.md.
Pending Chunk E-bis ✅ TẤT CẢ DONE ở Session 9 (xem section trên):
- FE Workflow Panel hiển thị progress 2-stage timeline (E2 — PE) + HĐ (E4) + Budget (E5)
- FE UserManager toggle
CanBypassReviewcheckbox (E3) - HĐ 2-stage mở rộng (E4)
- Budget 2-stage mở rộng (E5)
- Tests 2-stage logic Service-layer (E6 — 6 test PE + IdentityFixture)
✅ Session 6 done (2026-04-30 — pure docs work)
- MD audit + compact — STATUS -27%, HANDOFF -32%, migration-todos -35%, archive 51 row Phase 0-7 cũ
- 3 skill refresh —
form-enginePhase 2 MVP → Tier 3 feature-complete,permission-matrix12 menu → ~60 + inheritance roots,ef-core-migration24 DbSet → 52 bảng - Rule mới rules.md §7 — Khi nào viết test (timing rule 5-row table)
- Rule mới rules.md §6.4 — Audit + compact MD định kỳ (cadence + checklist + anti-pattern)
- rules.md §9.4 — Mở rộng skill audit cross-ref §6.4
✅ Session 9+ housekeeping done (2026-05-04 — sau Session 9 close)
- Audit định kỳ 2026-05 — combined skill + doc drift (commit
7dc0233). Logskill-audit-2026-05.md. - Optional polish — fe-user Inbox PE section (commit
332a90f). HĐ + PE 2 section trong InboxPage. - User Manual 7 file rewrite compact (commit
16c2c9c). End-user style: bỏ field/error tables, giữ numbered steps đơn giản. ~86 KB total.
✅ Session 11 done (2026-05-07) — Migration 17 manual budget fields PE + HĐ
User feedback: PE/HĐ link Budget Select chỉ Phase=DaDuyet → user phải break flow tạo Budget approved trước. Solution: toggle "Nhập tay" + 2 input field fallback (Tên text + Số tiền number) lưu trên entity, KHÔNG cần Budget entity. Mirror logic PE ↔ HĐ (Q3 chốt).
- Chunk 1 Domain+Infra (commit
ecd5f7e) — Migration 17AddManualBudgetFieldsToPeAndContract4 ALTER + 2 entity property + 2 EF config (HasMaxLength + HasPrecision). Applied LocalDB. 3-file rule. - Chunk 2 App CQRS (commit
0f7901c) — Create/Update PE + Contract commands + Validator (>=0 when has value) + Handlers + DTO + diff log audit + CreateContractFromEvaluation carry forward. - Chunk 3 FE-Admin (commit
bab5031) — types +2 field, PeHeaderForm toggle + 2 input + payload conditional, PeDetailTabs Section "b. Ngân sách" fallback display + badge "nhập tay", refactor PurchaseEvaluationCreatePage wrap PeHeaderForm DRY (222→30 LOC), ContractCreatePage NewForm + EditForm cùng pattern + read-only branch khi !isDraft. - Chunk 4 FE-User mirror (commit
14f8d9d) — 6 file y hệt content (rule §3.9). - Chunk 5 Docs (commit current) — STATUS row + HANDOFF TL;DR Session 11 + session log
2026-05-07-2300-pe-hd-manual-budget-mig17.md. - Verify: dotnet build + 83 test pass mỗi chunk · npm build fe-admin + fe-user pass · LocalDB migration applied.
- KHÔNG đụng Budget entity / Phase=DaDuyet validation (giữ invariant). Manual fields chỉ là fallback display/note, KHÔNG join với Budget.Details cho per-row comparison ở PE matrix Section 4.
✅ Session 10 done (2026-05-07) — PE "Thao tác" 2-panel workspace
User chỉ thị restructure menu PE: leaf "Thao tác" (Pe_*_Create) từ page Create header riêng /new sang workspace 2-panel mirror pattern HĐ Thầu phụ ContractCreatePage. Spec chốt 5 câu trước code (xem session log đầy đủ rationale).
- Chunk 1 fe-admin (commit
ee0d360) —PeListPanel.tsx(~180 LOC pure picker reuse + sticky "+ Thêm mới") +PeHeaderForm.tsx(~210 LOC extract) +PurchaseEvaluationWorkspacePage.tsx(~120 LOC 2-panel). PeDetailTabs thêm propmode?: 'detail' \| 'workspace'+ Section 5 hint amber + force opinionsReadOnly. Layout resolver remap. 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). - Chunk 3 docs (commit
7e3cfa5) — STATUS Recently Done + HANDOFF TL;DR Session 10 + session log2026-05-07-2100-pe-workspace-2panel.md. KHÔNG update skill (per §9.5 — không drift đáng audit, FE pure refactor). - Verify: 2 build (fe-admin + fe-user) pass + dotnet test 83 pass mỗi chunk. Route
/newcũ giữ tồn tại cho deep-link "Sửa header" button. - KHÔNG đụng BE / migration / schema / endpoint / test count.
A. Hard blockers (chờ user / ops)
- UAT thật 1 tuần với 2-3 user (30 demo: 16 sample + 14 Solutions thật)
- SMTP config → Email outbox (BLOCKED chờ user cấp host/user/pass)
- Rotate credentials (admin + 30 demo + SA + vrapp + JWT secret + Gitea runner token)
- Schedule SQL backup daily Task Scheduler
B. PE feature gap còn lại
- Export phiếu PDF/Excel —
IDocumentConverter+ templatePE-TrinhDuyet.docx(user pending — không quan trọng lắm)
C. Optional polish (làm khi UAT phát sinh)
- Budget MaNganSach atomic sequence + migration
AddBudgetCodeSequences - Budget versioned workflow + migration
AddBudgetVersionedWorkflow - Payment terms PE tách field (JSON → 6 column)
- Auto-map PE Details → Contract Details khi gen HĐ
- Matrix Quotes bulk paste từ Excel
- fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi" — done 2026-05-04 (commit
332a90f). useQuery thứ 2 cho/pe/inbox+ Panel 1 chia 2 section sticky header. Click PE → navigate/purchase-evaluations/:id(page riêng).
D. Tests Phase 3-5 (làm khi gặp bug recurring để justify ROI)
- Phase 3 — Application handler tests (CQRS + EF InMemory) ~15 test
- Phase 4 — API smoke tests (WebApplicationFactory) ~7 test
- Phase 5 — FE Vitest cho lib utility (queryMatches, fmtMoney) ~10 test
E. Ops chưa xong
- Remove binding cũ
.huypham.vnsau verify stable - win-acme scheduled task fix unhealthy (cert expire 2026-06-18)
🔁 Skill governance (recurring)
Quy tắc: docs/rules.md §9. Audit định kỳ mỗi đầu tháng — workflow §9.4.
- Setup ban đầu — 6 skill (3 domain + 3 ops), rules §9 ←
661f859 - Audit 2026-05-01 — log
docs/changelog/skill-audit-2026-05.md - Audit 2026-06-01
- Audit 2026-07-01
Cron task solution-erp-skill-audit-monthly fire 9:00 AM ngày 1 mỗi tháng.
📦 Post-launch (Phase 10+ — future)
- Email outbox (MailKit + SMTP) — blocked chờ SMTP config
- E-signature integration (VNPT CA hoặc FPT CA)
- Tích hợp Bravo / SAP ERP import NCC
- Mobile app (React Native?) cho BOD duyệt ngoài giờ
- AI: gợi ý điền form dựa HĐ cũ, OCR scan HĐ đối tác
- Multi-tenant nếu có công ty thứ 2