Backend Report: - Application/Reports/Dtos/DashboardStatsDto: 5 KPI + PhaseCount + SupplierCount + ProjectCount + MonthlyValue - Application/Reports/Queries/GetDashboardStats handler: total/active/overdue/published this month/totalValueActive + byPhase + top 5 NCC/du an + 12 thang monthly (fill zero khi thang empty) - Application/Reports/Services/IContractExcelExporter interface - Infrastructure/Reports/ContractExcelExporter: ClosedXML workbook 10 cot, header style bold+blue, number format #,##0, formula SUM, auto-fit, freeze header - Application/Reports/Commands/ExportContractsToExcelCommand: filter phase/supplier/project/date range - Api/Controllers/ReportsController: GET /reports/dashboard, GET /reports/contracts/export - DI register IContractExcelExporter (Scoped) Frontend fe-admin: - types/reports.ts: DashboardStats type - components/BarChart.tsx: generic horizontal bar chart — chi Tailwind, khong thu vien ngoai - pages/DashboardPage.tsx REWRITE: 5 KPI card (FileText/TrendingUp/AlertTriangle/CheckCircle2/Coins) + by-phase bar + monthly 12-month chart + top 5 NCC + top 5 du an + skeleton loader - pages/ReportsPage.tsx MOI: filter phase/fromDate/toDate → export Excel button - Route /reports vao App.tsx E2E verified: - GET /api/reports/dashboard → 200 voi day du KPI + monthly fill 12 thang - GET /api/reports/contracts/export → 200 xlsx 7229 bytes (Microsoft Excel 2007+) Docs consolidation (theo yeu cau user): - docs/rules.md MOI: 9 section coding conventions (ngon ngu UI/code/DB/docs, BE Clean Arch, CQRS+MediatR, Validation FluentValidation, Error handling, Async, Entity rules, DI, Package pinning, FE React/TS erasableSyntaxOnly, path alias, TanStack Query, Permission guard, Toast+error, DB convention, Git commit format, Docs structure, Testing, Security) - docs/architecture.md MOI: layered overview ASCII art, request lifecycle (1 POST/api/contracts qua 10 step), workflow state machine 9 phase, permission model, data flow sequence diagram 4 actor (Drafter/Manager/CCM/BOD/HRA), deployment architecture Phase 5, skill library, non-functional table - docs/database/schema-diagram.md MOI: full ERD 19 table mermaid + data flow diagram + vong doi 1 HD (create → 7 transition → gen ma → publish) + index strategy table + relationship cardinality + soft delete behavior + SQL queries (inbox/dashboard/gen ma) + migration history - docs/gotchas.md UPDATE: 17 → 26 pitfalls, them section "Claude Code harness quirks" (Edit File not read, DI build pass nhung runtime fail) + "Contract workflow" (ma HD gen 2 lan, BE-FE NEXT_PHASES sync, race condition) + "Permission matrix" (cache real-time, MenuKey typo) - docs/STATUS.md: Phase 4 MVP done, docs entry points section liet ke het, next Phase 5 Production - docs/HANDOFF.md: phase table them Phase 4 row, file tree update voi Reports, test points day du, git state commit 7 - docs/changelog/migration-todos.md: tick Phase 4 MVP items + them iteration 2 list - docs/changelog/sessions/2026-04-21-1430-phase4-report.md: session log voi thong so cumulative (BE 3100 LOC, 30 docs) - CLAUDE.md root: update Tai lieu quan trong section them rules.md, architecture.md, schema-diagram.md, .claude/skills (13 links now) Bug fix: - TS unused import ContractPhaseLabel trong DashboardPage - DI thieu register IContractExcelExporter — build pass but runtime would fail (added) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
12 KiB
Migration To-dos — Atomic Roadmap
Mỗi item là 1 task atomic (~2-8h work). Tick
[x]khi xong. Link session log nếu có.
Phase 0 — Draft Scaffold (T1)
- Tạo cấu trúc thư mục
SOLUTION_ERP/ - Scaffold .NET 10 solution
SolutionErp.slnx - Scaffold 4 project:
SolutionErp.{Domain, Application, Infrastructure, Api} - Wire Clean Arch references (Api → App/Infra, Infra → App, App → Domain)
- Install NuGet base: MediatR, FluentValidation, AutoMapper, EF Core SqlServer, Identity, JWT, Swagger, Serilog
- Scaffold 2 React + Vite apps
fe-admin+fe-uservới TS template - Config vite.config.ts: port, strictPort, proxy
/api, alias@ - Pin Node
>=20trong package.json +.nvmrccho CI - Parse 8 form →
docs/forms-spec.md - Parse quy trình →
docs/workflow-contract.md - Viết
docs/{CLAUDE,STATUS,PROJECT-MAP}.md - Viết
docs/database/database-guide.md(conventions + schema + ERD + migration workflow) - Viết
docs/flows/— README + 6 flow doc (auth, permission, contract-create, contract-approve, form-render, sla-expiry) - Viết
.gitignore,README.md,global.json,docker-compose.yml - Tạo placeholder skill folders:
contract-workflow,form-engine,permission-matrix git init+ commit đầu (25dad7f)- Push Gitea remote (chờ URL từ user)
Phase 1 — Alpha Core (T2-4)
Foundation (đã xong Session 2)
Domain/Common/BaseEntity.cs(Id Guid, CreatedAt, UpdatedAt, CreatedBy, UpdatedBy)Domain/Common/AuditableEntity.cs(IsDeleted, DeletedAt, DeletedBy)Domain/Contracts/Enums:ContractType,ContractPhase(9 state),ApprovalDecisionDomain/Identity/User.cs(IdentityUser + FullName + RefreshToken + IsActive)Domain/Identity/Role.cs(IdentityRole + Description)Domain/Identity/AppRoles.cs— 12 role constantsApplication/Common/Interfaces/: IApplicationDbContext, ICurrentUser, IDateTime, IJwtTokenServiceApplication/Common/Exceptions/*Application/Common/Behaviors/ValidationBehavior.csApplication/DependencyInjection.cs— MediatR + FluentValidationInfrastructure/Persistence/ApplicationDbContext.cs : IdentityDbContextInfrastructure/Persistence/Interceptors/AuditingInterceptor.csInfrastructure/Persistence/DbInitializer.cs— seed 12 role + adminInfrastructure/Persistence/DesignTimeDbContextFactory.csInfrastructure/Identity/{JwtSettings, JwtTokenService}.csInfrastructure/Services/DateTimeService.csInfrastructure/DependencyInjection.csApi/Services/CurrentUserService.csApi/Middleware/GlobalExceptionMiddleware.csApi/Controllers/AuthController.cs(login, refresh, me, logout)Api/Program.cs(Serilog, JWT, CORS, Swagger, middleware)Api/appsettings.{json, Development.json}+launchSettings.json(port 5443)- Migration 1
Init+ apply toSolutionErp_DevLocalDB - FE: Vite config (Tailwind 4 + proxy + alias)
- FE:
src/{index.css, lib/api.ts, lib/cn.ts, types/auth.ts}cho 2 app - FE:
src/contexts/AuthContext.tsx,components/{ProtectedRoute, Layout}.tsx - FE:
components/ui/{Button, Input, Label}.tsx - FE:
pages/LoginPage.tsx,pages/DashboardPage.tsx(admin) +pages/InboxPage.tsx(user) - FE:
App.tsxvới Router + AuthProvider + Toaster - FE:
main.tsxvới QueryClient (TanStack Query) - E2E verified: login qua Vite proxy cả 2 app → JWT + user info
Phase 1 đợt 2 — CRUD master + Permission Matrix (sắp tới)
Domain/Master/Supplier(+ SupplierType enum 5 loại) /Project/Department(AuditableEntity)- EF
IEntityTypeConfiguration<T>cho mỗi entity (unique Code + query filter IsDeleted) - CQRS CRUD: Create/Update/Delete/GetById/List (PagedResult) cho 3 entity
Api/Controllers/{SuppliersController, ProjectsController, DepartmentsController}- Migration 2:
AddMasterData Domain/Identity/MenuItem(Key PK, Label, ParentKey, Order, Icon) +MenuKeysconst classDomain/Identity/Permission(RoleId, MenuKey, CanRead/Create/Update/Delete)- Seed default menu tree (12 menu) + admin full access trong DbInitializer
Application/Permissions/Queries/GetMyMenuTreeQuery— resolve per-user, union OR, tree filterApi/Controllers/{MenusController, RolesController, PermissionsController}- Migration 3:
AddPermissions - Authorization handler
MenuPermissionHandler+ register 48 policy{menu}.{action} Domain/Entities/Contractskeleton (Id, Type, SupplierId, ProjectId, Phase=DangChon, DraftData JSON) — deferred Phase 2/3- Contract CRUD draft only (không workflow Phase 3) — deferred
- FE:
<PermissionGuard menuKey="Suppliers" action="Update">+usePermission()hook - FE Admin: 3 trang CRUD Supplier/Project/Department với DataTable + Dialog modal + search/sort/paging
- FE Admin: Permission Matrix grid page (role × menu × CRUD checkbox)
- FE Admin: Layout menu động từ
/api/menus/me - FE User: trang "HĐ của tôi" list + filter — Phase 3
- FE Admin: Users management page (tạo user + gán role) — sắp tới
- FE Admin: Roles CRUD — sắp tới
- Route guard theo role admin-only — có PermissionGuard ở button, route cần thêm
Exit criteria Phase 1
- Admin login → tạo NCC/Project → tạo role "Nhân viên CCM" → gán permission menu "Contracts.Read"
- User CCM login → thấy menu Contracts, không thấy menu Admin
- Tạo Contract draft → list hiển thị, không bị 403 sai
Phase 2 — Form Engine (T5-6)
MVP xong (Phase 2 iteration 1)
- Khảo sát: chọn OpenXml + ClosedXML (free, không cần license)
Domain/Forms/ContractTemplate(Id, FormCode, Name, ContractType, FileName, StoragePath, Format, FieldSpec JSON, IsActive)Domain/Forms/ContractClauseskeleton- EF config + Migration
AddForms Application/Forms/Services/IFormRendererinterfaceInfrastructure/Forms/DocxRenderer(OpenXml, handle placeholder split runs)Infrastructure/Forms/XlsxRenderer(ClosedXML)Application/Forms/FormFeatures.cs— List/Get/Render CQRSApi/Controllers/FormsController— GET templates, GET single, POST render- Copy 5 .docx/.xlsx template →
wwwroot/templates/ - Seed 8 ContractTemplate rows (5 IsActive=true, 3 chờ convert)
- FE admin:
FormsPage— list + render dialog điền JSON + download - E2E verified: render FO-002.05 → file .docx 482KB mở được bằng Word
Iteration 2 (optional — enhance)
- Convert 3 file
.doc→.docx(retry Word COM vớiDisplayAlerts=0+ timeout, hoặc LibreOffice headless) - Parse chi tiết field của 5 template HĐ — mỗi form thành JSON
FieldSpec - Support
{{#loop}}...{{/loop}}block cho table lặp (hạng mục HĐ giao khoán, PO) - FE user: form builder dynamic — render từ fieldSpec thay vì điền JSON tay
- FE admin: upload template mới qua UI (POST multipart) + edit field mapping
- Lưu
ContractClause(FO-002.04) dạng rich text, admin edit qua TipTap/TinyMCE - PDF convert via LibreOffice headless (
soffice --headless --convert-to pdf) - Import/export template (backup/restore)
- Format helpers: number →
150,000,000 VND, date →dd/MM/yyyy - Content preservation test: render → diff layout với template gốc
Phase 3 — Workflow State Machine (T7-9)
MVP xong (iteration 1)
Domain/Contracts/Contract(Phase, SlaDeadline, BypassProcurementAndCCM, MaHopDong, DraftData, SlaWarningSent)Domain/Contracts/ContractApproval(FromPhase, ToPhase, ApproverUserId, Decision, Comment)Domain/Contracts/ContractComment+ContractAttachment(+ AttachmentPurpose enum)Domain/Contracts/ContractCodeSequence(Prefix PK, LastSeq)- EF config + unique MaHopDong filtered + indexes Phase/Supplier/Project/SlaDeadline + cascade delete
- DbSets (5) +
IApplicationDbContextupdate - Migration
AddContractsWorkflow Application/Contracts/Services/IContractWorkflowService+IContractCodeGeneratorInfrastructure/Services/ContractWorkflowService— adjacency 9 phase + role guard + Admin bypass + system actor + bypass CCM (Chủ đầu tư)Infrastructure/Services/ContractCodeGenerator— 7 format RG-001 + transaction SERIALIZABLE- CQRS: Create/UpdateDraft/Transition/AddComment/List/Inbox/GetDetail/Delete (8 feature)
Api/Controllers/ContractsController— 8 endpoint REST- FE admin: ContractsListPage + ContractDetailPage (timeline + action dialog)
- FE user: InboxPage + ContractCreatePage + ContractDetailPage + MyContractsPage
- PhaseBadge component + color map
- E2E verified: tạo HĐ → chạy 9 phase → gen mã
FLOCK 01/HĐGK/SOL&PVL2026/01
Iteration 2 (polish — optional)
Infrastructure/HostedServices/SlaExpiryJob— check mỗi 15min, auto-approve quá hạn với Decision=AutoApprove- Warning notification khi còn 20% SLA
Infrastructure/Services/NotificationService— email (MailKit) + in-app- SignalR hub cho real-time notification badge
- MediatR
AuditBehavior— log mọi command (ngoài ContractApprovals) - Upload attachment endpoint (multipart) + FE upload UI (
wwwroot/uploads/contracts/{id}/) - RowVersion optimistic concurrency (2 user race → 409)
- Render HĐ docx lúc tạo (merge TemplateId + DraftData + ContractClause appendix)
- E2E test với non-admin user (Drafter/CCM/BOD role)
- Filter Inbox theo phase ở FE
- E2E test: reject → quay về DangSoanThao
- E2E test: SLA expired → auto-approve + log
Phase 4 — Reporting + Polish (T10-11)
MVP xong (iteration 1)
- Dashboard admin: 5 KPI (total/active/overdue/published this month/total value) + by phase + top 5 NCC + top 5 dự án + 12 tháng
- Excel export HĐ theo filter (phase/supplier/project/date range) qua ClosedXML
- BE
GetDashboardStatsQuery+ExportContractsToExcelCommand+ ReportsController - FE
DashboardPagerewrite vớiBarCharttự build (Tailwind only, không thư viện ngoài) - FE
ReportsPagefilter + export - Docs consolidation:
rules.md+architecture.md+database/schema-diagram.md+ gotchas update
Iteration 2 (polish — optional)
- SLA overdue report (by role / phase, export Excel)
- Contract audit log export (từng HĐ ra PDF)
- Dashboard user-specific (HĐ của tôi / role của tôi)
- Chart library recharts (nếu cần chart phức tạp)
- UX polish: skeleton loader cho mọi list, empty state có action, error boundary recovery
- Accessibility: keyboard nav, focus trap modal, aria labels
- Dark mode
- Performance: explicit index DB cho query hot đã identify
- Tài liệu user guide: quy trình tạo HĐ + duyệt
- UAT với 5-10 HĐ dữ liệu thật từ bộ phận Cung ứng
Phase 5 — Production (T12-13)
docs/guides/cicd.md— CI/CD runbook- Gitea Actions workflow
.gitea/workflows/deploy.yml— build .NET + 2 FE, deploy IIS qua SSH/WinRM - Pin Node 20.x trong workflow, test CI sớm (không để surprise cuối dự án)
scripts/deploy-iis.ps1— stop app pool, xcopy, start app pool- Windows Server setup: IIS + URL Rewrite + ARR (reverse proxy FE → IIS)
- SQL Server prod: backup plan daily + weekly full
- HTTPS certificate (Let's Encrypt qua win-acme hoặc mua cert)
appsettings.Production.json+ user secrets- Security audit: owasp top 10 check
- Rate limiting middleware (AspNetCoreRateLimit hoặc built-in)
- Health check endpoint
/healthcho IIS probe - Error tracking: Serilog → file rolling daily, retention 30 ngày
- Runbook: restart app, rollback migration, restore backup
- UAT production 1 tuần với 2-3 user thật
- Go-live checklist: backup, rollback plan, on-call contact
Post-launch (Phase 6+ — future)
- E-signature integration (VNPT CA hoặc FPT CA)
- Tích hợp Bravo / SAP ERP import NCC
- Mobile app (React Native?) cho BOD duyệt ngoài giờ
- AI: gợi ý điền form dựa HĐ cũ, OCR scan HĐ đối tác
- Multi-tenant nếu có công ty thứ 2