Files
solution-erp/docs/HANDOFF.md
pqhuy1987 e0b4e7f096
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m53s
[CLAUDE] Docs: chốt session 4 — Budget BE module + 14 Solutions users
- STATUS.md: header Phase 7 + 3 row Recently Done (Budget BE / 14 users / Docs cleanup) + cumulative cột mới (51 tables / 14 mig / ~124 endpoints)
- HANDOFF.md: TL;DR session 4 (2 milestone Budget BE + 14 users) + Cảnh báo session 5 + Priority 0 (FE Budget + PE/HD integration + PE feature gap) + Credentials 30 user
- migration-todos.md: Phase 7 thêm section D Budget done + Phase 8 mới (FE Budget pages + integration) + pending migrations Budget
- architecture.md: §10 Budget module mới (ERD + state machine + auto-recompute + integration roadmap)
- database/schema-diagram.md: migration history rows 13+14 + §12 Budget ERD chi tiết
- ef-core-migration SKILL: migration 14 entry + Phase 8 pending Budget refinement
- CLAUDE.md root + docs: modules table thêm Budget row + scope Budget + count 51 bảng / 14 mig
- Session log 2026-04-28-chot-session-4-budget.md (10+ section detailed)

Stats: 51 tables (+4 Budget), 14 migrations (+AddBudgets), ~124 endpoints (+11 Budget),
30 demo user (16 sample + 14 Solutions thật), 38 gotchas, ~340 LOC Budget CQRS.
FE Budget pages chưa làm — Priority 0 session 5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 12:36:31 +07:00

438 lines
25 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# HANDOFF — Brief 5 phút cho session tiếp theo
**Last updated:** 2026-04-28 (Phase 7 — **Module Ngân sách (Budget) BE + 14 demo users Solutions thật**)
## TL;DR
**Session 4 (28/04)** thêm 2 milestone lớn:
### A. Module Ngân sách (Budget) BE — migration 14, +4 bảng, +11 endpoint
- 4 entity: `Budget` (Header) + `BudgetDetail` (flat row) + `BudgetApproval` (history) + `BudgetChangelog` (audit log).
- Enum `BudgetPhase` 5 state (DangSoanThao→ChoCCM→ChoCEO→DaDuyet + TuChoi).
- `BudgetPolicy.Default` hardcoded simple 3-step (Drafter→CCM→CEO) — chưa versioned (TODO khi user cần admin config UI).
-`NS-YYYYMM-XXXX` Random.Shared (chưa atomic — TODO khi format chính thức).
- Link nullable: `Contract.BudgetId?` + `PE.BudgetId?` đã có FK + index, FE chưa wire form.
- Menu seed `Budgets` root + 3 leaf (Bg_List/Bg_Create/Bg_Pending) order=27 icon Wallet.
- Application: 11 CQRS handler ~340 LOC (Create/UpdateDraft/Transition/List/GetDetail/Delete + Detail CRUD auto-recompute TongNganSach + ListChangelogs).
- Api: `BudgetsController` 11 endpoint REST.
- **FE chưa làm — Priority 0 session 5.**
### B. 14 demo user Solutions thật
- PRO 5 (TPB tra.bui + 4 NV) + CCM 7 (TPB ngocanh.huynh + 6 NV) + ISO 1 (chau.le) + CEO 1 (huy.duong).
- Pwd `User@123456`. Reconcile pattern (gotcha #38 4-field rename).
- Tổng 30 user (16 sample cũ giữ + 14 Solutions thật mới).
**Tổng:** 51 DB tables, ~124 endpoints, 14 migrations, 38 gotchas, 5+ commit
session 4 push lên Gitea.
## ⚠️ CẢNH BÁO session tiếp (Session 5)
1. **FE Budget pages CHƯA LÀM** — BE đã sẵn sàng nhưng chưa có page nào. Pattern: copy từ PE 3-panel List + Create + Detail tabs (Thông tin / Hạng mục) + WorkflowPanel timeline. Mirror sang fe-user. Thêm route `/budgets`, `/budgets/new`, `/budgets/:id`. Menu resolver `Bg_*` → URL.
2. **PE/Contract → Budget integration CHƯA WIRE** — BE đã có `BudgetId?` nullable FK trên cả 2 entity, FE form chưa thêm field "Ngân sách" select. Cần filter Budget theo `Phase=DaDuyet && NamNganSach=current && ProjectId match`. Tab Hạng mục có thể bonus cột "So với ngân sách".
3. **Chưa xóa binding cũ `.huypham.vn`** — vẫn active fallback. Sau 1-2 ngày
verify stable → `.\migrate-domains.ps1 -RemoveOld -SkipCert` trên VPS.
4. **win-acme scheduled task "unhealthy"** — cert auto-renew có thể fail
khi gần 2026-06-18. Fix: mở `wacs.exe` interactive → Manage Renewals →
recreate task.
5. **PE feature gap carry over** (xem STATUS §C):
- PE Workflow admin designer UI `/system/pe-workflows/:typeCode`
- Section "Ý kiến 4 phòng ban" (Phê duyệt/CCM/MuaHàng/SM-PM)
- Export phiếu PDF/Excel
6. **Login email** `admin@solutionerp.local` / `Admin@123456` — domain default
chưa đổi sang `@solutions.com.vn` (chỉ demo user và rebrand cũ trong BackfillDemoEmail).
7. **Chú ý G-084:** VPS shared với VietReport — mọi reverse proxy / backend
service mới phải dùng `127.0.0.1` + bind loopback IPv4 explicit.
## ⭐ Skills (.claude/skills/) — PHẢI dùng khi task khớp
| Domain (3) | Ops (3) |
|---|---|
| `contract-workflow` — state machine + versioned WF | `dependency-audit-erp` — npm/dotnet CVE scan |
| `form-engine` — render docx/xlsx + PDF | `ef-core-migration` — EF migration + 3-file rule |
| `permission-matrix` — role × menu × CRUD | `iis-deploy-runbook` — 3 site IIS + win-acme + runner |
**Audit cron:** `0 9 1 * *` (9:00 AM ngày 1 mỗi tháng). Workflow: `docs/rules.md §9.4`. Lần kế: **2026-05-01**.
**Quy tắc:** KHÔNG bulk-clone repo skill 3rd party. Chỉ skill PROJECT-SPECIFIC. Đầy đủ: `docs/rules.md §9`.
## Ở đâu rồi?
| Phase | Trạng thái |
|---|---|
| 0 Draft | ✅ Done |
| 1 Alpha Core (foundation + đợt 2 CRUD + Permission) | ✅ Done |
| 2 Form Engine MVP + iter 2 (upload UI + .doc auto-convert + PDF export) | ✅ Done |
| 3 Workflow MVP (9 phase + code gen) + iter 2 (SLA job + attachment + notify) | ✅ Done |
| 4 Report MVP (Dashboard + Excel) + user-specific dashboard | ✅ Done |
| 5 Prep + 5.1 Security + Users Mgmt | ✅ Done |
| **5 Deploy prod** (3 domain HTTPS live) | ✅ Done |
| **Tier 3 (Attach + Realtime + Form builder + PDF + Versioned WF + Nested menu + Permission 3-panel)** | ✅ Done |
| **Skill governance** (6 skill project-level + audit cron 1/tháng) | ✅ Done |
| **3-panel List/Inbox/Thao tác + sidebar accordion + UserDashboard** | ✅ Done |
| **4-bảng data model** (Header + 7 Details + Workflow + Changelog audit) | ✅ Done |
| **Mã HĐ gen tại Create + backfill legacy** | ✅ Done |
| **4 master catalogs** (Units/Materials/Services/WorkItems) + datalist autocomplete | ✅ Done |
| **Roles VN labels (ShortName)** + Users dept/position + 13 demo users | ✅ Done |
| **RolesPage CRUD** `/system/roles` + custom role admin | ✅ Done |
| **7 demo HĐ varied phases** + details + comments + approvals workflow | ✅ Done |
| **User-kind approver runtime guard** + Warning 20% SLA | ✅ Done |
| **Edit detail row inline** (7 typed Update commands + EditRowDialog) | ✅ Done |
| **Master expand 15 NCC + 8 Project** + backfill demo HĐ diverse | ✅ Done |
| **Deps audit script** (`scripts/deps-audit.ps1`) | ✅ Done |
| **Module Duyệt NCC (tiền-HĐ) E2E** — 10 bảng + 2 workflow + Kế thừa HĐ | ✅ Done |
| **PE polish iter 2** — flat layout + per-NCC attachments + readOnly Duyệt + email rebrand | ✅ Done |
| **Domain rebrand huypham.vn → solutions.com.vn** — 3 subdomain + cert + CORS + FE bundle | ✅ Done |
| **Module Ngân sách BE** — 4 bảng + 11 endpoint + workflow simple + 30 user | ✅ Done (FE TODO) |
| **PE Workflow designer UI + Ý kiến 4 phòng ban + Export PDF** | 📝 Pending session 5+ |
| **FE Budget pages + PE/Contract → Budget integration** | 📝 Priority 0 session 5 |
| 8+ Post-launch (E-signature, Bravo/SAP, Mobile, AI) | 📝 Future |
## Run nhanh
```powershell
# Terminal 1 — API (auto seed 12 role + 9 dept + 5 supplier + 3 project + 8 template + 7 workflow definition + 28 ContractType menu + 7 workflow menu)
dotnet run --project src\Backend\SolutionErp.Api
# Terminal 2 — Admin FE
cd fe-admin && npm run dev # → http://localhost:8082
# Terminal 3 — User FE
cd fe-user && npm run dev # → http://localhost:8080
```
Login: `admin@solutionerp.local` / `Admin@123456`
## Quick sanity-check
**Admin (:8082):**
- `/dashboard` → "Của tôi" row 4 card + KPI cards + charts
- `/contracts` → list toàn bộ, filter phase/supplier/project
- `/contracts/new?type=5` → tạo HĐ Mua bán, pre-select type từ URL
- `/contracts/{id}` → timeline + action dialog + attachments drag-drop + WorkflowSummaryCard
- `/system/workflows` → 7-card landing (Thầu phụ/Giao khoán/NCC/Dịch vụ/Mua bán/NguyenTacNcc/NguyenTacDv)
- `/system/workflows/MuaBan` → DefinitionCard active + history + "Tạo phiên bản mới" modal với Steps + Approvers (+Role / +User)
- `/system/permissions` → 3-panel layout (Role list | Menu×CRUD matrix | Granted stats)
- `/system/users` → Users CRUD + assign roles
- `/forms` → upload .docx/.xlsx + render dialog Form↔JSON + Tải PDF
**User (:8080):**
- `/inbox?type=5` → HĐ Mua bán chờ role mình
- `/my-contracts?type=2` → HĐ Thầu phụ của tôi
- `/contracts/new?type=3` → tạo HĐ NCC
- Sidebar nested: 📄 Hợp đồng → expand 7 type → expand "HĐ Mua bán" → Danh sách / Thao tác / Duyệt
**Realtime check:**
- Login 2 tab (admin + user) → user tạo comment / transition → admin nhận toast + bell +1
## Cần làm kế tiếp
### 🔥 Priority 0 — Budget FE + PE/HD integration (session 5)
Xem **STATUS.md §🔥 In Progress** đầy đủ (nhóm A/B/C/D/E). Tóm tắt:
**A. Budget FE pages — copy pattern PE:**
1. `fe-admin/src/types/budget.ts` (types + enums BudgetPhase + ApprovalDecision reuse)
2. `fe-admin/src/pages/budgets/BudgetsListPage.tsx` (3-panel `lg:grid-cols-[320px_1fr_360px]` — Panel 1 list + Panel 2 detail tabs + Panel 3 workflow timeline)
3. `fe-admin/src/pages/budgets/BudgetCreatePage.tsx` (form Header: tên/năm/dự án/phòng ban/người soạn)
4. `fe-admin/src/components/budgets/BudgetDetailTabs.tsx` (Thông tin / Hạng mục — flat row giống PE Details)
5. `fe-admin/src/components/budgets/BudgetWorkflowPanel.tsx` (Panel 3: timeline phase + ô comment + button Trình)
6. Mirror tất cả sang `fe-user/`
7. App.tsx routes `/budgets`, `/budgets/new`, `/budgets/:id` cả 2 app
8. Menu resolver `Bg_*` ở Layout: `Bg_List``/budgets`, `Bg_Pending``/budgets?phase=Pending`, `Bg_Create``/budgets/new`
**B. PE/Contract → Budget integration:**
1. **PE form** thêm field `Ngân sách` select Budget (filter `Phase=DaDuyet && NamNganSach=current && ProjectId=peProjectId`). Lưu `PE.BudgetId`.
2. **Contract form** tương tự — link sang Budget cho đối chiếu chi phí.
3. PE Detail tab có thể thêm cột "So với ngân sách" — compute từ `BudgetDetail` tương ứng (match GroupCode + ItemCode) nếu có Budget link.
**C. PE feature gap (carry over từ session 3):**
1. **PE Workflow admin designer UI** `/system/pe-workflows/:typeCode` — mirror pattern `WorkflowsPage.tsx` + `WorkflowDesigner.tsx`. BE cần `PeWorkflowAdminFeatures.cs` + `PeWorkflowsController.cs`. Framework backend đã sẵn (3 bảng `PurchaseEvaluationWorkflow*` + `FromDefinition`), chỉ thiếu wire UI.
2. **Ý kiến 4 phòng ban** (Phê duyệt / P.CCM / P.MuaHàng / SM-PM) — Excel form có, entity chưa map. Cần design: 4 text field + signoff date, hoặc dùng Approvals với role-kind.
3. **Export phiếu PDF/Excel** — tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx`.
**D. Optional polish:**
- Budget MaNganSach atomic sequence (hiện Random.Shared, format chốt sau)
- Budget versioned workflow (admin config qua UI — hiện hardcoded `BudgetPolicy.Default`)
- Payment terms PE tách field
- Auto-map PE Details → Contract Details khi gen HĐ
- Matrix Quotes bulk paste từ Excel
**Đã xong trong session 4 (check STATUS Recently Done):**
- ~~Module Ngân sách BE — 4 bảng + 11 endpoint + workflow simple~~ ✅
- ~~14 demo user Solutions thật (PRO 5 + CCM 7 + ISO 1 + CEO 1)~~ ✅
- ~~Docs cleanup + tái cấu trúc MD~~ ✅
### A. Hard blockers (chờ user / ops)
1. **UAT thật 1 tuần với 2-3 user** — hard requirement từ roadmap. Kiến nghị:
- User A: Drafter (QS/NV.PB) — tạo 3 HĐ mỗi type, đi hết 9 phase
- User B: CCM — duyệt phase 6
- User C: BOD — duyệt phase 7
- Ghi bug / friction / đề xuất → backlog iter 2
2. **SMTP config** để bật Email outbox:
```json
"Email": {
"Host": "smtp.gmail.com",
"Port": 587,
"Username": "...",
"Password": "...",
"From": "noreply@solutionerp.local"
}
```
Khi có → thêm `MailKit`, `IEmailSender`, hook vào `NotificationService.CreateAsync` ngay trước khi enqueue realtime push.
3. **Rotate credentials** — SA SQL password, vrapp password, JWT secret prod, Gitea runner registration token
4. **Schedule SQL backup** — `schtasks /create /tn "SolutionErp Backup" /tr "powershell -File C:\...\scripts\backup-sql.ps1" /sc DAILY /st 03:00`
### A1. Định kỳ — Skill audit
**Cadence:** Mỗi đầu tháng (4 tuần). Lần audit kế tiếp: **2026-05-01**.
Workflow xem `docs/rules.md §9.4`. Tóm tắt:
1. Cross-check 6 skill hiện có với STATUS / gotchas / migration-todos
2. Check repo nguồn 3rd party (alirezarezvani/claude-skills) có gì mới
3. Update / archive / add skill nếu cần
4. Log vào `docs/changelog/skill-audit-2026-05.md`
Trigger: user nói "audit skill" hoặc tự chạy đầu Phase mới.
### B. Polish iterations (optional — khi UAT phát sinh)
- **Roles CRUD** — admin tạo custom role ngoài 12 hardcoded (`Domain.Identity.AppRoles`)
- **User-kind approver runtime** — data model `WorkflowStepApprover.Kind=User` + `AssignmentValue=userId` đã có, chỉ cần:
```csharp
// ContractWorkflowService.TransitionAsync (bổ sung):
var userApprovers = step.Approvers.Where(a => a.Kind == ApproverKind.User)
.Select(a => Guid.Parse(a.AssignmentValue));
if (userApprovers.Any() && !userApprovers.Contains(actorUserId))
throw new ForbiddenException();
```
- **Grant `Workflows.Read` cho non-admin role** trong PermissionsPage → menu Wf_* auto-visible (inheritance đã có)
- **Warning notification 20% SLA** — job emit khi `SlaDeadline - now < sla * 0.2 && !SlaWarningSent`, set flag
- **Reject → DangSoanThao E2E test** với 3 role khác nhau
- **Deps scan CI** — `dotnet list package --vulnerable` + `npm audit --audit-level=high`
### C. Non-goals / parked
- E-signature (VNPT CA / FPT CA) — Phase 6
- Bravo/SAP import NCC — Phase 6
- Mobile app — Phase 6
- AI OCR scan HĐ — Phase 6+
## Lưu ý kỹ thuật quan trọng
**Đọc [`gotchas.md`](gotchas.md) (38 bẫy) trước khi:**
- Thêm package mới → .NET 10 compat (MediatR 14 fail → dùng 12.4.1)
- Debug TS enum error → dùng const-object pattern (`erasableSyntaxOnly`)
- Expression tree lỗi → tách switch ra ngoài LINQ
- Deploy Windows Feature (WebSockets, etc.) → unlock section ở applicationHost (gotcha #25)
- Workflow transition 403 → check `Contract.WorkflowDefinitionId` pin đúng không
- Migration lỗi → 3 file đầy đủ (Designer + Migration + Snapshot)
## Versioned workflow — quick reference
```
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:
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:
- `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
```
admin@solutionerp.local / Admin@123456 ← Admin (QTV)
Demo users — 30 user (User@123456):
── 16 sample @solutionerp.local (giữ cho test legacy) ──
bod.huynh, bod.le Tổng GĐ + Phó GĐ NĐUQ
pm.nguyen GĐ Dự án FLOCK 01 (PM)
ccm.tran, pro.pham, fin.do TPB CCM/PRO/FIN
act.vu, equ.bui, hra.dang Kế toán trưởng / TPB EQU / TPB HRA
qs.hoang, qs.ngo QS công trường (NV.PB)
nv.cao, nv.dinh, nv.truong NV Cung ứng/Tài chính/CCM (NV.PB)
bod.tran, pm.le Bonus NĐUQ + PM thứ 2
── 14 Solutions thật @solutions.com.vn (session 4) ──
PRO 5: tra.bui (TPB) + phuong.nguyen, binh.lethanh, danh.huynh, dat.tran (NV)
CCM 7: ngocanh.huynh (TPB) + ha.dao, cuong.do, long.le, ha.nguyen,
dung.nguyen, anh.nguyen (NV)
ISO 1: chau.le
CEO 1: huy.duong
⚠ Rotate ALL passwords trước UAT thật
```
- API prod: https://api.solutions.com.vn — `/health/live`, `/health/ready`
- Admin FE prod: https://admin.solutions.com.vn
- User FE prod: https://eoffice.solutions.com.vn
- API dev: http://localhost:5443 — `/swagger` (Dev only)
- Admin FE dev: http://localhost:8082
- User FE dev: http://localhost:8080
- SQL dev: `(localdb)\MSSQLLocalDB` / `SolutionErp_Dev`
- SQL prod: `.\SQLEXPRESS` / `SolutionErp` / `vrapp` (⚠ rotate)
## Đánh giá nhanh
**Tốt:**
- 3 domain HTTPS prod live, CI/CD xanh
- Tier 3 feature-complete: attachment, realtime, form builder (upload + DynamicForm + PDF), versioned workflow (admin-configurable per ContractType, pin per contract), nested menu per type, 3-panel permissions
- Clean-arch 3-project split đúng cho 2 cross-cutting service (realtime + document-converter)
- 26 gotchas tích lũy, 8 session log, 40 docs agent onboard nhanh
- Invariant critical: " giữ quy trình " guaranteed by pinning (reference-based immutability, không snapshot copy)
**Rủi ro còn:**
- UAT thật chưa chạy thể phát hiện edge case missing
- SMTP chưa notification chỉ in-app (toast + bell), không email
- User-kind approver chưa enable guard runtime (designer cho pick, nhưng transition dùng Role fall-back)
- Credentials chưa rotate
- SQL backup chưa schedule Task Scheduler