Files
solution-erp/docs/changelog/migration-todos.md
pqhuy1987 c48ac2116d [CLAUDE] PurchaseEvaluation: demo seed 4 phieu + MaPhieu atomic sequence + Pe_* perm defaults
Polish session tiep cua PE module skeleton (commit 2c6f0ca..3990066):
3 task A (MISSING in MVP) khac STATUS.md In Progress:

1. Demo PE data seed (SeedDemoPurchaseEvaluationsAsync)
   - 4 phieu varied A/B x phase: A-001 DangSoanThao (mo), A-002
     ChoCEODuyetNCC (winner+9 quotes), A-003 DaDuyet (chua tao HD,
     PaymentTerms JSON), B-001 ChoDuAn (5-step giua chung).
   - Idempotent: skip-if-[DEMO]-exists.
   - Approval history dung policy A (3-step) hoac B (5-step).

2. MaPhieu atomic sequence — Migration 13
   - Format PE/{YYYY}/{TypeLetter}/{Seq:D3} (vd PE/2026/A/001).
   - PurchaseEvaluationCodeSequence entity (Prefix PK).
   - IPurchaseEvaluationCodeGenerator + impl SERIALIZABLE
     transaction (mirror ContractCodeGenerator 1:1).
   - Replace Random.Shared trong CreatePurchaseEvaluationCommandHandler.
   - Migration AddPurchaseEvaluationCodeSequences (1 bang).

3. Pe_* permission defaults
   - SeedPurchaseEvaluationPermissionDefaultsAsync — 7 role business x 9 menu key.
   - Drafter/DeptManager/Procurement: R+C+U; CostControl/PM/Director/AuthorizedSigner: R+U.
   - DeptManager them Delete (xoa nhap).
   - Idempotent per-(roleId x menuKey).

Build: 0 error, 2 warning (pre-existing DocxRenderer).

Files: 4 new + 8 modified (1 migration + entity + generator + DI + 2 ctx + 2 features).

Resolves: STATUS.md In Progress §A — 3 item PE MISSING.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:41:17 +07:00

387 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

# 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)
- [x] Tạo cấu trúc thư mục `SOLUTION_ERP/`
- [x] Scaffold .NET 10 solution `SolutionErp.slnx`
- [x] Scaffold 4 project: `SolutionErp.{Domain, Application, Infrastructure, Api}`
- [x] Wire Clean Arch references (Api → App/Infra, Infra → App, App → Domain)
- [x] Install NuGet base: MediatR, FluentValidation, AutoMapper, EF Core SqlServer, Identity, JWT, Swagger, Serilog
- [x] Scaffold 2 React + Vite apps `fe-admin` + `fe-user` với TS template
- [x] Config vite.config.ts: port, strictPort, proxy `/api`, alias `@`
- [x] Pin Node `>=20` trong package.json + `.nvmrc` cho CI
- [x] Parse 8 form → `docs/forms-spec.md`
- [x] Parse quy trình → `docs/workflow-contract.md`
- [x] Viết `docs/{CLAUDE,STATUS,PROJECT-MAP}.md`
- [x] Viết `docs/database/database-guide.md` (conventions + schema + ERD + migration workflow)
- [x] Viết `docs/flows/` — README + 6 flow doc (auth, permission, contract-create, contract-approve, form-render, sla-expiry)
- [x] Viết `.gitignore`, `README.md`, `global.json`, `docker-compose.yml`
- [x] Tạo placeholder skill folders: `contract-workflow`, `form-engine`, `permission-matrix`
- [x] `git init` + commit đầu (`25dad7f`)
- [ ] Push Gitea remote (chờ URL từ user)
## Phase 1 — Alpha Core (T2-4)
### Foundation (đã xong Session 2)
- [x] `Domain/Common/BaseEntity.cs` (Id Guid, CreatedAt, UpdatedAt, CreatedBy, UpdatedBy)
- [x] `Domain/Common/AuditableEntity.cs` (IsDeleted, DeletedAt, DeletedBy)
- [x] `Domain/Contracts/` Enums: `ContractType`, `ContractPhase` (9 state), `ApprovalDecision`
- [x] `Domain/Identity/User.cs` (IdentityUser<Guid> + FullName + RefreshToken + IsActive)
- [x] `Domain/Identity/Role.cs` (IdentityRole<Guid> + Description)
- [x] `Domain/Identity/AppRoles.cs` — 12 role constants
- [x] `Application/Common/Interfaces/`: IApplicationDbContext, ICurrentUser, IDateTime, IJwtTokenService
- [x] `Application/Common/Exceptions/*`
- [x] `Application/Common/Behaviors/ValidationBehavior.cs`
- [x] `Application/DependencyInjection.cs` — MediatR + FluentValidation
- [x] `Infrastructure/Persistence/ApplicationDbContext.cs : IdentityDbContext`
- [x] `Infrastructure/Persistence/Interceptors/AuditingInterceptor.cs`
- [x] `Infrastructure/Persistence/DbInitializer.cs` — seed 12 role + admin
- [x] `Infrastructure/Persistence/DesignTimeDbContextFactory.cs`
- [x] `Infrastructure/Identity/{JwtSettings, JwtTokenService}.cs`
- [x] `Infrastructure/Services/DateTimeService.cs`
- [x] `Infrastructure/DependencyInjection.cs`
- [x] `Api/Services/CurrentUserService.cs`
- [x] `Api/Middleware/GlobalExceptionMiddleware.cs`
- [x] `Api/Controllers/AuthController.cs` (login, refresh, me, logout)
- [x] `Api/Program.cs` (Serilog, JWT, CORS, Swagger, middleware)
- [x] `Api/appsettings.{json, Development.json}` + `launchSettings.json` (port 5443)
- [x] Migration 1 `Init` + apply to `SolutionErp_Dev` LocalDB
- [x] FE: Vite config (Tailwind 4 + proxy + alias)
- [x] FE: `src/{index.css, lib/api.ts, lib/cn.ts, types/auth.ts}` cho 2 app
- [x] FE: `src/contexts/AuthContext.tsx`, `components/{ProtectedRoute, Layout}.tsx`
- [x] FE: `components/ui/{Button, Input, Label}.tsx`
- [x] FE: `pages/LoginPage.tsx`, `pages/DashboardPage.tsx` (admin) + `pages/InboxPage.tsx` (user)
- [x] FE: `App.tsx` với Router + AuthProvider + Toaster
- [x] FE: `main.tsx` với QueryClient (TanStack Query)
- [x] E2E verified: login qua Vite proxy cả 2 app → JWT + user info
### Phase 1 đợt 2 — CRUD master + Permission Matrix
- [x] `Domain/Master/Supplier` (+ SupplierType enum 5 loại) / `Project` / `Department` (AuditableEntity)
- [x] EF `IEntityTypeConfiguration<T>` cho mỗi entity (unique Code + query filter IsDeleted)
- [x] CQRS CRUD: Create/Update/Delete/GetById/List (PagedResult) cho 3 entity
- [x] `Api/Controllers/{SuppliersController, ProjectsController, DepartmentsController}`
- [x] Migration 2: `AddMasterData`
- [x] `Domain/Identity/MenuItem` (Key PK, Label, ParentKey, Order, Icon) + `MenuKeys` const class
- [x] `Domain/Identity/Permission` (RoleId, MenuKey, CanRead/Create/Update/Delete)
- [x] Seed default menu tree + admin full access trong DbInitializer (mở rộng Tier 3: 28 Ct_* + 7 Wf_*)
- [x] `Application/Permissions/Queries/GetMyMenuTreeQuery` — resolve per-user + inherit Contracts/Workflows root
- [x] `Api/Controllers/{MenusController, RolesController, PermissionsController}`
- [x] Migration 3: `AddPermissions`
- [x] Authorization handler `MenuPermissionHandler` + register policy `{menu}.{action}`
- [x] FE: `<PermissionGuard menuKey="Suppliers" action="Update">` + `usePermission()` hook
- [x] FE Admin: 3 trang CRUD Supplier/Project/Department với DataTable + Dialog + search/sort/paging
- [x] FE Admin: Permission Matrix grid page (role × menu × CRUD checkbox) — iter 1 + 3-panel iter 2
- [x] FE Admin: Layout menu động từ `/api/menus/me` + recursive nested + filterForAdmin
- [x] FE User: trang "HĐ của tôi" list + filter `?type=X` — Tier 3
- [x] FE Admin: Users management page (tạo user + gán role + reset password + unlock)
- [x] FE Admin: Roles CRUD `/system/roles` — admin thêm custom role ngoài 12 hardcoded (commit 072ad6d)
- [x] Route guard theo role admin-only — PermissionGuard ở button level
### Exit criteria Phase 1
- [x] Admin login → tạo NCC/Project → gán permission menu
- [x] User non-admin login → thấy menu theo role, không bị 403
- [x] Tạo Contract draft → list hiển thị, filter role-aware
## Phase 2 — Form Engine (T5-6)
### MVP xong (Phase 2 iteration 1)
- [x] Khảo sát: chọn **OpenXml + ClosedXML** (free, không cần license)
- [x] `Domain/Forms/ContractTemplate` (Id, FormCode, Name, ContractType, FileName, StoragePath, Format, FieldSpec JSON, IsActive)
- [x] `Domain/Forms/ContractClause` skeleton
- [x] EF config + Migration `AddForms`
- [x] `Application/Forms/Services/IFormRenderer` interface
- [x] `Infrastructure/Forms/DocxRenderer` (OpenXml, handle placeholder split runs)
- [x] `Infrastructure/Forms/XlsxRenderer` (ClosedXML)
- [x] `Application/Forms/FormFeatures.cs` — List/Get/Render CQRS
- [x] `Api/Controllers/FormsController` — GET templates, GET single, POST render
- [x] Copy 5 .docx/.xlsx template → `wwwroot/templates/`
- [x] Seed 8 ContractTemplate rows (5 IsActive=true, 3 chờ convert)
- [x] FE admin: `FormsPage` — list + render dialog điền JSON + download
- [x] E2E verified: render FO-002.05 → file .docx 482KB mở được bằng Word
### Iteration 2 (Tier 3 — đã làm)
- [x] Convert `.doc``.docx` / `.xls``.xlsx` qua `IDocumentConverter` + LibreOffice headless (thay Word COM, auto-convert khi admin upload)
- [x] FE user: form builder dynamic — `DynamicForm` component render từ `FieldSpec` JSON (text/textarea/number/date/currency/select)
- [x] FE admin: upload template mới qua UI (POST multipart) + edit FieldSpec + delete (soft via IsActive)
- [x] PDF convert via LibreOffice headless (`soffice --headless --convert-to pdf`) — `LibreOfficeDocumentConverter` (timeout + per-request temp + isolated UserInstallation)
- [x] Format helpers: number → `VND`, date → `dd/MM/yyyy` (render layer)
- [ ] Support `{{#loop}}...{{/loop}}` block cho table lặp (hạng mục HĐ giao khoán, PO) — optional
- [ ] Lưu `ContractClause` (FO-002.04) dạng rich text + TipTap/TinyMCE editor — optional
- [ ] Import/export template (backup/restore) — optional
- [ ] Content preservation test (render → diff layout) — optional
## Phase 3 — Workflow State Machine (T7-9)
### MVP xong (iteration 1)
- [x] `Domain/Contracts/Contract` (Phase, SlaDeadline, BypassProcurementAndCCM, MaHopDong, DraftData, SlaWarningSent)
- [x] `Domain/Contracts/ContractApproval` (FromPhase, ToPhase, ApproverUserId, Decision, Comment)
- [x] `Domain/Contracts/ContractComment` + `ContractAttachment` (+ AttachmentPurpose enum)
- [x] `Domain/Contracts/ContractCodeSequence` (Prefix PK, LastSeq)
- [x] EF config + unique MaHopDong filtered + indexes Phase/Supplier/Project/SlaDeadline + cascade delete
- [x] DbSets (5) + `IApplicationDbContext` update
- [x] Migration `AddContractsWorkflow`
- [x] `Application/Contracts/Services/IContractWorkflowService` + `IContractCodeGenerator`
- [x] `Infrastructure/Services/ContractWorkflowService` — adjacency 9 phase + role guard + Admin bypass + system actor + bypass CCM (Chủ đầu tư)
- [x] `Infrastructure/Services/ContractCodeGenerator` — 7 format RG-001 + transaction SERIALIZABLE
- [x] CQRS: Create/UpdateDraft/Transition/AddComment/List/Inbox/GetDetail/Delete (8 feature)
- [x] `Api/Controllers/ContractsController` — 8 endpoint REST
- [x] FE admin: ContractsListPage + ContractDetailPage (timeline + action dialog)
- [x] FE user: InboxPage + ContractCreatePage + ContractDetailPage + MyContractsPage
- [x] PhaseBadge component + color map
- [x] E2E verified: tạo HĐ → chạy 9 phase → gen mã `FLOCK 01/HĐGK/SOL&PVL2026/01`
### Iteration 2 (polish — Tier 3 + Notification)
- [x] `Infrastructure/HostedServices/SlaExpiryJob` — check mỗi 15min, auto-approve quá hạn với Decision=AutoApprove (+30s delay startup)
- [x] E2E test với non-admin user (Drafter role) — IDOR filter verified
- [x] Admin password warning log khi vẫn dùng default
- [x] `Infrastructure/Services/NotificationService` — in-app + emit (email đợi SMTP)
- [x] SignalR hub cho real-time notification badge — `/hubs/notifications` + interceptor auto-push
- [x] Upload attachment endpoint (multipart) + FE drag-drop UI (`wwwroot/uploads/contracts/{id}/`) — IFileStorage + path-traversal guard
- [x] Filter Inbox theo type ở FE (`?type=X`)
- [x] Render HĐ template docx/xlsx → PDF export (LibreOffice)
- [ ] Warning notification khi còn 20% SLA — `SlaWarningSent` flag đã có
- [ ] MediatR `AuditBehavior` — log mọi command (ngoài ContractApprovals)
- [ ] RowVersion optimistic concurrency (2 user race → 409)
- [ ] Render HĐ docx lúc tạo (merge TemplateId + DraftData + ContractClause appendix)
- [ ] E2E test: reject → quay về DangSoanThao với multi-role
- [ ] Email notification (MailKit + SMTP) — blocked chờ user config
### Iteration 3 (Versioned workflow — Tier 3)
- [x] `Domain/Contracts/WorkflowDefinition` (Code + Version + IsActive + ContractType + Description)
- [x] `Domain/Contracts/WorkflowStep` (Order + Phase + Name + SlaDays)
- [x] `Domain/Contracts/WorkflowStepApprover` (Kind: Role|User + AssignmentValue)
- [x] `Contract.WorkflowDefinitionId` nullable FK pin tại create time
- [x] Migration `AddVersionedWorkflows` + seed v01 cho 7 ContractType
- [x] `WorkflowPolicyRegistry.FromDefinition()` — runtime policy build từ DB
- [x] `ContractWorkflowService` — load pinned def → FromDefinition → guard
- [x] `WorkflowAdminFeatures` — GetOverview + CreateNewVersion (auto-increment Version + deactivate old)
- [x] FE admin `/system/workflows/:typeCode` — DefinitionCard + history + Designer modal
- [x] Designer: Steps repeatable, per-step phase/name/SLA, +Role / +User approver select
- [x] Clone-from-version button cho starting point
- [x] Invariants: UNIQUE (Code, Version), 1 IsActive per ContractType, no cascade FK
- [x] E2E: create QT-MB-v02 → v01 archived → HĐ mới pin v02 → HĐ cũ pin v01 giữ nguyên
- [ ] Runtime enable User-kind approver trong TransitionAsync guard (data model ready)
## Phase 4 — Reporting + Polish (T10-11)
### MVP xong (iteration 1)
- [x] Dashboard admin: 5 KPI (total/active/overdue/published this month/total value) + by phase + top 5 NCC + top 5 dự án + 12 tháng
- [x] Excel export HĐ theo filter (phase/supplier/project/date range) qua ClosedXML
- [x] BE `GetDashboardStatsQuery` + `ExportContractsToExcelCommand` + ReportsController
- [x] FE `DashboardPage` rewrite với `BarChart` tự build (Tailwind only, không thư viện ngoài)
- [x] FE `ReportsPage` filter + export
- [x] Docs consolidation: `rules.md` + `architecture.md` + `database/schema-diagram.md` + gotchas update
### Iteration 2 (Tier 3 + optional)
- [x] Dashboard user-specific (`MyDashboard` endpoint — DraftsInProgress / PendingMyApproval / DueSoon / Overdue / DraftsTotalValue) + FE "Của tôi" row 4 card
- [x] UX polish: skeleton loader DataTable, empty state có action, error boundary recovery
- [x] Content polish: typography 14px + leading 1.55 + tracking-tight + PageHeader + Button + Input + DataTable
- [x] Brand identity: #1F7DC1 palette + Be Vietnam Pro font + Solutions logo
- [ ] SLA overdue report (by role / phase, export Excel)
- [ ] Contract audit log export (từng HĐ ra PDF)
- [ ] Chart library recharts (nếu cần chart phức tạp)
- [ ] 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)
### Prep xong (code + scripts + docs)
- [x] `docs/guides/cicd.md` — CI/CD runbook
- [x] Gitea Actions workflow `.gitea/workflows/deploy.yml` — build .NET + 2 FE, deploy IIS qua WinRM
- [x] Pin Node `.nvmrc` 20 (gotcha #5)
- [x] `scripts/deploy-iis.ps1` — stop pool → backup → xcopy → start → health check
- [x] `scripts/backup-sql.ps1` — daily BACKUP DATABASE + COMPRESSION + retention 30d
- [x] `appsettings.Production.json` template + user secrets pattern
- [x] Rate limiting (built-in .NET 10) — auth-login 5/min + global 300/min
- [x] Health check `/health/live` + `/health/ready` (DB probe)
- [x] Serilog File sink rolling daily retention 30d
- [x] HSTS production (1 year)
- [x] `docs/guides/deployment-iis.md` — setup lần đầu + troubleshooting
- [x] `docs/guides/security-checklist.md` — OWASP top 10
- [x] `docs/guides/runbook.md` — operations (restart, rollback, restore)
- [x] FE refresh token auto interceptor (queue pattern cả 2 app)
### Deploy thật
- [x] Windows Server setup: IIS + URL Rewrite + ARR (reverse proxy FE → IIS)
- [x] SQL Server prod (SQLEXPRESS) + vrapp db_owner
- [x] HTTPS certificate (Let's Encrypt qua win-acme — 3 cert + auto-renew)
- [x] Gitea remote setup + push all commits
- [x] Set Gitea Actions secrets (JWT_SECRET, DB_CONNECTION — deploy local via runner)
- [x] Enable Gitea runner (Windows self-hosted, shared với VIETREPORT)
- [x] Test CI/CD workflow — xanh E2E, /health/live 200 sau deploy
- [ ] **UAT production 1 tuần với 2-3 user thật** ← hard blocker còn lại
- [ ] SQL Task Scheduler trigger backup-sql.ps1 (script có sẵn, chưa schedule)
- [ ] Go-live checklist: rotate creds + backup plan + on-call contact
### Phase 5.1 Security hardening + Users Mgmt
- [x] Security headers middleware (X-Content-Type-Options, X-Frame-Options, CSP, Referrer-Policy, Permissions-Policy)
- [x] Identity account lockout (5 fail → 15min, config-driven)
- [x] Password policy config-driven (default 8 dev, override `Identity:Password:RequiredLength` prod)
- [x] LoginCommand: check IsLockedOutAsync + AccessFailedAsync + reset on success
- [x] BE Users management: CQRS 8 feature + UsersController 7 endpoint (Users.Read/Create/Update policies)
- [x] FE admin `/system/users`: list + create + assign roles + reset password + unlock + toggle active
- [x] IDOR check ContractsController — user Drafter chỉ xem HĐ mình tạo hoặc role eligible phase (`ListContractsQueryHandler` + `GetContractQueryHandler`)
- [x] Admin mặc định warning log (`DbInitializer.WarnDefaultAdminPasswordAsync`)
- [ ] Dependencies scan vào CI (`dotnet list package --vulnerable --include-transitive`, `npm audit --audit-level=high`)
- [x] BE Roles CRUD (Create/UpdateLabels/Delete custom role) + FE `/system/roles` — Name FK không đổi, ShortName+Description editable (commit 072ad6d)
## Skill governance (recurring)
**Quy tắc:** xem `docs/rules.md §9`. Audit định kỳ mỗi đầu tháng — workflow §9.4.
- [x] **Setup ban đầu** — 6 skill (3 domain + 3 ops), rules §9, HANDOFF section A1 ← `661f859`+
- [ ] **Audit 2026-05-01** — log `docs/changelog/skill-audit-2026-05.md`
- [ ] **Audit 2026-06-01**
- [ ] **Audit 2026-07-01**
- [ ] (lập lại mỗi tháng đầu)
## Session 2026-04-23 (tối) — RolesPage + Demo data + Clear pending tasks
- [x] **RolesPage CRUD** `/system/roles` — 12 hardcoded + custom (`072ad6d`)
- [x] **Seed 7 demo HĐ** varied phases + details + comments + approvals (`8bc9565`)
- [x] **User-kind approver runtime guard** trong TransitionAsync (`4edcd58`)
- [x] **Warning 20% SLA** notification trong SlaExpiryJob (`4edcd58`)
- [x] **Edit detail row inline** — 7 typed UpdateXxxDetail + EditRowDialog (`e53cd3a`)
- [x] **Deps audit helper** `scripts/deps-audit.ps1` (`e53cd3a`)
- [x] **Master expand** 5→15 NCC + 3→8 Project (per-Code idempotent) (`bcdc007`)
- [x] **Backfill demo HĐ** supplier+project diverse theo loại (`bcdc007`)
## Session 2026-04-23 (chiều) — Toolkit + 4-bảng + Master Catalogs + Roles VN
- [x] **3-panel layout HĐ list/inbox/thao tác** (commits b75448e/89c7e88/8c4b4da)
- [x] **Sidebar accordion** menu loại HĐ (`7ea3957`)
- [x] **Tách Tổng quan → /dashboard riêng** fix bug trùng /inbox (`d326e80`)
- [x] **4-bảng overhaul** Header/7 Details/Workflow/Changelog (Migration 9, `70810e1`)
- [x] **IChangelogService** + integrate vào CRUD/Workflow handlers (`71c035d`)
- [x] **Details CRUD endpoints + Changelogs query** (`e684455`)
- [x] **FE Panel 2 7/3 grid** (Chi tiết + Lịch sử inline, `ad0652d`)
- [x] **Edit/Xóa hover row Panel 1 Thao tác** (mờ khi != DangSoanThao, `7f26ff9`)
- [x] **Mã HĐ gen ngay tại Create + backfill legacy** (`51449d6`)
- [x] **4 master catalogs** Units/Materials/Services/WorkItems (Migration 10, `e27c547`)
- [x] **Admin CatalogsPage** + datalist autocomplete trong Details form (`16e24ed`)
- [x] **Roles VN labels** ShortName + Description backfill (Migration 11, `330d529`)
- [x] **Users +DepartmentId/Position** + 13 demo users seed (`330d529`)
- [x] **FE UsersPage** dept dropdown + position field + role badge ShortName (`ae59cfe`)
## Tier 3 ERP (Session 2026-04-22) — feature-complete
- [x] **Attachment upload E2E** — IFileStorage + CQRS + FE drag-drop (gotcha path-traversal) — `c8d0070`
- [x] **SignalR realtime notifications** — 3-project clean-arch split + JWT `?access_token=` + auto-reconnect — `ea9ab5e`
- [x] **Form template builder CRUD** — Upload/Update/Delete + FieldSpec JSON editor — `166d26c`
- [x] **PDF export + DynamicForm + .doc auto-convert** — LibreOffice headless per-request temp — `6bbd894` + `e459097`
- [x] **Dynamic workflow policy** — Standard/SkipCcm registry theo ContractType — `cae4d84`
- [x] **Versioned workflow** — WorkflowDefinition + Steps + Approvers pinned per Contract — `e7e5f2d`
- [x] **Admin workflow designer** — per-type page + Designer modal + clone — `e7e5f2d`
- [x] **Nested sidebar menu fe-user** — 7 type × 3 action + admin/user split — `5e0f380`
- [x] **Workflows tabs → sidebar menu** — 7 Wf_ leaves + URL-driven — `f216169`
- [x] **PermissionsPage 3-panel layout** — Role list | Menu×CRUD | Granted stats — `91b2da1`
- [x] **Seed master data** — 9 dept + 5 supplier + 3 project + MyDashboard — `6197c84`
- [x] **Brand identity**#1F7DC1 palette + Be Vietnam Pro + Solutions logo — `4abb559`..`bf1fbe3`
## Phase 6 — Module Duyệt NCC (tiền-HĐ) ✅ Done
- [x] **Migration 12 AddPurchaseEvaluations** — 10 bảng: PurchaseEvaluations + Suppliers + Details + Quotes + Approvals + Changelogs + Attachments + WorkflowDefinitions + WorkflowSteps + WorkflowStepApprovers
- [x] **Domain** — 2 enum (Type A/B, Phase 7 state) + Policy record + Registry + FromDefinition builder (mirror WorkflowPolicyRegistry HĐ)
- [x] **Seed** — 13 menu Pe_*/PeWf_* + 2 WorkflowDefinition v01 (QT-DN-A 3-step, QT-DN-B 5-step)
- [x] **Application CQRS** — ~900 dòng: Create/Update/Transition/List/Inbox/Get/Delete + Supplier CRUD + Detail CRUD + Quote Upsert + SelectWinner + Changelog
- [x] **PurchaseEvaluationWorkflowService** — policy-based guard + approval + notification + changelog
- [x] **PurchaseEvaluationsController** — 17 endpoint REST
- [x] **FE 2 app** — Types + PurchaseEvaluationsListPage 3-panel + PurchaseEvaluationCreatePage + PeDetailTabs (5 tab) + PeWorkflowPanel + Menu resolver Pe_*
- [x] **Kế thừa HĐ** — CreateContractFromEvaluationCommand guard DaDuyet + SelectedSupplier + !ContractId → gen Contract draft kế thừa Supplier/Project/GiaTri/DraftData. FE CreateContractDialog pick ContractType 7 loại. Link 2 chiều PE.ContractId.
- [ ] **PE Workflow admin designer UI**`/system/pe-workflows/:typeCode` (framework đã sẵn, mirror `/system/workflows/:typeCode`) — optional
- [ ] **PE Attachments upload** (pattern reuse ContractAttachmentFeatures) — optional
- [ ] **Auto-map PE Details → Contract Details per-type** khi gen HĐ — phức tạp vì 7 ContractType schema khác nhau, user điền lại manual — optional
## Phase 7 — PE refinement (session tiếp)
**User feedback (2026-04-23 tối session 2):** "phần Duyệt NCC chưa xong đâu đấy nhé, còn chỉnh nhiều"
### A. Chức năng MISSING trong MVP
- [ ] **PE Workflow admin designer UI** `/system/pe-workflows/:typeCode`
- BE `Application/PurchaseEvaluations/PeWorkflowAdminFeatures.cs` — mirror `WorkflowAdminFeatures.cs` (GetOverview + CreateNewVersion)
- `Api/Controllers/PeWorkflowsController.cs` — GET overview + POST create-version (`[Authorize(Policy = "PeWorkflows.Create")]`)
- FE `fe-admin/src/pages/system/PeWorkflowsPage.tsx` — URL-driven, landing + per-type
- FE `fe-admin/src/components/workflow/PeWorkflowDesigner.tsx` — Steps + Approvers modal
- Route `/system/pe-workflows/:typeCode` trong App.tsx (menu `PeWf_*` + resolver đã sẵn)
- [ ] **PE Attachments upload** — pattern copy từ `ContractAttachmentFeatures.cs`
- BE `Application/PurchaseEvaluations/PurchaseEvaluationAttachmentFeatures.cs` (Upload/Download/Delete)
- 3 endpoint POST/GET/DELETE `/api/purchase-evaluations/{id}/attachments`
- FE `components/pe/PeAttachmentsSection.tsx` — drag-drop với purpose selector
- [ ] **Ý kiến 4 phòng ban** (Phê duyệt / P.CCM / P.MuaHàng / SM-PM từ Excel form)
- Option A: Thêm 4 text field (NhậnXétPheDuyet/CCM/MuaHang/SmPm) + 4 date field + 4 UserId vào `PurchaseEvaluations` header
- Option B: Dùng existing `PurchaseEvaluationApprovals` với roleKind extra field
- UX: tab Thông tin hiển thị 4 box sign-off như Excel
- [ ] **Payment terms tách field** từ JSON → 6 columns hoặc subtable
- Migration `AddPurchaseEvaluationPaymentTerms` — cột/bảng: TamUng%, ThanhToanTam%, QuyetToan%, BaoHanh%, HanMucCongNo, DanhGia
- FE form field-based (loại bỏ JSON textarea)
- [ ] **Auto-map PE Details → Contract Details khi gen HĐ** (optional — nâng cấp)
- 7 mapping function per ContractType (khó vì schema khác biệt)
- MVP: skip, user nhập manual
- [x]**Demo PE data seed**`SeedDemoPurchaseEvaluationsAsync` (2026-04-24)
- A-001 DangSoanThao (Drafter mới mở, chưa có quotes)
- A-002 ChoCEODuyetNCC (winner đề xuất + 9 quotes 3×3 grid)
- A-003 DaDuyet (chưa tạo HĐ — showcase kế thừa button) + PaymentTerms JSON
- B-001 ChoDuAn (5-step giữa chừng)
- Idempotent: skip-if-`[DEMO]` exists
- [x]**MaPhieu format chính thức + atomic sequence** (2026-04-24)
- Format: `PE/{YYYY}/{TypeLetter}/{Seq:D3}` (TypeLetter = A | B)
- `PurchaseEvaluationCodeSequences` bảng mới (Prefix PK, mirror ContractCodeSequences)
- `IPurchaseEvaluationCodeGenerator` + impl SERIALIZABLE transaction
- Migration 13 `AddPurchaseEvaluationCodeSequences` (1 bảng)
- Wired vào CreatePurchaseEvaluationCommandHandler thay Random.Shared
### B. UX / Polish
- [ ] Matrix Quotes **bulk paste** từ Excel column giá → paste matrix row
- [ ] **Export phiếu PDF/Excel** — tái dùng `IDocumentConverter` + template `PE-TrinhDuyet.docx` upload qua FormsPage
- [x]**Permission grant Pe_* cho non-admin role** (2026-04-24)
- `SeedPurchaseEvaluationPermissionDefaultsAsync` — 7 role × 9 menu key
- Drafter/DeptManager/Procurement: R+C+U
- CostControl/PM/Director/AuthorizedSigner: R+U
- DeptManager thêm Delete (xóa nháp)
- Idempotent per-(roleId × menuKey), admin tinh chỉnh tiếp qua /system/permissions
- [ ] **fe-user Inbox** thêm section "Phiếu Duyệt NCC chờ tôi" (hoặc route `/pe-inbox`)
- [ ] **Sidebar accordion fe-user** extend cover `Pe_<code>` (hiện chỉ track `Ct_<code>`)
- [ ] **Dashboard** — thêm KPI "PE phiếu chờ tôi", "PE đã duyệt tháng này", "PE cần tạo HĐ"
### C. Edge case + E2E test
- [ ] Reject path E2E: DangSoanThao → ChoPurchasing → (CCM reject) → DangSoanThao
- [ ] Admin tạo PE workflow v02 sau khi có phiếu pin v01 — invariant test
- [ ] Xóa phiếu khi PE.ContractId set — warning dialog
- [ ] 2 admin đồng thời tạo v02 cho cùng type — race condition (atomic dealsactivate old)
### D. Deploy
- [ ] Verify commit `3990066` apply prod (runner status + `/api/purchase-evaluations` → 401 expected)
## Post-launch (Phase 8+ — future)
- [ ] **Email outbox** (MailKit + SMTP) — blocked chờ SMTP config
- [x] **Roles CRUD** — admin tạo custom role ngoài 12 hardcoded (commit 072ad6d)
- [ ] **User-kind approver runtime** — data model có, guard cần wire
- [ ] 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