# STATUS — Snapshot hiện tại > **Update rule:** trước khi bắt đầu 1 task → ghi row vào `🔥 In Progress`. Xong → chuyển sang `✅ Recently Done`. **Last updated:** 2026-04-29 chiều (Phase 8 — **Budget FE complete + PE feature gap đóng + Tests Phase 1-2 + CI gate + Path filter docs-only skip**) ## 📍 Phase hiện tại: **Budget feature-complete + PE feature gap đóng + Test gate live** — 52 DB tables (+1 PEDepartmentOpinions), 15 migrations (+`AddPurchaseEvaluationDepartmentOpinions`), ~128 API endpoints (+4 PE WF admin + Opinion). 31 FE pages (+5 Budget + 1 PeWorkflowsPage). **71 unit test pass** (Phase 1: 54 Domain policy + Phase 2: 17 Infra code generator) — CI fail-fast nếu test fail. 30 demo user (16 sample + 14 Solutions thật). ### 🌐 Production URLs - https://api.solutions.com.vn — API (Let's Encrypt, auto-renew via win-acme) - https://admin.solutions.com.vn — Admin FE (HTTP→HTTPS auto-redirect) - https://eoffice.solutions.com.vn — User FE (HTTP→HTTPS auto-redirect) - https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp — Gitea repo + Actions - Default admin: `admin@solutionerp.local` / `Admin@123456` ⚠️ **RE-ROTATE sau login đầu** ## 🔥 In Progress — Còn lại cho session 6 ### A. PE feature gap (carry over) - [ ] **Export phiếu PDF/Excel** — tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx` (user pending — không quan trọng lắm) ### B. Optional polish (khi UAT phát sinh) - [ ] Budget MaNganSach atomic sequence (hiện `NS-YYYYMM-XXXX` Random.Shared, chốt format chính thức sau → migration `AddBudgetCodeSequences`) - [ ] Budget versioned workflow (admin config qua UI — hiện hardcoded `BudgetPolicy.Default`) - [ ] Payment terms tách field (PE) — JSON blob → 6 column riêng (migration `AddPePaymentTermFields`) - [ ] Auto-map PE Details → Contract per-type Details khi gen HĐ - [ ] Matrix Quotes bulk paste từ Excel - [ ] fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi" ### C. Tests Phase 3-5 (làm khi gặp bug recurring để justify ROI) - [ ] **Phase 3** — Application handler tests (CQRS) với EF InMemory (~1 ngày, ~15 test) - [ ] **Phase 4** — API smoke tests qua WebApplicationFactory (~0.5 ngày, ~7 test) - [ ] **Phase 5** — FE Vitest cho lib utility (queryMatches, fmtMoney) (~0.5 ngày, ~10 test) ### D. Deploy / Ops chưa xong - [ ] **Remove binding cũ `.huypham.vn`** sau verify: `ssh vietreport-vps ; cd C:\solution-erp\scripts ; .\migrate-domains.ps1 -RemoveOld -SkipCert` - [ ] **win-acme scheduled task "unhealthy"** — auto-renew fix trước 2026-06-18. - [ ] UAT thật 1 tuần với 2-3 user (16 demo + 14 Solutions = 30 user) - [ ] SMTP config → Email outbox - [ ] Rotate creds (admin + 30 demo + SA + vrapp + JWT) - [ ] Schedule SQL backup Task Scheduler ## ✅ Recently Done (newest on top) | Ngày | Ai | Task | Commit | |---|---|---|---| | 2026-04-29 | Claude | **Tests Phase 2 — Code generator format + sequence (SQLite in-memory)** — `tests/SolutionErp.Infrastructure.Tests/` xUnit + EF SQLite 10. `SqliteDbFixture` + `TestApplicationDbContext` subclass override `nvarchar(max) → TEXT` (SQLite không support `max`). 17 test: ContractCodeGenerator (format RG-001 5 type + Framework year scope vs Project scope + sequence per prefix + year boundary reset + persistence verify) + PurchaseEvaluationCodeGenerator (format A/B + 3-digit pad + independent A/B sequences + year boundary). CI gate +1 step. Total 71 test pass / 2.1s. | `df5988b` | | 2026-04-29 | Claude | **Tests Phase 1 — Domain unit tests + CI gate** — `tests/SolutionErp.Domain.Tests/` xUnit 2.9 + FluentAssertions 7.2 (pin trước v8 commercial). 54 test pure function (no DB/IO): WorkflowPolicy (Standard 9-phase + SkipCcm 7-phase + Registry per ContractType + FromDefinition versioned + UserKindApprover) / PEPolicy (NccOnly 3-step + NccWithPlan 5-step + reject paths) / BudgetPolicy (Default 3-step + terminals + SLA spec). `.gitea/workflows/deploy.yml` thêm step "Run unit tests" trước build, fail → `exit $LASTEXITCODE` → no deploy. SolutionErp.slnx + folder `/tests/`. | `d3f9346` | | 2026-04-29 | Claude | **PE Workflow designer admin UI + Ý kiến 4 phòng ban** — Migration 15 `AddPurchaseEvaluationDepartmentOpinions` (UNIQUE PEId+Kind, 1 row/phòng/phiếu). Domain `PurchaseEvaluationDepartmentOpinion` + enum `PeDepartmentKind` (PheDuyet/Ccm/MuaHang/SmPm). BE: `PeWorkflowAdminFeatures.cs` ~250 LOC mirror Contract pattern (GetOverview + Create version, deactivate cũ atomic) + `PeWorkflowsController` 2 endpoint reuse policy `Workflows.*`. `PeDepartmentOpinionFeatures.cs` Upsert (sign=true→set SignedAt+UserId, sign=false giữ chữ ký cũ) + Delete + 2 endpoint. FE: `PeWorkflowsPage.tsx` ~500 LOC + designer dialog (clone version + add/remove steps + +Role/+User approvers). Section "5. Ý kiến 4 phòng ban (sign-off)" 2x2 grid OpinionBox (read mode chữ ký vs edit textarea + 2 button Lưu/Lưu&Ký). | `5d94bb4` | | 2026-04-29 | Claude | **PE Detail UI restructure theo spec form PHIẾU TRÌNH KÝ** — 4 section đánh số match form chính thức: "1. Thông tin gói thầu" (a/b chỉ Tên + Dự án) / "2. Chọn NCC/TP" (a NCC chọn / b Ngân sách / c Giá chào thầu auto-compute từ winner quotes / d Bản so sánh embed GeneralAttachments) / "3. NCC/TP tham gia" / "4. Hạng mục + Báo giá". FormRow helper (label 176px + value flex) thay cho dl grid 2-col cũ. | `7e36241` | | 2026-04-29 | Claude | **PE/Contract → Budget integration + cột "So với ngân sách"** — BE: `BudgetSummaryDto` shared (PE & Contract DetailBundle), Create/Update PE+Contract commands + `BudgetId?` validate cùng Project + Phase=DaDuyet. `CreateContractFromEvaluation` carry forward pe.BudgetId → contract.BudgetId. FE: PE & Contract Create form + Select "Ngân sách" filter Phase=DaDuyet + Project match. PE InfoTab + Contract Edit display Budget link clickable. PE ItemsTab matrix + cột "NS link · Δ" — match per-row qua key `groupCode\|itemCode`, fetch /budgets/{id} riêng + footer aggregate (xanh dưới / đỏ vượt / xám khớp). | `61e5d4d` | | 2026-04-29 | Claude | **Budget FE 3-panel pages cả 2 app** — `types/budget.ts` (BudgetPhase 5-state enum + DTO) + `BudgetsListPage` 3-panel `[340px_1fr_360px]` + filter Phase + Năm + alias `?phase=Pending` + readOnly mode menu Duyệt + `BudgetCreatePage` form Header + `BudgetDetailTabs` flat (Section Thông tin Header + Section Hạng mục table CRUD inline auto-compute ThanhTien=KL×ĐG) + `BudgetWorkflowPanel` Panel 3 timeline + dialog comment + Approvals/Changelog. Mirror fe-user. App.tsx 3 route + Layout resolver `Bg_*`. TS build pass cả 2 app. | `df12fb1` | | 2026-04-28 | Claude | **Module Ngân sách (Budget) BE — 4 bảng + workflow simple 3-step** — Migration 14 `AddBudgets`. Domain: `Budget` (Header — MaNganSach `NS-YYYYMM-XXXX`, NamNganSach, ProjectId/DepartmentId, DrafterUserId, TongNganSach auto-sum, SlaDeadline) + `BudgetDetail` (flat row pattern: GroupCode/Name + ItemCode + NoiDung + DonViTinh + KhoiLuong/DonGia/ThanhTien) + `BudgetApproval` (workflow history reuse `ApprovalDecision`) + `BudgetChangelog` (`BudgetEntityType` Header/Detail/Workflow). Enum `BudgetPhase` 5 state (DangSoanThao→ChoCCM→ChoCEO→DaDuyet + TuChoi). `BudgetPolicy.Default` hardcoded 3-step (Drafter→CCM→CEO). Application: 11 CQRS handler (~340 LOC: Create/UpdateDraft/Transition/List/GetDetail/Delete + Detail CRUD auto-recompute TongNganSach + ListChangelogs). Api: `BudgetsController` 11 endpoint. Link: Contract.BudgetId? + PE.BudgetId? nullable FK + index. Menu seed `Budgets` root + 3 leaf (Bg_List/Create/Pending) order=27 icon Wallet. **FE chưa làm** (next session). | `a05c57b` | | 2026-04-28 | Claude | **14 demo users Solutions company thật** — PRO 5 (TPB tra.bui + 4 NV: phuong/binh/danh/dat) + CCM 7 (TPB ngocanh.huynh + 6 NV: ha.dao/cuong.do/long.le/ha.nguyen/dung.nguyen/anh.nguyen) + ISO 1 (chau.le) + CEO 1 (huy.duong). Pattern reconcile per-user try-catch (gotcha #38 4-field rename). Pwd `User@123456`. Tổng 30 user (16 sample cũ + 14 Solutions thật). Test login E2E pass. | `8097892` | | 2026-04-28 | Claude | **Docs cleanup + chốt session 3** — Tái cấu trúc MD: archive raw dump (forms-spec-raw, workflow-raw) → `_archive/`, compact `migration-todos.md` collapse Phase 0-5+Tier3 cũ, update `CLAUDE.md` modules table + skills 6 (3 domain + 3 ops), `flows/` thêm PE ref architecture, audit `gotchas.md` (38 pitfall — thêm #34 NavLink query string + #35 menu inheritance + #36 Vite env rebuild + #37 mutex accordion family + #38 Identity 4-field rename). Session log `2026-04-23-2200-roles-demo-pending-cleanup.md` + `2026-04-24-chot-session-3-pe-polish.md`. | `309dcd9` · `e71e0eb` · `e65578a` | | 2026-04-24 | Claude | **PE polish — Demo seed + MaPhieu atomic + Pe_* perm defaults** — Migration 13 `AddPurchaseEvaluationCodeSequences` (Prefix PK, mirror ContractCodeSequences). `IPurchaseEvaluationCodeGenerator` + impl SERIALIZABLE — format `PE/{YYYY}/{A\|B}/{Seq:D3}`. Replace Random.Shared trong CreatePEHandler. `SeedDemoPurchaseEvaluationsAsync` 4 phiếu varied (A-001 DangSoanThao, A-002 ChoCEODuyetNCC + 9 quotes, A-003 DaDuyet PaymentTerms JSON, B-001 ChoDuAn 5-step). `SeedPurchaseEvaluationPermissionDefaultsAsync` 7 role × 9 menu key — Drafter/DeptManager/Procurement Create+Update, các role duyệt R+U, DeptManager thêm Delete. | `c48ac21` | | 2026-04-24 | Claude | **Rebrand 3 domain huypham.vn → solutions.com.vn E2E** — 18 file repo (FE env + scripts + CI/CD + docs + skill + code comments). Viết `scripts/migrate-domains.ps1` (ASCII-only gotcha #30) chạy trên VPS: 3 HTTP binding mới + 3 cert Let's Encrypt (HTTP-01 via SelfHosting) + auto HTTPS 443 + redirect. CI/CD auto-deploy commit `66c1a5c+b93dacf`: appsettings.Production.json CORS mới + FE bundle `VITE_API_BASE_URL=api.solutions.com.vn`. E2E verified: `/api/purchase-evaluations` 401, CORS preflight OK, FE dist có domain mới. URL cũ fallback. Claude SSH tự chạy (không cần user RDP). | `66c1a5c` · `b93dacf` | | 2026-04-23 | Claude | **SeedDemoUsersAsync robust reconcile + 16 demo users** — Reconcile pattern (per-user try-catch, fix drift dept/position/role nếu user existing). 13 → 16 demo (+bod.tran NĐUQ thứ 2, +pm.le PM thứ 2, +nv.truong NV CCM). Detailed log created/fixed/failed counts. Resolves prod issue: chỉ thấy admin user vì old skip-if-exists fail silent. | `a667665` | | 2026-04-23 | Claude | **G-084 hardening** — localhost → 127.0.0.1 trong `deploy-iis.ps1` + skill `iis-deploy-runbook`. Thêm gotcha #33 (IPv4/IPv6 port hijack) ref VietReport incident + 3 rules (reverse-proxy IP literal / backend loopback IPv4 explicit / service dependency). SOLUTION_ERP risk thấp (API in-process IIS, no ARR proxy) nhưng chuẩn hóa cho tương lai. | `3990066` | | 2026-04-23 | Claude | **Kế thừa HĐ từ phiếu PE** — `CreateContractFromEvaluationCommand` guard DaDuyet + SelectedSupplier + ContractId=null → tạo Contract draft với SupplierId/ProjectId/GiaTri kế thừa. Link 2 chiều PE.ContractId. 2 endpoint mới (approved-pending-contract + create-contract). FE PeDetailTabs InfoTab banner emerald + CreateContractDialog pick ContractType 7 loại. | `a385d70` | | 2026-04-23 | Claude | **PE FE — 2 app pages (List/Create/Detail 3-panel)** — Types + PurchaseEvaluationsListPage 3-panel + PurchaseEvaluationCreatePage + PeDetailTabs (5 tab: Thông tin/NCC/Hạng mục+Quote matrix/Duyệt/Lịch sử) + PeWorkflowPanel timeline. Menu resolver Pe_* → /purchase-evaluations?type=N. fe-user mirror. TS build pass cả 2 app. | commit Phase 3 | | 2026-04-23 | Claude | **PE App+Api CQRS** — ~900 dòng: Create/UpdateDraft/Transition/List/Inbox/GetDetail bundle/Delete + Supplier CRUD + Detail CRUD + Quote Upsert + SelectWinner + Changelog list. PurchaseEvaluationWorkflowService policy-based guard + notification push. PurchaseEvaluationsController ~15 endpoint. | commit Phase 2 BE | | 2026-04-23 | Claude | **PurchaseEvaluation module — Domain+Infra (migration 12)** — 10 bảng mới (7 core: Header+Suppliers+Details+Quotes+Approvals+Changelogs+Attachments + 3 workflow config: Definitions/Steps/StepApprovers). 2 enum (PEType A/B, PEPhase 7 state + TuChoi). PurchaseEvaluationPolicy + Registry + FromDefinition. Seed 13 menu Pe_*/PeWf_* + 2 WorkflowDefinition v01 (QT-DN-A 3-step, QT-DN-B 5-step). | `2c6f0ca` | | 2026-04-23 | Claude | **Mở rộng master data + backfill demo HĐ** — SeedDemoMasterDataAsync per-Code idempotent (5→15 NCC + 3→8 Project). DemoSupplierByType/DemoProjectByType maps đa dạng theo loại HĐ. BackfillDemoContractsSupplierProjectAsync update [DEMO] HĐ supplier+project nếu mismatch. Match business: ThauPhu↔NTP, NCC↔NCC, DV↔DV. | `bcdc007` | | 2026-04-23 | Claude | **Edit detail row inline + deps audit script** — 7 typed UpdateXxxDetailCommand BE (ChangelogAction.Update log) + 7 PUT endpoints. FE ContractDetailsTab: ActionBtns (Pencil + Trash) + EditRowDialog reuse FIELDS_BY_TYPE config + buildPayload + populate form từ row data. Mirror fe-admin. `scripts/deps-audit.ps1` chạy dotnet+npm scan, color output, -FailOnHigh CI gate. | `e53cd3a` | | 2026-04-23 | Claude | **User-kind approver runtime guard + Warning 20% SLA** — WorkflowPolicy +UserTransitions parallel dict (default null cho Standard/SkipCcm, populated qua FromDefinition khi WorkflowStepApprover Kind=User). IsTransitionAllowed signature update accept actorUserId, fallback User-kind nếu Role không match. SlaExpiryJob.ProcessWarningsAsync mới — pull HĐ !SlaWarningSent && remaining ≤ 20% × default SLA → notify Drafter via NotificationType.SlaWarning + set flag tránh spam. | `4edcd58` | | 2026-04-23 | Claude | **SeedDemoContractsAsync — 7 demo HĐ varied phases** — Idempotent (skip nếu [DEMO] tồn tại). 7 HĐ covering 7 ContractType + final phases (DangSoanThao/DangGopY/DangInKy/DangTrinhKy/DaPhatHanh) + 14 Details rows + ~30 Approvals workflow history + 2 Comments demo. ApproverUserId mapping đúng role (CCM→ccm.tran, BOD→bod.huynh, HRA→hra.dang). Mã HĐ auto gen RG-001. | `8bc9565` | | 2026-04-23 | Claude | **RolesPage CRUD `/system/roles`** — BE: CreateRole regex Name validation, UpdateRole CHỈ ShortName/Description (Name FK không đổi), DeleteRole block AppRoles.All + count UserRoles > 0. 3 endpoint POST/PUT/DELETE Authorize Admin. FE: 5-column table + Loại badge (Mặc định/Tùy chỉnh) + Edit dialog Name disabled + Delete with HARDCODED_ROLES guard + Banner amber FK warning + Create dialog regex pattern HTML5. | `072ad6d` | | 2026-04-23 | Claude | **Roles VN labels (ShortName) + Users dept/position + 13 demo users** — Migration 11 `AddRoleShortNameAndUserDepartment`. Role thêm ShortName max 50 (QTV/BOD/CCM/PRO/FIN/...) + Description full VN. User thêm DepartmentId FK Restrict + Position. Idempotent backfill 12 roles VN labels. Seed 13 demo users (bod/pm/ccm/pro/fin/act/equ/hra TPB + 4 Drafter), pwd `User@123456`. FE UsersPage column Phòng ban + Chức vụ + edit dialog mới + roles checkbox "ShortName — Full". PermissionsPage Panel 1 2-line per role. | `330d529` + `ae59cfe` | | 2026-04-23 | Claude | **4 master catalogs cho Details + datalist autocomplete** — Migration 10 `AddMasterCatalogs`: UnitsOfMeasure (20 seed) + MaterialItems (15) + ServiceItems (10) + WorkItems (15). CQRS CRUD 13 endpoint, Admin role POST/PUT/DELETE. Menu mới `Catalogs` group + 4 leaves dưới Master. FE Admin CatalogsPage generic 4-tab CRUD `/master/catalogs/:kind`. FE Details Add form HTML5 `` per field + smart-fill (pick code → autofill name + defaultUnit). DB 32→36 bảng. | `e27c547` + `16e24ed` | | 2026-04-23 | Claude | **Mã HĐ gen ngay tại CreateContract + backfill HĐ legacy** — `CreateContractCommandHandler` call `codeGenerator.GenerateAsync` trước `db.Contracts.Add`. TransitionAsync giữ defensive `if MaHopDong null gen` cho legacy. `BackfillContractCodesAsync` trong DbInitializer chạy startup, idempotent (NULL count → skip nếu 0). Trade-off: gap sequence khi reject nhưng user có mã ngay đầu để reference giấy tờ. | `51449d6` | | 2026-04-23 | Claude | **"Thao tác" 2-panel + Edit/Xóa hover + Detail preview** — `ContractCreatePage` rewrite: Panel 1 List HĐ theo type + button "+ Thêm mới" cuối. Panel 2 ContractHeaderForm (new) hoặc ContractEditForm (edit) + Chi tiết section. URL state ?type/?id/?mode=new. Action buttons row Panel 1: Edit ✏ + Xóa 🗑 luôn hiện, mờ + disabled khi Phase != DangSoanThao. ContractDetailsPreview cho create mode show table headers + lock icon. | `8c4b4da` + `ec0c983` + `501b4de` + `7f26ff9` + `39031ca` | | 2026-04-23 | Claude | **Panel 2 layout: tabs → 7/3 grid (UX iter 2)** — Bỏ tabs Tổng quan/Chi tiết/Lịch sử. Render flat: Tổng quan (Info+Comments+Attachments) ở trên, dưới grid lg:grid-cols-10 (Chi tiết 7 cột + Lịch sử Changelog 3 cột). | `b3762af` (tabs) → `ad0652d` (grid) | | 2026-04-23 | Claude | **4-bảng data model overhaul (Header+Details+Workflow+Changelog)** — Migration 9 `AddContractDetailsAndChangelog`: 7 ContractType-specific Details bảng + 1 ContractChangelog unified audit log. IChangelogService 5 method, wired vào CreateContract+UpdateDraft+AddComment+Upload/Delete/Transition. CQRS 9 endpoint mới (GetBundle+7 Add+Delete+ListChangelogs). FE ContractDetailsTab + ContractChangelogsTab. DB 24→32 bảng. | `70810e1` + `71c035d` + `e684455` | | 2026-04-23 | Claude | **3-panel layout HĐ (List/Detail/Workflow) + Inbox + sidebar accordion + UserDashboard fix** — MyContractsPage + ContractsListPage + InboxPage 3-panel `lg:grid-cols-[320px_1fr_360px]`, `?id=` URL state, mobile fallback fullpage. Sidebar accordion qua AccordionContext (chỉ 1 Ct_ expand). Tách "Tổng quan" → /dashboard riêng (fix bug trùng /inbox), UserDashboardPage 5-card "Của tôi" + recent contracts. | `b75448e` + `89c7e88` + `7ea3957` + `d326e80` | | 2026-04-23 | Claude | **Skill governance + audit định kỳ** — `docs/rules.md §9` mới (6 skill bảng, nguyên tắc tạo project-specific, format SKILL.md bắt buộc, workflow audit 7 bước, 4 anti-patterns). Cron task `solution-erp-skill-audit-monthly` fire 9:00 AM ngày 1 mỗi tháng (next 2026-05-01) — self-contained prompt cold-start, auto-refresh stale nhỏ + đề xuất add/archive cho human approve, log vào `docs/changelog/skill-audit-{YYYY-MM}.md`, ABORT nếu repo dirty. Touch-points: CLAUDE.md callout + HANDOFF A1 + migration-todos checkbox + skill scope commit | `b904a25` | | 2026-04-23 | Claude | **3 skill ops project-specific** — Khảo sát alirezarezvani/claude-skills, quyết định KHÔNG bulk-clone (skill global đã cover phần generic, repo còn lại doc-dump không có when-to-use). Viết 3 skill mới encode SOLUTION_ERP-only: `dependency-audit-erp` (npm/dotnet CVE scan respect MediatR 12.4.1 + Swashbuckle 6.9.0 pin), `ef-core-migration` (8 migration history + 3-file rule + DesignTimeDbContextFactory + 6 pitfalls cụ thể), `iis-deploy-runbook` (3 IIS site + win-acme + NSSM gitea-runner + LibreOffice + debug playbook 500/502/SignalR). Total skill project-level = 6 (3 domain + 3 ops) | `661f859` | | 2026-04-22 | Claude | **PermissionsPage 3-panel layout** — Grid `lg:grid-cols-[280px_1fr_300px]`: Panel 1 Role list click-to-select (active ring-brand), Panel 2 Menu×CRUD matrix sticky thead + search + column bulk-toggle + brand-tinted hover, Panel 3 Granted progress bar + CRUD breakdown color badges (slate/emerald/amber/red) + Tip | `91b2da1` | | 2026-04-22 | Claude | **Admin Workflows tabs → sidebar menu items** — Seed 7 `Wf_` leaf dưới group `Workflows`. Layout resolvePath `Wf_` → `/system/workflows/`. WorkflowsPage bỏ tab bar, URL param drives type selection. Landing 7-card grid khi click top-level `Quy trình HĐ`. Inheritance: `Workflows.Read` perm → tất cả 7 leaves auto-visible. | `f216169` | | 2026-04-22 | Claude | **Versioned workflow per ContractType** — 3 entity mới: WorkflowDefinition (Code+Version+IsActive+ContractType), WorkflowStep (Order+Phase+Name+SlaDays), WorkflowStepApprover (Role/User + AssignmentValue). Contract.WorkflowDefinitionId nullable FK pin tại create. Migration `AddVersionedWorkflows`. Seed v01 per 7 ContractType. `WorkflowPolicyRegistry.FromDefinition()` build runtime policy từ DB. ContractWorkflowService load pinned definition. Admin `/system/workflows/:typeCode` Designer modal (create new version, clone, add/remove step, +Role/+User approvers). POST /api/workflows auto-increment Version + deactivate old. Invariant: HĐ cũ pin v01 giữ nguyên khi v02 active. E2E verified: QT-MB-v02 active, HĐ cũ vẫn chạy v01. | `e7e5f2d` + `355bbe3` | | 2026-04-21 | Claude | **Nested sidebar menu fe-user** — 7 ContractType × 3 actions (Danh sách/Thao tác/Duyệt), nested 3-level. Admin hide `Ct_*`. Layout recursive MenuNodeRenderer. MyContracts + Inbox filter `?type=X` | `5e0f380` + `48e91fe` | | 2026-04-21 | Claude | **Seed master data + MyDashboard widgets** — DbInitializer seed 9 departments (PM/QS/CCM/PRO/FIN/ACT/EQU/HRA/BOD) + 5 demo suppliers + 3 demo projects idempotent. MyDashboard endpoint role-aware: DraftsInProgress / PendingMyApproval / DueSoon / Overdue / DraftsTotalValue. FE "Của tôi" row 4 card hover-interactive, admin auto-hide nếu = 0 | `6197c84` | | 2026-04-21 | Claude | **Dynamic workflow policy per ContractType** — Domain WorkflowPolicy record + registry (Standard 8-phase cho Thầu phụ/Giao khoán/NCC; SkipCcm 7-phase cho Dịch vụ/Mua bán/Nguyên tắc). ContractWorkflowService dùng policy.ForContract(c). FE xóa NEXT_PHASES hardcoded, dùng contract.workflow.nextPhases BE trả. WorkflowSummaryCard timeline visual. Gotcha #21 resolved | `cae4d84` | | 2026-04-21 | Claude | **PDF export + .doc/.xls auto-convert + DynamicForm** — LibreOffice 25.8.6 VPS, IDocumentConverter shell soffice `--convert-to pdf/docx/xlsx` timeout+temp isolation. Admin upload .doc auto-convert .docx. DynamicForm parse FieldSpec JSON render inputs (text/textarea/number/date/currency/select). Form↔JSON toggle. E2E verified PDF 488KB/126 pages | `e459097` + `6bbd894` | | 2026-04-21 | Claude | **Form template builder CRUD** — Admin tự upload `.docx/.xlsx` qua UI (không cần dev). BE multipart + FormCode regex unique + FieldSpec JSON validation + soft delete via IsActive. FE FormsPage upload dialog + row actions render/edit/delete. E2E verified | `166d26c` | | 2026-04-21 | Claude | **Fix Gitea 500 sau Install Web-WebSockets** — appcmd unlock section webSocket. Gotcha #25 | `c52186b` | | 2026-04-21 | Claude | **SignalR realtime notifications E2E** — 3-project clean-arch: IRealtimeNotifier (App) + SignalRNotifier (Api) + NotificationPushInterceptor (Infra SaveChanges hook). Hub `/hubs/notifications` JWT `?access_token=` query (WebSocket headers limit). FE singleton lib/realtime.ts auto-reconnect + toast + query invalidation. IIS WebSocket module enabled | `ea9ab5e` | | 2026-04-21 | Claude | **Attachment upload E2E** — IFileStorage + LocalFileStorage (path-traversal guard) + CQRS Upload/Download/Delete + 3 endpoint (multipart, stream, DELETE) + FE ContractAttachmentsSection drag-drop + purpose selector + icon-per-MIME + auth-blob download + confirm delete. Wired 2 ContractDetailPage | `c8d0070` + `dc3f09b` | | 2026-04-21 | Claude | **Content polish** — typography 14px + leading 1.55 + tracking-tight + PageHeader border-b + Button shadow+active + Input inset shadow + DataTable rounded-xl UPPERCASE header brand hover | `346bd5d` | | 2026-04-21 | Claude | **Brand identity từ Solutions logo** — pixel-sampled #1F7DC1 → palette brand-50..900 + accent red + Be Vietnam Pro (Vietnamese-first) + favicon 'S' crop + apple-touch-icon + login gradient brand | `4abb559` + `bf1fbe3` | | 2026-04-21 | Claude | **Fix login Network Error** — SPA web.config HTTP→HTTPS redirect rule (CORS chỉ https) | `397eb36` | | 2026-04-21 | Claude | **Notifications module E2E** — Domain entity + EF migration + Infra service + CQRS + API controller + FE bells wire real endpoint + ContractWorkflowService emit notification cho Drafter khi phase transition | `49c0ddc` | | 2026-04-21 | Claude | **PermissionsPage iter 1** — search, stats badge, bulk column toggle, empty state | `6c0e206` | | 2026-04-21 | Claude | **ERP shell** — TopBar + NotificationBell + UserMenu (avatar + role badges). Layout `[sidebar] [topbar + content]` | `2b6f91c` | | 2026-04-21 | Claude | **Tier 1 UI polish** — SlaTimer (inline + full variant, 5 chỗ), Inbox stat cards, DataTable skeleton rows, EmptyState | `290936a`..`2e43799` | | 2026-04-21 | Claude | **CI/CD deploy xanh E2E** — self-hosted Windows runner, single job build+deploy, fresh node_modules (Vite 8 rolldown binding), appsettings từ secrets, /health/live 200 sau deploy | `b40da1e` | | 2026-04-21 | Claude | **VPS prod setup** — SQL DB (SQLEXPRESS), IIS sites (SolutionErp-Api/Admin/User), win-acme 3 Let's Encrypt + auto-renew, shared gitea-runner với VIETREPORT | `169e268`..`519ba85` | | 2026-04-21 | Claude | **IDOR + SLA Job + Admin warning** — ContractsController filter theo role. SlaExpiryJob BackgroundService 15min auto-approve Decision=AutoApprove. DbInitializer warn khi admin vẫn default | `fba0754` | | 2026-04-21 | Claude | **Phase 5.1 Security + Users Mgmt** — Security headers + Identity lockout + LoginHandler check + Users CQRS + UsersController + FE `/system/users` | `11e61c9` | | 2026-04-21 | Claude | **Phase 5 Prep** — BE rate limit + health check + Serilog file + HSTS + scripts deploy-iis/backup-sql + .gitea/workflows/deploy.yml + 4 guides + FE refresh token queue pattern | `46a2cab` | | 2026-04-21 | Claude | **Phase 4 Report MVP** — Dashboard KPI + Excel export + rules.md + architecture.md + schema-diagram.md + gotchas 26 pitfalls | `fe7ad8e` | | 2026-04-21 | Claude | **Phase 3 Workflow MVP** — 9 phase state machine + gen mã HĐ RG-001 | `7e957a7` | | 2026-04-21 | Claude | **Phase 2 Form Engine MVP** | `5113e4c` | | 2026-04-21 | Claude | **Phase 1.2** — CRUD Master + Permission Matrix | `54d6c9b` | | 2026-04-21 | Claude | **Phase 1 foundation** + Docs addition | `702411f` + `49a5f57` | | 2026-04-21 | Claude | **Phase 0** | `25dad7f` | Session logs: [P0](changelog/sessions/2026-04-21-1045-phase0-scaffold.md) · [P1f](changelog/sessions/2026-04-21-1100-phase1-foundation.md) · [P1.2](changelog/sessions/2026-04-21-1130-phase1-cruds-permission.md) · [P2](changelog/sessions/2026-04-21-1200-phase2-form-engine.md) · [P3](changelog/sessions/2026-04-21-1330-phase3-workflow.md) · [P4](changelog/sessions/2026-04-21-1430-phase4-report.md) · [P5prep](changelog/sessions/2026-04-21-1530-phase5-prep.md) · [Tier 3](changelog/sessions/2026-04-22-0300-tier3-feature-complete.md) · [Skill gov](changelog/sessions/2026-04-23-0900-skill-governance.md) · [Toolkit+4-bảng+Roles VN](changelog/sessions/2026-04-23-1500-toolkit-data-roles.md) · [Roles+Demo+Pending](changelog/sessions/2026-04-23-2200-roles-demo-pending-cleanup.md) · [PE polish iter 2 + rebrand](changelog/sessions/2026-04-24-chot-session-3-pe-polish.md) · [Budget BE + 14 Solutions users](changelog/sessions/2026-04-28-chot-session-4-budget.md) · [**Budget FE + PE/HD-Budget + PE WF Designer + Tests Phase 1-2**](changelog/sessions/2026-04-29-chot-session-5-budget-fe-pe-tests.md) **Docs entry points:** - [`rules.md`](rules.md) · [`architecture.md`](architecture.md) · [`HANDOFF.md`](HANDOFF.md) - [`workflow-contract.md`](workflow-contract.md) · [`forms-spec.md`](forms-spec.md) - [`database/database-guide.md`](database/database-guide.md) · [`database/schema-diagram.md`](database/schema-diagram.md) - [`flows/`](flows/) (7 file) · [`guides/`](guides/) (4 file) · [`gotchas.md`](gotchas.md) - [`changelog/migration-todos.md`](changelog/migration-todos.md) · [`changelog/sessions/`](changelog/sessions/) (17 file) - [`.claude/skills/README.md`](../.claude/skills/README.md) — 6 skill (3 domain + 3 ops) · audit định kỳ 1/tháng (cron `solution-erp-skill-audit-monthly` next 2026-05-01) ## 🎯 Next up ### Hard blockers (chờ user / ops) - [ ] **UAT 1 tuần 2-3 user thật** — hard requirement từ roadmap Phase 5 - [ ] **Email outbox** — MailKit + SMTP (BLOCKED chờ user cấp SMTP host/user/pass) - [ ] **Rotate credentials** — SA, vrapp, JWT secret, runner token (đã post chat) - [ ] **SQL backup daily** — Task Scheduler (script `scripts/backup-sql.ps1` đã có, chưa schedule) ### Optional polish (khi rảnh / UAT phát sinh) - [ ] Roles CRUD — admin tạo custom role ngoài 12 hardcoded (schema sẵn, chỉ cần CQRS + FE) - [ ] User-level approver targeting runtime — data model đã có (`WorkflowStepApprover.Kind=User`), chỉ cần wire User-kind vào `ContractWorkflowService.TransitionAsync` guard - [ ] PermissionsPage: grant `Workflows.Read` cho non-admin role → menu Wf_* visible - [ ] Warning notification khi còn 20% SLA (`SlaWarningSent` flag đã có, chỉ thiếu job emit) - [ ] E2E test reject → quay về DangSoanThao (multi-role) - [ ] Dependencies scan CI (`dotnet list package --vulnerable`, `npm audit`) ### Tier 3 ERP roadmap ✓ (close) - [x] Attachment upload BE + FE ✓ - [x] SignalR real-time push ✓ - [x] Form template builder CRUD + DynamicForm ✓ - [x] PDF export qua LibreOffice headless ✓ - [x] .doc/.xls → .docx/.xlsx auto-conversion ✓ - [x] Dynamic workflow policy per ContractType ✓ - [x] **Versioned workflow (WorkflowDefinition pinned per Contract)** ✓ - [x] **Admin workflow designer UI (per-type, per-step approvers)** ✓ - [x] **Nested sidebar menu per ContractType (fe-user) + menu split admin/user** ✓ - [x] **PermissionsPage 3-panel layout** ✓ - [ ] Email outbox for Notification (blocked — SMTP config) ## 📊 Thông số cumulative | | P0 | P1f | P1.2 | P2 | P3 | P4 | P5prep | Tier3 | +Toolkit | +RolesPg+Demo | +PE module | +PE polish | +Budget+30 users | **+Session 5** | |---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:| | BE LOC | 0 | ~400 | ~1500 | ~1900 | ~2700 | ~3100 | ~3300 | ~4800 | ~7800 | ~8800 | ~11100 | ~11400 | ~11750 | **~13050** (+1300 PE WF Designer + Opinion + Budget integration) | | DB tables | 0 | 7 | 12 | 14 | 19 | 19 | 19 | 24 | 36 | 36 | 46 | 47 | 51 | **52** (+1 PEDepartmentOpinions) | | API endpoints | 0 | 4 | 20 | 23 | 31 | 33 | 35 | ~50 | ~80 | ~93 | ~110 | ~113 | ~124 | **~128** (+2 PE WF + 2 Opinion) | | Migrations | 0 | 1 | 3 | 4 | 5 | 5 | 5 | 8 | 11 | 11 | 12 | 13 | 14 | **15** (`AddPEDepartmentOpinions`) | | FE pages | 0 | 2 | 6 | 7 | 14 | 16 | 16 | ~20 | ~22 | ~23 | ~26 | ~26 | ~26 | **~31** (+5 Budget × 2 app + PeWorkflowsPage) | | FE components | — | — | — | — | — | — | — | many | many+ | +EditRowDialog | +PE 5-tab | +Compare section | — | **+Budget tabs/panel + PE OpinionBox + PE 4-section restructure** | | Scripts PS | 0 | 0 | 0 | 1 | 1 | 1 | 3 | 4 | 4 | 5 | 5 | 6 | 6 | 6 | | CI/CD workflow | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | **1+test gate (2 step)** | | **Tests** | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | **71** (54 Domain + 17 Infra) | | Docs | 10 | 13 | 14 | 24 | 26 | 30 | 35 | ~40 | ~42 | ~44 | ~46 | ~48 | ~50 | **~52** (+session log + Test plan) | | Demo data | 0 | 0 | empty | 0 | 0 | 0 | 0 | 0 | 5+3 | 15+8+7+13+4 | +PE 4 phiếu | +rebrand email | 30 user | 30 user | | Commits | 1 | 2 | 3 | 5 | 6 | 7 | 8 | ~25 | ~47 | ~52 | ~63 | ~70 | ~75 | **~82** (+6 session 5) | ## 🚨 Blockers / risks - ⚠️ **Email SMTP chưa có** — blocker cho notification outbound - ⚠️ **UAT real user chưa chạy** — risk phát sinh bug edge-case quan trọng - ⚠️ **Credentials leaked trong chat** — cần rotate trước go-live thật - ⚠️ **SQL backup không auto** — risk data loss nếu VPS crash - ⚠️ **Permission `Workflows.Read` cho non-admin** — cần grant để họ thấy menu Wf_* (hiện chỉ admin thấy) - ⚠️ **User-kind approver chưa enable runtime** — designer cho chọn User nhưng guard fall back DeptManager ## Credentials + URLs ``` admin@solutionerp.local / Admin@123456 ``` - API prod: https://api.solutions.com.vn — Health `/health/live` + `/health/ready` - API dev: http://localhost:5443 — Swagger `/swagger` - Admin FE prod: https://admin.solutions.com.vn · dev `http://localhost:8082` - User FE prod: https://eoffice.solutions.com.vn · dev `http://localhost:8080` - SQL prod: `.\SQLEXPRESS` / `SolutionErp` / `vrapp` - SQL dev: `(localdb)\MSSQLLocalDB` / `SolutionErp_Dev`