[CLAUDE] Phase3: Workflow MVP — 9-phase state machine + code gen + FE Inbox/Detail
Backend Contracts domain (5 entities):
- Contract aggregate: Phase (9 enum), SlaDeadline, MaHopDong, BypassProcurementAndCCM, DraftData, SlaWarningSent
- ContractApproval: FromPhase → ToPhase, ApproverUserId (null = system auto-approve), Decision, Comment
- ContractComment: thread theo Phase current
- ContractAttachment: FileName + StoragePath + Purpose (DraftExport/ScannedSigned/SealedCopy)
- ContractCodeSequence: Prefix PK + LastSeq — atomic gen
EF configs:
- Unique MaHopDong filtered [MaHopDong] IS NOT NULL
- Indexes: Phase+IsDeleted, SupplierId, ProjectId, SlaDeadline, ContractId+ApprovedAt, ContractId+CreatedAt
- Cascade delete Approvals/Comments/Attachments khi Contract xoa
- Query filter IsDeleted
- Migration AddContractsWorkflow (DB 19 tables)
Workflow service:
- IContractWorkflowService.TransitionAsync:
- Adjacency check qua Transitions Dict<(from,to), roles[]> (12 transitions)
- Role guard: user phai co role ∈ allowed
- Admin bypass (role Admin pass moi check)
- System bypass (userId=null + Decision=AutoApprove → cho SLA job sau nay)
- Bypass CCM: BypassProcurementAndCCM=true cho phep DangInKy → DangTrinhKy skip phase 6
- Gen ma HD khi chuyen DangDongDau (idempotent — khong gen lai neu da co)
- Reset SlaDeadline = UtcNow + PhaseSla
- Insert ContractApproval row
Code generator (RG-001):
- 7 format theo ContractType: HDTP / HDGK / NCC / HDDV / MB + 2 framework (year prefix)
- BeginTransactionAsync(Serializable) + ContractCodeSequences UPSERT → atomic
- Idempotent: neu MaHopDong da co thi skip
CQRS (8 feature, ContractFeatures.cs):
- CreateContractCommand + Validator + Handler (set SlaDeadline = +7d)
- UpdateContractDraftCommand (chi khi Phase=DangSoanThao)
- TransitionContractCommand (delegate → WorkflowService)
- AddCommentCommand (phase = hien tai)
- ListContractsQuery (PagedResult + filter phase/supplier/project/search)
- GetMyInboxQuery (map Phase → actor roles, filter theo role user)
- GetContractQuery (detail + approvals + comments + attachments + resolve user names)
- DeleteContractCommand (soft, block > DangInKy)
Controller:
- ContractsController 8 endpoint: GET list/inbox/detail, POST create/transition/comment, PUT update, DELETE
Frontend fe-admin (2 page moi):
- types/contracts.ts: ContractPhase const + Label + Color maps + types
- components/PhaseBadge.tsx
- pages/contracts/ContractsListPage.tsx: filter phase + search + click → detail
- pages/contracts/ContractDetailPage.tsx: 2-col layout (info+comments | timeline), action dialog select target phase + comment
Frontend fe-user (4 page moi + 14 file shared):
- cp 14 file shared tu fe-admin (menuKeys, types/*, DataTable, PhaseBadge, Dialog, Textarea, Select, apiError, usePermission, PermissionGuard)
- AuthContext update: load menu tu /menus/me + cache
- Layout: menu fixed 3 muc + user info + roles display
- InboxPage: list HD cho role user xu ly (sort theo SLA)
- ContractCreatePage: form chon loai + template + NCC + du an + gia tri + bypass CDT
- ContractDetailPage: duplicate fe-admin pattern (convention)
- MyContractsPage: list HD cua toi
- App.tsx: 4 route moi
E2E verified:
- Setup Supplier + Project
- POST /contracts → 201 + phase=2
- POST /contracts/{id}/transitions x7 → di het 9 phase
- Final: MaHopDong = "FLOCK 01/HĐGK/SOL&PVL2026/01" dung format RG-001
- Approvals: 7 rows audit day du
Docs:
- .claude/skills/contract-workflow/SKILL.md: placeholder → full spec voi state machine, SLA table, role matrix, 7 code format, code pointers, API, E2E workflow, pitfalls
- docs/changelog/sessions/2026-04-21-1330-phase3-workflow.md: session log
- docs/STATUS.md: Phase 3 MVP done, next Phase 4
- docs/HANDOFF.md: update phase status + file tree + commit log + testing points
- docs/changelog/migration-todos.md: tick Phase 3 MVP items + add iteration 2 list
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
109
docs/HANDOFF.md
109
docs/HANDOFF.md
@ -1,6 +1,6 @@
|
||||
# HANDOFF — Brief 5 phút cho session tiếp theo
|
||||
|
||||
**Last updated:** 2026-04-21 12:00 (cuối Phase 2 MVP)
|
||||
**Last updated:** 2026-04-21 13:30 (cuối Phase 3 MVP)
|
||||
|
||||
## Ở đâu rồi?
|
||||
|
||||
@ -9,10 +9,11 @@
|
||||
| 0 Draft | ✅ Done |
|
||||
| 1 Alpha Core foundation | ✅ Done |
|
||||
| 1 Alpha Core đợt 2 (CRUD + Permission) | ✅ Done |
|
||||
| **2 Form Engine MVP** | ✅ Done |
|
||||
| 2 Form Engine MVP | ✅ Done |
|
||||
| 2 Form Engine iteration 2 | 📝 Optional |
|
||||
| 3 Workflow (9 phase state machine) | 📋 Next |
|
||||
| 4 Report + Polish | 📋 Queue |
|
||||
| **3 Workflow MVP (9 phase + code gen)** | ✅ Done |
|
||||
| 3 Workflow iteration 2 (SLA job + notify + attachment) | 📝 Optional |
|
||||
| 4 Report + Polish | 📋 Next |
|
||||
| 5 Production (CI/CD IIS) | 📋 Queue |
|
||||
|
||||
## Run nhanh
|
||||
@ -30,43 +31,48 @@ cd fe-user && npm run dev # → http://localhost:8080
|
||||
|
||||
Login: `admin@solutionerp.local` / `Admin@123456`
|
||||
|
||||
Điểm cần test ngay:
|
||||
- `/forms` → render FO-002.05 → download .docx (Phase 2 MVP)
|
||||
- `/system/permissions` → chọn role → tick matrix
|
||||
- `/master/suppliers|projects|departments` → CRUD
|
||||
Điểm cần test ngay (Phase 3 MVP):
|
||||
- **fe-user `/contracts/new`** → tạo HĐ draft (Phase 2 DangSoanThao, SLA +7d)
|
||||
- **fe-user `/inbox`** → xem HĐ chờ role mình xử lý
|
||||
- **`/contracts/{id}`** (cả 2 FE) → click "Duyệt → tiếp" chạy state machine. Đến phase 8 DangDongDau → xem `MaHopDong` tự gen theo RG-001
|
||||
- **`/forms`** (admin) → render template .docx với JSON data
|
||||
- **`/system/permissions`** → ma trận Role × MenuKey
|
||||
- **`/master/suppliers|projects|departments`** → CRUD
|
||||
|
||||
## Cần làm kế tiếp (ưu tiên)
|
||||
|
||||
### A. Phase 3 — Workflow (item lớn, ~3 tuần work)
|
||||
### A. Phase 4 — Report + Polish (tuần 10-11)
|
||||
|
||||
**Đọc trước:**
|
||||
1. [`workflow-contract.md`](workflow-contract.md) — spec 9 phase + role matrix
|
||||
2. [`flows/contract-approval-flow.md`](flows/contract-approval-flow.md) — sequence diagram
|
||||
3. [`flows/sla-expiry-flow.md`](flows/sla-expiry-flow.md) — BackgroundService auto-approve
|
||||
4. [`forms-spec.md#RG-001`](forms-spec.md) — format mã HĐ
|
||||
- Dashboard admin: HĐ theo phase, top NCC, top dự án, tổng giá trị tháng
|
||||
- Excel export list HĐ (dùng ClosedXML đã có)
|
||||
- Report quá hạn SLA theo phase/role
|
||||
- UX polish: skeleton loader, empty state có action, error boundary recovery
|
||||
- Accessibility: keyboard nav, aria labels
|
||||
- Index DB hot query (SupplierId, ProjectId, Phase combo)
|
||||
- User guide docs
|
||||
|
||||
**Deliverable chính:**
|
||||
- Entity: `Contract` (Phase, SlaDeadline, BypassProcurementAndCCM, DraftData) + `ContractApproval` + `ContractComment` + `ContractAttachment`
|
||||
- `IContractWorkflowService.TransitionAsync()` — state guard (9 phase adjacency) + role guard
|
||||
- `IContractCodeGenerator` (implement RG-001) với transaction SERIALIZABLE + `ContractCodeSequences` table
|
||||
- `SlaExpiryJob` BackgroundService — auto-approve HĐ quá hạn mỗi 15 phút
|
||||
- `INotificationService` — email (MailKit) + in-app
|
||||
- API `POST /api/contracts/{id}/transitions`
|
||||
- FE `/inbox` (list HĐ chờ tôi xử lý theo role × phase)
|
||||
- FE `/contracts/{id}` detail — timeline 9 phase, approval panel, comment thread, attachment upload
|
||||
### B. Phase 3 iteration 2 (polish workflow)
|
||||
|
||||
### B. Phase 2 iteration 2 (nếu user muốn polish Form Engine)
|
||||
- [ ] `SlaExpiryJob` BackgroundService auto-approve (xem `flows/sla-expiry-flow.md`)
|
||||
- [ ] Email notification (MailKit) — pick up phase + SMTP config
|
||||
- [ ] In-app notification (SignalR + badge counter)
|
||||
- [ ] Upload attachment endpoint + FE multipart (store vào `wwwroot/uploads/contracts/{id}/`)
|
||||
- [ ] RowVersion optimistic concurrency (2 user race → 409)
|
||||
- [ ] Render HĐ docx lúc tạo (merge TemplateId + DraftData + ContractClause appendix)
|
||||
|
||||
- Convert 3 file `.doc` (retry Word COM với timeout OR LibreOffice)
|
||||
- Field spec JSON → dynamic form builder
|
||||
- `{{#loop}}...{{/loop}}` support
|
||||
- PDF convert
|
||||
- FE upload template UI
|
||||
### C. Phase 2 iteration 2 (form engine polish)
|
||||
|
||||
### C. Quick wins (không block phase)
|
||||
- Convert 3 file `.doc` qua Word COM `DisplayAlerts=0` + timeout, hoặc LibreOffice
|
||||
- Field spec JSON per template + dynamic form builder FE
|
||||
- `{{#loop}}...{{/loop}}` block support cho table lặp
|
||||
- PDF convert via LibreOffice headless
|
||||
- Admin upload template UI (multipart)
|
||||
|
||||
- FE Users management + Roles CRUD (test permission với non-admin role)
|
||||
- fe-user sync menu động (đang hardcode)
|
||||
### D. Quick wins (không block phase)
|
||||
|
||||
- FE Users management + Roles CRUD (test permission với non-admin user thật)
|
||||
- Filter Inbox theo phase FE
|
||||
- Refresh token auto (FE axios interceptor retry 401)
|
||||
|
||||
## Lưu ý kỹ thuật quan trọng
|
||||
|
||||
@ -85,43 +91,50 @@ SOLUTION_ERP/
|
||||
├── src/Backend/ (Clean Arch, 4 project, .NET 10)
|
||||
│ ├── SolutionErp.Domain/
|
||||
│ │ ├── Common/ BaseEntity, AuditableEntity
|
||||
│ │ ├── Contracts/ ContractType, ContractPhase, ApprovalDecision
|
||||
│ │ ├── Contracts/ ContractType, ContractPhase, ApprovalDecision + **Contract, ContractApproval, ContractComment, ContractAttachment, ContractCodeSequence** ← Phase 3
|
||||
│ │ ├── Forms/ ContractTemplate, ContractClause ← Phase 2
|
||||
│ │ ├── Identity/ User, Role, MenuItem, Permission, AppRoles, MenuKeys
|
||||
│ │ └── Master/ Supplier, Project, Department, SupplierType
|
||||
│ ├── SolutionErp.Application/
|
||||
│ │ ├── Auth/ Login, Refresh, Me
|
||||
│ │ ├── Common/ Exceptions, Behaviors, Interfaces, Models
|
||||
│ │ ├── Contracts/ **ContractFeatures (8 CQRS), IContractWorkflowService, IContractCodeGenerator, DTOs** ← Phase 3
|
||||
│ │ ├── Forms/ FormFeatures (List/Get/Render) ← Phase 2
|
||||
│ │ ├── Master/ Suppliers, Projects, Departments CQRS
|
||||
│ │ └── Permissions/ GetMyMenuTree, matrix upsert
|
||||
│ ├── SolutionErp.Infrastructure/
|
||||
│ │ ├── Forms/ DocxRenderer, XlsxRenderer, FormRenderer ← Phase 2
|
||||
│ │ ├── Identity/ JwtSettings, JwtTokenService
|
||||
│ │ ├── Persistence/ DbContext, DbInitializer, Interceptors, Migrations (4)
|
||||
│ │ └── Services/ DateTimeService
|
||||
│ │ ├── Persistence/ DbContext, DbInitializer, Interceptors, Migrations (**5** now)
|
||||
│ │ └── Services/ DateTimeService, **ContractWorkflowService, ContractCodeGenerator** ← Phase 3
|
||||
│ └── SolutionErp.Api/
|
||||
│ ├── Authorization/ MenuPermissionHandler + Requirement
|
||||
│ ├── Controllers/ Auth, Suppliers, Projects, Departments, Menus, Roles, Permissions, Forms
|
||||
│ ├── Controllers/ Auth, Suppliers, Projects, Departments, Menus, Roles, Permissions, Forms, **Contracts** ← Phase 3
|
||||
│ ├── Middleware/ GlobalExceptionMiddleware
|
||||
│ ├── Services/ CurrentUserService, WebHostEnvironmentLocator
|
||||
│ └── wwwroot/templates/ 5 file .docx/.xlsx ← Phase 2
|
||||
├── fe-admin/ (7 page)
|
||||
├── fe-admin/ (9 page)
|
||||
│ └── src/pages/
|
||||
│ ├── LoginPage
|
||||
│ ├── DashboardPage
|
||||
│ ├── master/SuppliersPage, ProjectsPage, DepartmentsPage
|
||||
│ ├── system/PermissionsPage
|
||||
│ └── forms/FormsPage ← Phase 2
|
||||
├── fe-user/ (2 page — Login + Inbox placeholder)
|
||||
├── docs/ (24 file — STATUS, PROJECT-MAP, workflow, forms-spec, database-guide, 6 flow, gotchas, 4 session log, 1 handoff)
|
||||
└── .claude/skills/ (3 skill — contract-workflow placeholder, form-engine + permission-matrix full spec)
|
||||
│ ├── forms/FormsPage ← Phase 2
|
||||
│ └── contracts/ContractsListPage, ContractDetailPage ← Phase 3
|
||||
├── fe-user/ (5 page)
|
||||
│ └── src/pages/
|
||||
│ ├── LoginPage
|
||||
│ ├── InboxPage ← Phase 3
|
||||
│ └── contracts/ContractCreatePage, ContractDetailPage, MyContractsPage ← Phase 3
|
||||
├── docs/ (26 file — STATUS, PROJECT-MAP, workflow, forms-spec, database-guide, 6 flow, gotchas, HANDOFF, 5 session log)
|
||||
└── .claude/skills/ (3 skill — all full spec: contract-workflow, form-engine, permission-matrix)
|
||||
```
|
||||
|
||||
## Git state
|
||||
|
||||
```
|
||||
49a5f57..(sẽ là commit 5) — Phase 2 MVP
|
||||
(sẽ là commit 6) — Phase 3 Workflow MVP
|
||||
5113e4c — Phase 2 Form Engine MVP
|
||||
54d6c9b — Phase 1.2 CRUD + Permission
|
||||
49a5f57 — Docs database-guide + flows
|
||||
702411f — Phase 1 foundation
|
||||
@ -146,10 +159,14 @@ admin@solutionerp.local / Admin@123456
|
||||
|
||||
**Tốt:**
|
||||
- Build pass 100% cả BE + FE
|
||||
- E2E test: login + CRUD + render template đều pass
|
||||
- Docs đầy đủ: 24 file, có session log mỗi chunk, gotchas library tích lũy
|
||||
- E2E test full 9-phase workflow end-to-end — mã HĐ gen đúng format RG-001
|
||||
- Docs đầy đủ: 26 file, session log mỗi chunk, gotchas tích lũy 17 pitfall
|
||||
- Cả 2 FE đều có Contract detail page + timeline + comment thread + state machine action
|
||||
|
||||
**Rủi ro:**
|
||||
- fe-user còn thô — Phase 3 phải build inbox
|
||||
**Rủi ro còn:**
|
||||
- SLA chỉ set deadline — không có job auto-approve (Phase 3.2)
|
||||
- Không có notification (email + in-app) — user phải F5 inbox
|
||||
- Form render chỉ MVP — loop table + PDF chưa có
|
||||
- Permission matrix chưa test thực với non-admin user
|
||||
- 3 file .doc chưa convert (carryover Phase 2)
|
||||
- Không có upload attachment endpoint (chỉ có entity + DTO)
|
||||
|
||||
Reference in New Issue
Block a user