Files
solution-erp/docs/HANDOFF.md
pqhuy1987 e65578a821
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m55s
[CLAUDE] Docs: chot session 3 — PE polish iter 2 + domain rebrand + 5 gotcha moi
User request: 'Chot lai toan bo MD de sang session moi'.

Session 3 (2026-04-24) — ~15 commit feat/fix PE module + domain migration:
 - Domain 3 subdomain huypham.vn → solutions.com.vn E2E live
 - PE rename 'Phuong An' → 'Giai phap' + backfill DB
 - Menu tree inheritance extend Pe_*/PeWf_*
 - Accordion mutex Pe_* + sidebar w-72 + label nowrap
 - NavLink queryMatches (fix 2 leaf cung highlight)
 - PE detail flat layout: Panel 2 = 4 section, Panel 3 + approvals/history
 - Upload file dinh kem per-NCC (SupplierAttachmentsCell) + Bang so sanh tong
 - readOnly mode menu 'Duyet' (pendingMe=1)
 - HD move Lich su dieu chinh → Panel 3
 - Demo email rebrand @solutionerp.local → @solutions.com.vn + BackfillUserEmailDomain

Docs updated (6 file):
 - STATUS.md: +9 row Recently Done session 3. In Progress tick 10+ done. Phase
   hien tai = 'UX polish hoan thien, UAT-ready'.
 - HANDOFF.md: TL;DR session 3 summary. Priority 0 = 3 task MISSING cuoi
   (Designer UI, Y kien 4 phong ban, Export PDF). Login email moi.
 - gotchas.md: +5 entry (#34 NavLink query, #35 menu inheritance extend,
   #36 Vite env rebuild, #37 PS 5.1 ASCII, #38 Identity rename 4 field) +
   checklist debug +5 entry.
 - ef-core-migration SKILL: migration 13 AddPurchaseEvaluationCodeSequences
   + Phase 6 update section (ComparisonTable enum + BackfillUserEmail).
 - skills/README: ef-core-migration 13 migration label updated.
 - docs/changelog/sessions/2026-04-24-chot-session-3-pe-polish.md: session log
   15 commit + bugs + stats + next priorities session 4.

Memory project_solution_erp.md: Phase 6 iter 2 DONE. Domain rebrand DONE.
Session 4 priority 3 PE gap remaining.

Stats: 47 DB tables (+1 MaPhieu seq), ~113 endpoint (+3 PE attachments),
13 migrations, 38 gotchas, ~85 commits total.
2026-04-25 00:37:30 +07:00

22 KiB
Raw Blame History

HANDOFF — Brief 5 phút cho session tiếp theo

Last updated: 2026-04-24 chiều (Phase 6 — PE polish iter 2 + domain rebrand hoàn tất, UAT-ready)

TL;DR

PE module UX polish gần complete. Session 3 (24/04) apply 10 commit fix tất cả UX friction user báo:

  • Rename menu "Phương Án" → "Giải pháp"
  • Menu tree inheritance Pe_/PeWf_ (fix bug children không hiện)
  • Accordion mutex 2 PE group + sidebar w-72 nowrap label
  • NavLink active check query string (fix 2 leaf cùng highlight)
  • PE detail flat layout: Panel 2 = 4 section (Thông tin/NCC/Hạng mục/Bảng so sánh), Panel 3 thêm Duyệt + Lịch sử thay đổi
  • Upload file đính kèm per-NCC (SupplierAttachmentsCell) + Bảng so sánh tổng
  • readOnly mode cho menu "Duyệt" (pendingMe=1)
  • HĐ: move Lịch sử điều chỉnh Panel 2 → Panel 3
  • Demo email rebrand @solutionerp.local → @solutions.com.vn với backfill

Domain migration (session 2): 3 subdomain .huypham.vn.solutions.com.vn live E2E — api/admin/eoffice.solutions.com.vn. Cert Let's Encrypt + CORS + FE bundle VITE_API_BASE_URL đều đã apply.

Tổng: 47 DB tables, ~113 endpoints, 13 migrations, 33 gotchas, 20+ commit session 3 push lên Gitea.

⚠️ CẢNH BÁO session tiếp (Session 4)

  1. Chưa xóa binding cũ .huypham.vn — vẫn active fallback. Sau 1-2 ngày verify stable → .\migrate-domains.ps1 -RemoveOld -SkipCert trên VPS.
  2. win-acme scheduled task "unhealthy" — cert auto-renew có thể fail khi gần 2026-06-18. Fix: mở wacs.exe interactive → Manage Renewals → recreate task.
  3. PE còn 3 task MISSING cho feature-complete (xem STATUS §A):
    • PE Workflow admin designer UI /system/pe-workflows/:typeCode
    • Auto-map PE Details → Contract 7 per-type Details khi gen HĐ
    • Section "Ý kiến 4 phòng ban" (Phê duyệt/CCM/MuaHàng/SM-PM)
  4. Login email mới admin@solutions.com.vn / Admin@123456 — old @solutionerp.local đã bị rename 401.
  5. 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 feature gap (session 4)

Xem STATUS.md §🔥 In Progress đầy đủ (nhóm A/B/C/D). 3 task MISSING cuối:

  1. PE Workflow admin designer UI /system/pe-workflows/:typeCode — mirror pattern WorkflowsPage.tsx + WorkflowDesigner.tsx. BE cần PeWorkflowAdminFeatures.cs (GetOverview + CreateNewVersion) + PeWorkflowsController.cs. Framework backend đã sẵn (3 bảng PurchaseEvaluationWorkflow* + FromDefinition builder), chỉ thiếu wire UI.
  2. Ý 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.
  3. Export phiếu PDF/Excel — tái dùng IDocumentConverter + template PE-TrinhDuyet.docx.
  4. Payment terms tách field từ JSON blob → 6 field riêng (optional UX polish).
  5. Auto-map PE Details → Contract Details khi gen HĐ (optional nâng cấp).

Đã xong trong session 3 (check STATUS Recently Done):

  • PE Attachments upload (per-NCC + Bảng so sánh tổng)
  • Menu tree inheritance Pe_/PeWf_
  • Accordion mutex + sidebar width + label nowrap
  • NavLink query active check
  • PE detail flat layout + readOnly mode
  • HĐ move Lịch sử điều chỉnh → Panel 3
  • Menu rename Phương Án → Giải pháp
  • Demo email rebrand solutionerp.local → solutions.com.vn

A. Hard blockers (chờ user / ops)

  1. 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
  2. SMTP config để bật Email outbox:
    "Email": {
      "Host": "smtp.gmail.com",
      "Port": 587,
      "Username": "...",
      "Password": "...",
      "From": "noreply@solutionerp.local"
    }
    
    Khi có → thêm MailKit, IEmailSender, hook vào NotificationService.CreateAsync ngay trước khi enqueue realtime push.
  3. Rotate credentials — SA SQL password, vrapp password, JWT secret prod, Gitea runner registration token
  4. Schedule SQL backupschtasks /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:

  1. Cross-check 6 skill hiện có với STATUS / gotchas / migration-todos
  2. Check repo nguồn 3rd party (alirezarezvani/claude-skills) có gì mới
  3. Update / archive / add skill nếu cần
  4. 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.Read cho 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 CIdotnet 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.WorkflowDefinitionId pin đú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.WorkflowDefinitionId KHÔ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

Đá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