User feedback: "phần Duyệt NCC chưa xong đâu đấy nhé, còn chỉnh nhiều" → mark PE module skeleton (not feature-complete), liệt kê chi tiết chức năng/UX/edge-case còn missing cho session tiếp. Update 7 file: - STATUS.md — phase = "PE skeleton + refinement WIP", In Progress liệt kê 4 nhóm: A Chức năng MISSING (9 item), B UX/Polish (6 item), C Edge case (4 item), D Deploy/Ops (1 item). +G-084 row Recently Done. - HANDOFF.md — TL;DR "PE skeleton, còn chỉnh nhiều" + Priority 0 section cho session tiếp (9 task PE refinement) + cảnh báo runner + G-084. - migration-todos.md — Phase 7 checklist (A/B/C/D nhóm) trước Phase 8 post-launch. Pending migrations: PaymentTermFields + DepartmentOpinions + CodeSequences. - architecture.md — Section 9 PurchaseEvaluation module (ERD + workflow A/B + kế thừa HĐ flow). - CLAUDE.md (root) — 5 file đọc đầu (thêm HANDOFF), Modules table, 12 migration 46 bảng, +PurchaseEvaluation commit scope. - .claude/skills/ — 4 skill cross-ref Phase 6: * README: trạng thái updated với Phase 6 note * contract-workflow: note PE workflow tách table riêng * permission-matrix: +Pe_*/PeWf_* menu keys + TODO grant non-admin * ef-core-migration: 12 migration history + Phase 7 pending - docs/changelog/sessions/2026-04-23-2359-chot-session-pe-skeleton.md — session log full commits + MD files updated + session tiếp priorities + notes (PE là skeleton, runner check, G-084 rule, MaPhieu format).
21 KiB
HANDOFF — Brief 5 phút cho session tiếp theo
Last updated: 2026-04-23 tối (Phase 6 — PE skeleton + G-084 hardening, PE còn chỉnh nhiều)
TL;DR
PE module skeleton E2E — 2 quy trình A/B config được admin, 10 bảng, 17
endpoint, 3 page × 2 app + kế thừa HĐ 1-click. BE + FE + migration đã push
lên Gitea main (2c6f0ca..3990066, 6 commit). **CHƯA XONG — nhiều polish
- feature thiếu cho session tiếp** (xem STATUS.md §🔥 In Progress đầy đủ).
G-084 hardening: localhost → 127.0.0.1 trong scripts + skill, doc gotcha #33 từ bài học VietReport (IPv4/IPv6 port hijack trên VPS shared).
Tổng: 46 DB tables, ~110 endpoints, 12 migrations, 33 gotchas. Blocker user/ops cũ vẫn còn: UAT thật + SMTP + rotate creds + runner apply.
⚠️ CẢNH BÁO session tiếp
- Runner có thể đang stopped sau VPS restart →
/api/purchase-evaluationsvẫn 404. Kiểm tra + startgitea-runnerservice trước khi test PE endpoint. - PE chỉ là skeleton — user nói "còn chỉnh nhiều". Nhóm A/B/C/D tasks đầy đủ ở STATUS.md §🔥 In Progress.
- Chú ý G-084: VPS shared với VietReport — mọi reverse proxy / backend
service mới phải dùng
127.0.0.1+ bind loopback IPv4 explicit.
⭐ 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 |
| 3-panel List/Inbox/Thao tác + sidebar accordion + UserDashboard | ✅ Done |
| 4-bảng data model (Header + 7 Details + Workflow + Changelog audit) | ✅ Done |
| Mã HĐ gen tại Create + backfill legacy | ✅ Done |
| 4 master catalogs (Units/Materials/Services/WorkItems) + datalist autocomplete | ✅ Done |
| Roles VN labels (ShortName) + Users dept/position + 13 demo users | ✅ Done |
RolesPage CRUD /system/roles + custom role admin |
✅ Done |
| 7 demo HĐ varied phases + details + comments + approvals workflow | ✅ Done |
| User-kind approver runtime guard + Warning 20% SLA | ✅ Done |
| Edit detail row inline (7 typed Update commands + EditRowDialog) | ✅ Done |
| Master expand 15 NCC + 8 Project + backfill demo HĐ diverse | ✅ Done |
Deps audit script (scripts/deps-audit.ps1) |
✅ Done |
| Module Duyệt NCC (tiền-HĐ) E2E — 10 bảng + 2 workflow + Kế thừa HĐ | ✅ 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
🔥 Priority 0 — PE module refinement (session tiếp)
Xem STATUS.md §🔥 In Progress đầy đủ (nhóm A/B/C/D). Tóm tắt nhanh:
- PE Workflow admin designer UI
/system/pe-workflows/:typeCode— mirror pattern từWorkflowsPage.tsx+WorkflowDesigner.tsx. BE cầnPeWorkflowAdminFeatures.cs+PeWorkflowsController.cs. Framework backend đã sẵn, chỉ thiếu wire UI. - PE Attachments upload — copy pattern
ContractAttachmentFeatures.cs+ContractAttachmentsSection.tsx. Entity + enum có sẵn. - Ý kiến 4 phòng ban (Phê duyệt / P.CCM / P.MuaHàng / SM-PM) — Excel form có, entity chưa map. Cần design: 4 text field + signoff date, hoặc dùng Approvals với role-kind.
- Payment terms tách field từ JSON blob → 6 field riêng (Tạm ứng / TT tạm / Quyết toán / Bảo hành / Hạn mức / Đánh giá) theo Excel section D.
- Seed demo PE data — 2-3 phiếu cho UAT + screenshot.
- Permission grant Pe_ cho non-admin role* —
/system/permissionstickPurchaseEvaluations.Read+ CRUD cho role Drafter/PRO/CCM/BOD. - MaPhieu format chính thức — user confirm.
- Export phiếu PDF/Excel — tái dùng
IDocumentConverter+ template. - Auto-map PE Details → Contract Details khi gen HĐ (optional — nâng cấp).
Deploy blocker hiện tại: commit 3990066 chưa apply prod. Check runner:
Get-Service *gitea-runner* ; Start-Service gitea-runner
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
bcdc007 — Infra: Mở rộng seed master (15 NCC + 8 Project) + backfill demo HĐ (LATEST)
e53cd3a — App+Api+FE+Scripts: Edit detail row inline + deps audit helper
4edcd58 — Domain+Infra: User-kind approver runtime guard + Warning 20% SLA
8bc9565 — Infra: SeedDemoContractsAsync — 7 HĐ varied phases UAT-ready
072ad6d — App+Api+FE-Admin: RolesPage CRUD (/system/roles)
ff5e35f — Docs: chốt session 2026-04-23 chiều
ae59cfe — FE-Admin: UsersPage dept/position + RoleShortName VN
330d529 — Domain+App+Infra: Role ShortName + User Dept/Position + 13 demo users (migration 11)
39031ca — FE: ContractDetailsPreview cho create mode
16e24ed — FE: Admin CatalogsPage CRUD + Details datalist autocomplete
e27c547 — Domain+App+Api: 4 master catalogs cho Details (migration 10)
51449d6 — App+Infra: Mã HĐ gen ngay tại CreateContract + backfill legacy
7f26ff9 — Edit/Xóa luôn hiện, mờ khi != DangSoanThao
8c4b4da — 2-panel layout cho Thao tác
ad0652d — Bỏ tabs, Chi tiết+Lịch sử 7/3 grid
b3762af — Panel 2 tabs (Tổng quan/Chi tiết/Lịch sử)
e684455 — App+Api: 7 Details CRUD endpoints + Changelogs query
71c035d — App+Infra: IChangelogService
70810e1 — Domain+Infra: 7 Details + ContractChangelog (migration 9)
d326e80 — FE-User: tách Tổng quan thành /dashboard riêng
89c7e88 — FE-User: 3-panel InboxPage
b75448e — 3-panel layout cho danh sách HĐ
7ea3957 — Sidebar accordion menu loại HĐ
d43d2c0 — Docs: chốt session 2026-04-23 — skill governance
Branch: main (tracking origin/main)
Remote: https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp.git
Credentials + URLs
admin@solutionerp.local / Admin@123456 ← Admin (QTV)
Demo users (User@123456):
bod.huynh@solutionerp.local Tổng GĐ (BOD)
bod.le@solutionerp.local Phó GĐ NĐUQ
pm.nguyen@solutionerp.local GĐ Dự án FLOCK 01 (PM)
ccm.tran@solutionerp.local TPB Kiểm soát chi phí (CCM + TPB)
pro.pham@solutionerp.local TPB Cung ứng (PRO + TPB)
fin.do@solutionerp.local TPB Tài chính (FIN + TPB)
act.vu@solutionerp.local Kế toán trưởng (ACT + TPB)
equ.bui@solutionerp.local TPB Thiết bị (EQU + TPB)
hra.dang@solutionerp.local TPB HRA (HRA + TPB)
qs.hoang, qs.ngo QS công trường (NV.PB)
nv.cao, nv.dinh NV Cung ứng/Tài chính (NV.PB)
⚠ Rotate ALL passwords trước UAT thật
- 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