Files
solution-erp/docs/HANDOFF.md
pqhuy1987 b874743081
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m21s
[CLAUDE] Docs+Tests: chốt final session 5 — 77 test (Phase 3 mini PE WF) + 3 gotcha CI + 8 doc updates
Final close session 5 — bao gồm:

==== Tests Phase 3 mini (NEW) ====
tests/SolutionErp.Infrastructure.Tests/Application/PeWorkflowAdminTests.cs
- 6 test CreatePeWorkflowDefinitionCommandHandler:
  - First version → IsActive=true, Version=1, ActivatedAt set
  - Second version same Code → auto-increment v2 + deactivate v1 (atomic)
  - Different EvaluationType (A vs B) → independent active state
  - Persists steps ordered by Order field
  - Persists approvers per step
  - Third version → v1 + v2 deactivate, v3 active

Total tests: 71 → 77 pass / ~2s (54 Domain + 23 Infra).
Skip Phase 3 full (UpsertOpinion + Budget link validation) — cần
Identity UserManager DI helper, defer session sau.

==== 3 gotcha CI mới (#39 #40 #41) ====
- #39 act_runner github.com TCP timeout 21s → manual checkout fix (run #108/#109 fail, #110 pass)
- #40 npm junction cache `tsc not found` after Move-Item — rolled back, hypothesis nested junctions
  trong node_modules disrupt .bin/ paths. TODO debug session sau với robocopy hoặc act_runner cache.host
- #41 Gitea Actions paths-ignore behavior — workflow file change vẫn trigger (correct), commit
  MD-only skip 100% (verify 512880c → no run #113)
+ Checklist debug bug mới items 18-20 referencing 3 gotcha trên.

==== Doc updates (8 file) ====
- STATUS.md: header Phase 8 update + 3 row Recently Done CI fixes + cumulative test 71→77
- HANDOFF.md: TL;DR + CI optimize section + Phase status + gotcha count 38→41
- migration-todos.md: Phase 8 §E updated với Phase 3 mini done + CI fixes
- rules.md §7 Testing: rewrite full — stack + test pyramid + quy tắc bổ sung mỗi feature +
  workflow user end-of-task (`dotnet test` local trước push) + CI gate behavior
- architecture.md §11: update test pyramid + phased priority + CI optimization sub-section
  (3 fix manual checkout / path filter / npm cache rollback) + tốc độ deploy table
- gotchas.md: + #39 #40 #41 đầy đủ (triệu chứng + nguyên nhân + fix + reference)
- ef-core-migration SKILL: Phase 8 update note thêm CI fixes + 77 test
- CLAUDE.md root: test count 71→77 + folder structure + CI/CD pipeline 3 fix section
- memory project_solution_erp.md: session 5 summary + workflow user mới
- session log 2026-04-29-2300-chot-final-ci-tests-gotchas.md (NEW — 9 section detail)

==== Skill audit cron ====
`solution-erp-skill-audit-monthly` next fire 2026-05-01 (2 ngày sau).
Cron survives across sessions (setup commit b904a25). Khi fire sẽ:
- Cross-check 6 skill với STATUS/gotchas/migration-todos
- Auto-refresh stale + đề xuất add/archive cho human approve
- Log vào docs/changelog/skill-audit-2026-05.md
- ABORT nếu repo dirty

==== Verify ====
- dotnet test SolutionErp.slnx → 77 pass / ~2s (54 Domain + 23 Infra)
- git status clean sau commit này
- CI: commit này chứa code (test + workflow) → trigger CI test gate

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 23:43:42 +07:00

460 lines
26 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-29 tối (Phase 8 — **Budget complete + PE feature gap đóng + Tests Phase 1-2-3mini (77 test) + CI gate + Path filter + 3 gotcha CI mới (#39 #40 #41)**)
## TL;DR
**Session 5 (29/04)** đóng gần hết feature gap + bật test gate:
### 🎯 Headline outcomes
-**Budget feature-complete** — FE 3-panel pages cả 2 app, BE đã sẵn từ session 4
-**PE/HD ↔ Budget integration** — form select Budget filter Phase=DaDuyet + Project, cột "So với ngân sách" ở PE matrix với delta indicator
-**PE Detail UI restructure** match form chính thức PHIẾU TRÌNH KÝ (4 section đánh số)
-**PE Workflow Designer admin UI** `/system/pe-workflows/:typeCode` versioned với clone/edit/+Role/+User
-**Ý kiến 4 phòng ban** — migration 15 + section sign-off 2x2 grid (Phê duyệt/CCM/MuaHàng/SM-PM)
-**Tests Phase 1-2** — 71 unit test pass + CI gate fail-fast (Domain policy + Infra code generator)
**Session 4 (28/04)** đã có:
### A. Module Ngân sách (Budget) BE — migration 14, +4 bảng, +11 endpoint
- 4 entity: `Budget` (Header) + `BudgetDetail` (flat row) + `BudgetApproval` (history) + `BudgetChangelog` (audit log).
- Enum `BudgetPhase` 5 state (DangSoanThao→ChoCCM→ChoCEO→DaDuyet + TuChoi).
- `BudgetPolicy.Default` hardcoded simple 3-step (Drafter→CCM→CEO) — chưa versioned (TODO khi user cần admin config UI).
-`NS-YYYYMM-XXXX` Random.Shared (chưa atomic — TODO khi format chính thức).
- Link nullable: `Contract.BudgetId?` + `PE.BudgetId?` đã có FK + index, FE chưa wire form.
- Menu seed `Budgets` root + 3 leaf (Bg_List/Bg_Create/Bg_Pending) order=27 icon Wallet.
- Application: 11 CQRS handler ~340 LOC (Create/UpdateDraft/Transition/List/GetDetail/Delete + Detail CRUD auto-recompute TongNganSach + ListChangelogs).
- Api: `BudgetsController` 11 endpoint REST.
- **FE chưa làm — Priority 0 session 5.**
### B. 14 demo user Solutions thật
- PRO 5 (TPB tra.bui + 4 NV) + CCM 7 (TPB ngocanh.huynh + 6 NV) + ISO 1 (chau.le) + CEO 1 (huy.duong).
- Pwd `User@123456`. Reconcile pattern (gotcha #38 4-field rename).
- Tổng 30 user (16 sample cũ giữ + 14 Solutions thật mới).
**Tổng cumulative:** 52 DB tables, ~128 endpoints, 15 migrations, **41 gotchas (+3 CI fixes)**,
**77 unit test (+6 PE WF Application)**, 11+ commit session 5 push lên Gitea.
### 🆕 CI/CD optimize (29/04 tối)
-**Manual checkout bypass github.com** — fix #108/#109 transient TCP timeout 21s. Run #110 pass.
-**Path filter docs-only skip**`paths-ignore` trong on:push, commit MD-only KHÔNG trigger CI (verify `512880c` → no run #113).
- ⏸️ **npm junction cache** — thử ở #111 fail `tsc not found`, rollback. Cần debug session sau (gotcha #40 doc rồi).
-**Tests Phase 3 mini (PE Workflow Designer)** — 6 test versioning logic. Total 77.
## ⚠️ CẢNH BÁO session tiếp (Session 6)
1. **CI test gate active** — mỗi commit push trigger 2 step `dotnet test` (Domain + Infrastructure) TRƯỚC build/deploy. Test fail = NO deploy. Khi feature mới có bug logic → thêm regression test trước khi commit. Workflow: code → `dotnet test SolutionErp.slnx` local → commit → push.
2. **Login email** `admin@solutionerp.local` / `Admin@123456` — domain default
chưa đổi sang `@solutions.com.vn` (chỉ demo user rebrand qua BackfillDemoEmail).
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. **Export phiếu PDF/Excel PE** — pending vô thời hạn (user nói không quan trọng lắm). Khi cần: tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx`.
6. **Atomic sequence Budget chưa chốt**`NS-YYYYMM-XXXX` Random.Shared. Khi format chốt → migration `AddBudgetCodeSequences` + `IBudgetCodeGenerator` SERIALIZABLE pattern (mirror Contract/PE).
7. **Versioned workflow Budget chưa có** — hardcoded `BudgetPolicy.Default` 3-step. Khi user cần admin config UI → migration `AddBudgetVersionedWorkflow` + 3 bảng + pin per Budget.
8. **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 + 30 user** — 4 bảng + 11 endpoint + workflow simple | ✅ Done (S4) |
| **Module Ngân sách FE** — 3-panel pages + Detail tabs + Workflow Panel cả 2 app | ✅ Done (S5) |
| **PE/Contract → Budget integration** — form Select + cột "So với ngân sách" PE matrix | ✅ Done (S5) |
| **PE Workflow designer admin UI** `/system/pe-workflows/:typeCode` | ✅ Done (S5) |
| **Ý kiến 4 phòng ban PE** — migration 15 + section sign-off 2x2 grid | ✅ Done (S5) |
| **Tests Phase 1-2 + CI gate** — 71 test (Domain policy + Infra code generator) | ✅ Done (S5) |
| **CI manual checkout bypass github.com + Path filter docs-only skip** | ✅ Done (S5) |
| **Tests Phase 3 mini** — 6 test PE Workflow Designer versioning (total 77) | ✅ Done (S5) |
| **Export phiếu PDF/Excel PE**`IDocumentConverter` + template | 📝 Pending (không quan trọng) |
| **Tests Phase 3 full** — Application handlers (UpsertOpinion + Budget link validation, cần UserManager setup) | 📝 Pending |
| **Tests Phase 4-5** — API smoke + FE Vitest | 📝 Pending (làm khi cần) |
| **npm cache CI optimize** (debug junction Move-Item issue #40) | 📝 Pending |
| 9+ Post-launch (E-signature, Bravo/SAP, Mobile, AI) | 📝 Future |
## Run nhanh
```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 — Session 6 (Ops + UAT focus)
Đa số feature gap đã đóng. Còn lại chủ yếu Ops/UAT + optional polish.
**A. Hard blockers (chờ user / ops):**
1. **UAT thật 1 tuần với 2-3 user** — Drafter (QS/NV.PB) + CCM + BOD ghi bug/friction
2. **SMTP config** → bật Email outbox (BLOCKED chờ user cấp host/user/pass)
3. **Rotate credentials** — admin + 30 demo + SA + vrapp + JWT secret
4. **Schedule SQL backup daily**`scripts/backup-sql.ps1` chưa schedule Task Scheduler
**B. PE feature gap còn lại:**
- **Export phiếu PDF/Excel** PE — pending (user nói không quan trọng lắm). Khi cần: tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx`.
**C. Optional polish (làm khi UAT phát sinh):**
- Budget MaNganSach atomic sequence — chốt format → migration `AddBudgetCodeSequences` mirror Contract/PE
- Budget versioned workflow — admin config UI thay hardcoded `BudgetPolicy.Default`
- Payment terms PE tách field — JSON blob → 6 column riêng
- Auto-map PE Details → Contract per-type Details khi gen HĐ
- Matrix Quotes bulk paste từ Excel
- fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi"
**D. Tests Phase 3-5 (làm khi gặp bug recurring để justify ROI):**
- Phase 3 — Application handler tests (CQRS + EF InMemory) ~15 test
- Phase 4 — API smoke tests (WebApplicationFactory) ~7 test
- Phase 5 — FE Vitest cho lib utility (queryMatches, fmtMoney) ~10 test
**Đã xong session 5 (check STATUS Recently Done):**
- ~~Module Ngân sách FE — 3-panel pages cả 2 app~~ ✅
- ~~PE/Contract → Budget integration + cột "So với ngân sách"~~ ✅
- ~~PE Detail UI restructure 4 section đánh số~~ ✅
- ~~PE Workflow Designer admin UI~~ ✅
- ~~Ý kiến 4 phòng ban (migration 15)~~ ✅
- ~~Tests Phase 1-2 + CI gate (71 test)~~ ✅
### A. Hard blockers (chờ user / ops)
1. **UAT thật 1 tuần với 2-3 user** — hard requirement từ roadmap. Kiến nghị:
- User A: Drafter (QS/NV.PB) — tạo 3 HĐ mỗi type, đi hết 9 phase
- User B: CCM — duyệt phase 6
- User C: BOD — duyệt phase 7
- Ghi bug / friction / đề xuất → backlog iter 2
2. **SMTP config** để bật Email outbox:
```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) (41 bẫy) trước khi:**
- Thêm package mới → .NET 10 compat (MediatR 14 fail → dùng 12.4.1)
- Debug TS enum error → dùng const-object pattern (`erasableSyntaxOnly`)
- Expression tree lỗi → tách switch ra ngoài LINQ
- Deploy Windows Feature (WebSockets, etc.) → unlock section ở applicationHost (gotcha #25)
- Workflow transition 403 → check `Contract.WorkflowDefinitionId` pin đúng không
- Migration lỗi → 3 file đầy đủ (Designer + Migration + Snapshot)
## Versioned workflow — quick 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