[CLAUDE] Docs: compact 3 file core (-273 dòng tổng)

STATUS.md (-52 dòng, -27%): archive 51 row Recently Done Phase 0-7 cũ
  → changelog/recently-done-archive-2026-04.md (file mới)
  Giữ 12 row mới nhất Phase 8/Session 5 + pointer archive.

HANDOFF.md (-147 dòng, -32%): bỏ 3 section duplicate
  - Versioned workflow quick ref → cross-ref workflow-contract.md §7bis
  - File đang active (90 dòng tree) → cross-ref PROJECT-MAP.md
  - Git state snapshot stale → cross-ref `git log --oneline -10`

migration-todos.md (-74 dòng, -35%): collapse Phase 6 iter 1+2 + Phase 7
  done parts thành 4 dòng paragraph. Giữ Phase 8/9 active + skill audit.

Quy tắc compact (rules.md §6 implicit):
  - Recently Done > 30 row → archive cũ vào changelog/recently-done-archive-{YYYY-MM}.md
  - Phase done >= 1 tháng → collapse thành 1 paragraph + cross-ref session log
  - Section duplicate file khác → cross-ref thay vì copy

Commit MD-only → CI skip (path filter gotcha #41).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-30 01:10:03 +07:00
parent d2298fa614
commit f24acb699a
4 changed files with 79 additions and 288 deletions

View File

@ -253,159 +253,13 @@ Trigger: user nói "audit skill" hoặc tự chạy đầu Phase mới.
- Workflow transition 403 → check `Contract.WorkflowDefinitionId` pin đúng không - Workflow transition 403 → check `Contract.WorkflowDefinitionId` pin đúng không
- Migration lỗi → 3 file đầy đủ (Designer + Migration + Snapshot) - Migration lỗi → 3 file đầy đủ (Designer + Migration + Snapshot)
## Versioned workflow — quick reference ## 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`](workflow-contract.md).
Contract.WorkflowDefinitionId (nullable Guid FK)
→ pin tại `CreateContractCommandHandler` = WorkflowDefinitions.Single(d => d.ContractType == c.Type && d.IsActive)
→ ContractWorkflowService.LoadAsync(contractId):
if contract.WorkflowDefinitionId != null:
def = db.WorkflowDefinitions.Include(Steps.Approvers).First(wfId)
return WorkflowPolicyRegistry.FromDefinition(def)
else if admin override ở WorkflowTypeAssignments:
return Registry.ByName(override.PolicyName)
else:
return Registry.For(contract.Type) // hardcoded Standard/SkipCcm
Admin tạo version mới: ## File đang active
POST /api/workflows
body: { code, name, contractType, steps: [{ order, phase, name, slaDays, approvers: [{ kind, assignmentValue }] }] }
→ auto increment Version = max(Version where Code==code) + 1
→ deactivate old IsActive trong cùng ContractType (atomic)
→ HĐ cũ ĐÃ PIN WorkflowDefinitionId = old Id → vẫn chạy policy cũ ✓
```
Invariants: Cấu trúc thư mục + file map: [`PROJECT-MAP.md`](PROJECT-MAP.md). Git state: `git log --oneline -10`.
- `UNIQUE (Code, Version)` per WorkflowDefinitions
- **Chỉ 1 IsActive=true per ContractType** tại 1 thời điểm
- `Contract.WorkflowDefinitionId` KHÔNG cascade khi xóa WorkflowDefinition → protect history
## File đang active (hiện trạng)
```
SOLUTION_ERP/
├── src/Backend/ (Clean Arch, 4 project, .NET 10)
│ ├── SolutionErp.Domain/
│ │ ├── Common/ BaseEntity, AuditableEntity
│ │ ├── Contracts/ ContractType, ContractPhase, ApprovalDecision,
│ │ │ Contract (+WorkflowDefinitionId), ContractApproval,
│ │ │ ContractComment, ContractAttachment, ContractCodeSequence,
│ │ │ **WorkflowPolicy** (record + registry + FromDefinition),
│ │ │ **WorkflowDefinition** (Code+Version+IsActive+ContractType),
│ │ │ **WorkflowStep** (Order+Phase+Name+SlaDays),
│ │ │ **WorkflowStepApprover** (Kind=Role|User, AssignmentValue),
│ │ │ **WorkflowTypeAssignment** (admin override legacy)
│ │ ├── Forms/ ContractTemplate (+FieldSpec JSON), ContractClause
│ │ ├── Identity/ User, Role, MenuItem, Permission, AppRoles,
│ │ │ **MenuKeys** (+ContractTypeCodes, ContractTypeGroup/List/Create/Pending helpers, WorkflowTypeLeaf)
│ │ ├── Master/ Supplier (+SupplierType), Project, Department
│ │ └── Notifications/ **Notification** (+NotificationType enum)
│ ├── SolutionErp.Application/
│ │ ├── Auth/ Login, Refresh, Me
│ │ ├── Common/
│ │ │ └── Interfaces/ IApplicationDbContext, ICurrentUser, IDateTime,
│ │ │ IJwtTokenService, **IFileStorage**, **IDocumentConverter**,
│ │ │ **IRealtimeNotifier**, **INotificationService**
│ │ ├── Contracts/ ContractFeatures, IContractWorkflowService,
│ │ │ **ContractAttachmentFeatures** (Upload/Download/Delete CQRS),
│ │ │ **WorkflowAdminFeatures** (Overview + CreateNewVersion)
│ │ ├── Forms/ FormFeatures (List/Get/Render/**Upload/Update/Delete/ExportPdf**)
│ │ ├── Master/ Suppliers, Projects, Departments CQRS
│ │ ├── **Notifications/** NotificationFeatures (List/UnreadCount/MarkRead/MarkAllRead)
│ │ ├── Permissions/ GetMyMenuTree (**+inherit Contracts/Workflows**)
│ │ └── Reports/ DashboardStats, ExportToExcel, **MyDashboard**
│ ├── SolutionErp.Infrastructure/
│ │ ├── Forms/ DocxRenderer, XlsxRenderer, FormRenderer,
│ │ │ **LibreOfficeDocumentConverter**
│ │ ├── Identity/ JwtSettings, JwtTokenService
│ │ ├── Persistence/
│ │ │ ├── Interceptors/ AuditingInterceptor, **NotificationPushInterceptor**
│ │ │ └── Migrations/ 8 migrations
│ │ ├── Reports/ ContractExcelExporter
│ │ ├── **Storage/** LocalFileStorage (path-traversal guard)
│ │ └── Services/ DateTimeService, **ContractWorkflowService (load pinned def)**,
│ │ ContractCodeGenerator, **NotificationService**
│ └── SolutionErp.Api/
│ ├── Authorization/ MenuPermissionHandler + Requirement
│ ├── Controllers/ Auth, Suppliers, Projects, Departments, Menus,
│ │ Roles, Permissions, Forms, Contracts, Reports,
│ │ Users, **Notifications**, **Workflows**
│ ├── **Hubs/** NotificationHub (/hubs/notifications)
│ ├── Middleware/ GlobalExceptionMiddleware
│ ├── **Realtime/** SignalRNotifier
│ ├── Services/ CurrentUserService, WebHostEnvironmentLocator
│ └── wwwroot/templates/ .docx/.xlsx templates
├── fe-admin/ (~18 page)
│ └── src/
│ ├── pages/
│ │ ├── LoginPage, DashboardPage (MyDashboardRow)
│ │ ├── master/ Suppliers, Projects, Departments
│ │ ├── system/ **PermissionsPage (3-panel)**, **WorkflowsPage (URL-driven)**, Users
│ │ ├── forms/ FormsPage (upload + Form/JSON + PDF)
│ │ ├── contracts/ List, Detail (+Attachments), **Create**
│ │ └── ReportsPage
│ ├── components/ Layout (recursive menu + filterForAdmin),
│ │ TopBar, NotificationBell, UserMenu, SlaTimer,
│ │ EmptyState, PhaseBadge, WorkflowSummaryCard,
│ │ ContractAttachmentsSection, DynamicForm,
│ │ **WorkflowDesigner** (Steps + Approvers modal)
│ └── lib/ api.ts, realtime.ts, cn.ts
├── fe-user/ (~10 page)
│ └── src/
│ ├── pages/ Login, Inbox (+?type filter),
│ │ contracts/{Create, Detail, MyContracts}
│ ├── components/ Layout (recursive + filterForUser + USER_FIXED_TOP),
│ │ NotificationBell, ContractAttachmentsSection, SlaTimer
│ └── lib/ realtime.ts (same singleton pattern)
├── docs/ (~40 file)
│ ├── STATUS, HANDOFF, rules, architecture, CLAUDE, PROJECT-MAP (6)
│ ├── workflow-contract, forms-spec (2)
│ ├── database/{database-guide, schema-diagram} (2)
│ ├── flows/ (7 file — README + 6 flow)
│ ├── guides/ (4 file)
│ ├── changelog/migration-todos + sessions/ (8 session log)
│ └── gotchas (26 pitfall)
├── scripts/ (5 PS + py)
│ ├── parse_forms, parse_workflow (Phase 0)
│ ├── convert-doc-to-docx (Phase 2)
│ ├── deploy-iis, backup-sql (Phase 5)
│ └── install-libreoffice (Tier 3)
├── .gitea/workflows/deploy.yml CI/CD Windows self-hosted runner
└── .claude/skills/ 3 skill (contract-workflow, form-engine, permission-matrix)
```
## Git state
```
HEAD → main
bcdc007 — Infra: Mở rộng seed master (15 NCC + 8 Project) + backfill demo HĐ (LATEST)
e53cd3a — App+Api+FE+Scripts: Edit detail row inline + deps audit helper
4edcd58 — Domain+Infra: User-kind approver runtime guard + Warning 20% SLA
8bc9565 — Infra: SeedDemoContractsAsync — 7 HĐ varied phases UAT-ready
072ad6d — App+Api+FE-Admin: RolesPage CRUD (/system/roles)
ff5e35f — Docs: chốt session 2026-04-23 chiều
ae59cfe — FE-Admin: UsersPage dept/position + RoleShortName VN
330d529 — Domain+App+Infra: Role ShortName + User Dept/Position + 13 demo users (migration 11)
39031ca — FE: ContractDetailsPreview cho create mode
16e24ed — FE: Admin CatalogsPage CRUD + Details datalist autocomplete
e27c547 — Domain+App+Api: 4 master catalogs cho Details (migration 10)
51449d6 — App+Infra: Mã HĐ gen ngay tại CreateContract + backfill legacy
7f26ff9 — Edit/Xóa luôn hiện, mờ khi != DangSoanThao
8c4b4da — 2-panel layout cho Thao tác
ad0652d — Bỏ tabs, Chi tiết+Lịch sử 7/3 grid
b3762af — Panel 2 tabs (Tổng quan/Chi tiết/Lịch sử)
e684455 — App+Api: 7 Details CRUD endpoints + Changelogs query
71c035d — App+Infra: IChangelogService
70810e1 — Domain+Infra: 7 Details + ContractChangelog (migration 9)
d326e80 — FE-User: tách Tổng quan thành /dashboard riêng
89c7e88 — FE-User: 3-panel InboxPage
b75448e — 3-panel layout cho danh sách HĐ
7ea3957 — Sidebar accordion menu loại HĐ
d43d2c0 — Docs: chốt session 2026-04-23 — skill governance
Branch: main (tracking origin/main)
Remote: https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp.git
```
## Credentials + URLs ## Credentials + URLs

View File

@ -57,60 +57,9 @@
| 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 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 | **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-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` | > **51 row Phase 0-7 (2026-04-21..28) đã archive →** [`changelog/recently-done-archive-2026-04.md`](changelog/recently-done-archive-2026-04.md)
| 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 `<datalist>` 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_<Code> 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_<Code>` leaf dưới group `Workflows`. Layout resolvePath `Wf_<Code>``/system/workflows/<code>`. 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) 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)

View File

@ -17,95 +17,22 @@
Detail chi tiết: `docs/changelog/sessions/2026-04-21-*.md` + `2026-04-22-0300-tier3-feature-complete.md` + `2026-04-23-*.md`. 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 ## ✅ Phase 6-7 — PE Module + Budget BE — Done (2026-04-23..28)
### Iter 1 (2026-04-23) - **Phase 6 iter 1+2**: Module Duyệt NCC tiền-HĐ — Migration 12+13, 10 bảng PE + WorkflowDefinitions, 2 type A/B, 17 endpoint, FE 3-panel cả 2 app, kế thừa HĐ qua `CreateContractFromEvaluationCommand`. UX polish iter 2 (flat layout, per-NCC attachments, readOnly mode Duyệt). Demo seed 4 phiếu varied + 7 role × 9 Pe_* permission defaults.
- **Domain rebrand**: 3 sub `.huypham.vn``.solutions.com.vn` E2E live HTTPS (cert + CORS + FE bundle).
- **Phase 7 — Budget BE**: Migration 14, 4 bảng Budgets + workflow simple 3-step hardcoded `BudgetPolicy.Default`, 11 endpoint, link nullable Contract.BudgetId/PE.BudgetId. **FE chưa làm** (Phase 8 done).
- **Phase 7 — 14 demo Solutions users**: PRO 5 + CCM 7 + ISO 1 + CEO 1, total 30 user (16 sample + 14 thật).
- [x] Migration 12 `AddPurchaseEvaluations` — 10 bảng (Header/Suppliers/Details/Quotes/Approvals/Changelogs/Attachments/WorkflowDefinitions/Steps/StepApprovers) Session logs: `2026-04-23-2300-purchase-evaluations.md` · `2026-04-24-chot-session-3-pe-polish.md` · `2026-04-28-chot-session-4-budget.md`.
- [x] Domain — 2 enum (Type A/B, Phase 7-state) + Policy record + Registry + FromDefinition builder
- [x] Seed — 13 menu Pe_*/PeWf_* + 2 WorkflowDefinition v01 (QT-DN-A 3-step, QT-DN-B 5-step)
- [x] Application CQRS ~900 LOC — Create/Update/Transition/List/Inbox/Get/Delete + Supplier CRUD + Detail CRUD + Quote Upsert + SelectWinner + Changelog
- [x] PurchaseEvaluationWorkflowService — policy guard + approval + notification + changelog
- [x] PurchaseEvaluationsController — 17 endpoint REST
- [x] FE 2 app — Types + PurchaseEvaluationsListPage 3-panel + Create page + PeDetailTabs + PeWorkflowPanel + Menu resolver Pe_*
- [x] Kế thừa HĐ — `CreateContractFromEvaluationCommand` (guard DaDuyet + SelectedSupplier + !ContractId) → Contract draft. FE CreateContractDialog pick ContractType.
- [x] **Migration 13** `AddPurchaseEvaluationCodeSequences` — atomic MaPhieu sequence `PE/{YYYY}/{A|B}/{Seq:D3}`
- [x] 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`. 3 task PE feature gap (Workflow Designer / Ý kiến 4 PB / Export PDF) → đã đóng 2/3 ở Phase 8 §C+D. Export PDF carry over Phase 9.
### Iter 2 — UX polish (2026-04-24) ### Pending migrations (chưa cần đến)
- [x] Rename menu "Phương Án" → "Giải pháp" + backfill DB (zero breaking change) - [ ] `AddPePaymentTermFields` — nếu chốt UX tách field (JSON → 6 column riêng)
- [x] Menu tree inheritance extend Pe_*/PeWf_* (`GetMyMenuTreeQuery` + 4 root) - [ ] `AddBudgetCodeSequences` — atomic SERIALIZABLE khi chốt format MaNganSach (hiện Random.Shared)
- [x] Accordion mutex Pe_* groups + sidebar w-72 + label nowrap - [ ] `AddBudgetVersionedWorkflow` — nếu user cần admin config UI thay hardcoded `BudgetPolicy.Default`
- [x] NavLink active check query string (queryMatches helper) — fix 2 leaf cùng highlight
- [x] 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
- [x] Upload file đính kèm per-NCC (SupplierAttachmentsCell) + Bảng so sánh tổng (GeneralAttachmentsSection, supplierRowId=null) + enum `ComparisonTable=4`
- [x] readOnly mode menu "Duyệt" (pendingMe=1) — hide Sửa/Xóa/Thêm/Edit/Upload/Delete, giữ download + transition + comment
- [x] Contract: move Lịch sử điều chỉnh Panel 2 → Panel 3 (Chi tiết HĐ full-width)
- [x] 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)
- [x] 18 file repo (FE env + scripts + CI/CD + docs + skill + code comments)
- [x] `scripts/migrate-domains.ps1` (ASCII-only #30) — 3 IIS binding + 3 cert Let's Encrypt + auto HTTPS + redirect
- [x] CI/CD auto rebuild BE CORS + FE bundle VITE_API_BASE_URL
- [x] 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 7 — PE feature gap + Budget BE (Session 4 partial done)
### A. PE feature gap (3 task MISSING — carry over session 5)
- [ ] **PE Workflow admin designer UI** `/system/pe-workflows/:typeCode`
- BE `Application/PurchaseEvaluations/PeWorkflowAdminFeatures.cs` (mirror `WorkflowAdminFeatures.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)
- [ ] **Ý kiến 4 phòng ban** (Phê duyệt / P.CCM / P.MuaHàng / SM-PM) ở tab Thông tin
- Option A: 4 text field + signoff date + UserId vào header
- Option B: dùng `PurchaseEvaluationApprovals` với roleKind extra field
- UX: 4 box sign-off như Excel mẫu
- [ ] **Export phiếu PDF/Excel** — tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx`
### B. Optional polish
- [ ] 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
- [ ] Remove binding cũ `.huypham.vn` sau 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
- [x] **Migration 14** `AddBudgets` — 4 bảng (Budgets/BudgetDetails/BudgetApprovals/BudgetChangelogs) + index BudgetId nullable trên Contract & PurchaseEvaluation
- [x] Domain — `Budget` (Header) + `BudgetDetail` (flat row) + `BudgetApproval` + `BudgetChangelog` + enum `BudgetPhase` 5-state + `BudgetEntityType` Header/Detail/Workflow
- [x] `BudgetPolicy.Default` hardcoded simple 3-step (Drafter→CCM→CEO + Reject từ ChoCCM/ChoCEO về DangSoanThao)
- [x] Application CQRS ~340 LOC — Create + UpdateDraft + Transition + List + GetDetail + Delete (only DangSoanThao/TuChoi) + Detail CRUD (auto-recompute TongNganSach) + ListChangelogs
- [x] `BudgetsController` 11 endpoint REST
- [x] Menu seed `Budgets` root + 3 leaf (Bg_List/Bg_Create/Bg_Pending) order=27 icon Wallet
- [x] **14 demo user Solutions thật** — PRO 5 + CCM 7 + ISO 1 + CEO 1 (pwd `User@123456`)
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)
- [x] **`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ì hardcoded `BudgetPolicy.Default`)
## ✅ Phase 8 — Budget FE + PE/HD integration (Session 5 done) ## ✅ Phase 8 — Budget FE + PE/HD integration (Session 5 done)

View File

@ -0,0 +1,61 @@
# Recently Done — Archive (Phase 0-7, 2026-04-21..28)
> Archive từ `STATUS.md` Recently Done table sau khi Phase 8 chốt 2026-04-29.
> Giữ lại để tra cứu lịch sử commit ↔ feature mapping. Newest đã chuyển ngược lên top.
| Ngày | Ai | Task | Commit |
|---|---|---|---|
| 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 `<datalist>` 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_<Code> 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_<Code>` leaf dưới group `Workflows`. Layout resolvePath `Wf_<Code>``/system/workflows/<code>`. 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` |