# HANDOFF — Brief 5 phút cho session tiếp theo

**Last updated:** 2026-04-23 09:30 (post-skill-governance + cron audit định kỳ)

## TL;DR

Tier 3 ERP feature-complete (prod 3 domain live). **Skill governance setup xong** — 6 skill project-level (3 domain + 3 ops) + cron audit 1/tháng (next 2026-05-01). **Còn lại chủ yếu là UAT + SMTP + rotate creds**, không còn module kỹ thuật lớn nào chưa làm.

## ⭐ 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 |
| 6+ 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

### 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) (26 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
b904a25 — Skill: governance + audit định kỳ hàng tháng (LATEST)
661f859 — Skill: thêm 3 skill ops project-specific
fbca832 — Docs: chốt session Tier 3 feature-complete + versioned workflow
91b2da1 — FE-Admin: PermissionsPage 3-panel layout
f216169 — FE-Admin+Domain+Infra+App: Workflows tab → sidebar menu items
355bbe3 — Fix Dialog size TS (xl → lg)
e7e5f2d — Versioned workflow entities + migration + designer

Branch: main (tracking origin/main)
Remote: https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp.git
```

## Credentials + URLs

```
admin@solutionerp.local / Admin@123456
```

- API prod: https://api.huypham.vn — `/health/live`, `/health/ready`
- Admin FE prod: https://admin.huypham.vn
- User FE prod: https://user.huypham.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: "HĐ cũ giữ quy trình cũ" guaranteed by pinning (reference-based immutability, không snapshot copy)

**Rủi ro còn:**
- UAT thật chưa chạy → có thể phát hiện edge case missing
- SMTP chưa có → 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
