All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
- rules.md §9 mới: liệt kê 6 skill (3 domain + 3 ops) với trigger, nguyên tắc tạo skill project-specific (không clone generic), format SKILL.md bắt buộc, audit workflow §9.4 chi tiết 7 bước, 4 anti-patterns - CLAUDE.md (root): block "🛠️ Skills" callout 6 skill + audit cadence + commit scope thêm `Skill` - HANDOFF.md: section A1 — định kỳ audit, lần kế tiếp 2026-05-01 - migration-todos: section "Skill governance (recurring)" với checkbox audit hàng tháng Cron task tạo qua scheduled-tasks (ID: solution-erp-skill-audit- monthly): chạy 9:00 AM ngày 1 mỗi tháng. Self-contained prompt cold- start để session tự audit + log vào docs/changelog/skill-audit- {YYYY-MM}.md. Auto-refresh stale skill nhỏ, đề xuất add/archive cho human approve. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15 KiB
15 KiB
HANDOFF — Brief 5 phút cho session tiếp theo
Last updated: 2026-04-22 03:00 (post-Tier-3-feature-complete + versioned workflow)
TL;DR
Tier 3 ERP features xong hết (Attachment, SignalR, Form builder, PDF, Versioned workflow, Nested menu, Permission layout). Prod live 3 domain. 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.
Ở đâ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 |
| 6+ Post-launch (E-signature, Bravo/SAP, Mobile, AI) | 📝 Future |
Run nhanh
# 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)
- 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
- SMTP config để bật Email outbox:
Khi có → thêm
"Email": { "Host": "smtp.gmail.com", "Port": 587, "Username": "...", "Password": "...", "From": "noreply@solutionerp.local" }MailKit,IEmailSender, hook vàoNotificationService.CreateAsyncngay trước khi enqueue realtime push. - Rotate credentials — SA SQL password, vrapp password, JWT secret prod, Gitea runner registration token
- 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:
- Cross-check 6 skill hiện có với STATUS / gotchas / migration-todos
- Check repo nguồn 3rd party (alirezarezvani/claude-skills) có gì mới
- Update / archive / add skill nếu cần
- 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:// 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.Readcho 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 (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.WorkflowDefinitionIdpin đú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.WorkflowDefinitionIdKHÔ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
91b2da1 — PermissionsPage 3-panel layout (LATEST)
f216169 — Admin Workflows tabs → sidebar menu items
355bbe3 — Fix Dialog size TS (xl → lg)
e7e5f2d — Versioned workflow entities + migration + designer
4 session trước đó nằm trong STATUS table
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