Files
solution-erp/docs/HANDOFF.md
pqhuy1987 fbca83264c
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m33s
[CLAUDE] Docs: chốt session Tier 3 feature-complete + versioned workflow
- Session log 2026-04-22-0300 (A→K): attachment, SignalR, form builder,
  PDF, dynamic + versioned workflow, nested menu, 3-panel permissions,
  seed master, brand identity, content polish, Gitea fix
- STATUS: Tier 3 feature-complete snapshot + cumulative stats (24 tables,
  ~50 endpoints, 8 migrations); next-up = UAT + Email SMTP (blocked) +
  rotate creds + SQL backup schedule
- HANDOFF: rewrite brief cho session mới — phase 5 prod done, Tier 3
  đóng gói, quick sanity-check 2 app, versioned workflow quick ref,
  file active hiện trạng, git state
- migration-todos: tick Tier 3 items (attachment/realtime/form builder/
  PDF/dynamic+versioned workflow/nested menu) + thêm iter-3 versioned
  workflow section + post-launch list
- schema-diagram: +5 table (Notifications, WorkflowTypeAssignments,
  WorkflowDefinitions, WorkflowSteps, WorkflowStepApprovers); indexes
  mới, cardinality FK restrict cho pinned policy, truy vấn tiêu biểu
- workflow-contract: +section 7bis resolution order, 7ter admin
  designer flow, updated data model + code pointers Tier 3
- PROJECT-MAP: module map post-Tier-3 (3 box mới Notification/
  Attachment/Branding + Infra/DevOps box), API namespace đầy đủ,
  architectural wins 5 điểm
- contract-workflow skill: versioned workflow section, policy
  resolution code snippet, admin designer flow, code pointers Tier 3,
  tier 4+ backlog
- gotchas +7 bẫy mới (#26-32): SignalR WebSocket headers, interceptor
  2-phase pattern, LibreOffice mirror 404, PS 5.1 UTF-16 GITHUB_PATH,
  PS 5.1 diacritics parse, Dialog size TS, NavLink end query-params

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

15 KiB
Raw Blame History

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

Last updated: 2026-04-22 03:00 (post-Tier-3-feature-complete + versioned workflow)

TL;DR

Tier 3 ERP features xong hết (Attachment, SignalR, Form builder, PDF, Versioned workflow, Nested menu, Permission layout). Prod live 3 domain. Còn lại chủ yếu là UAT + SMTP + rotate creds, không còn module kỹ thuật lớn nào chưa làm.

Ở đâ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
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

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

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
91b2da1 — PermissionsPage 3-panel layout (LATEST)
f216169 — Admin Workflows tabs → sidebar menu items
355bbe3 — Fix Dialog size TS (xl → lg)
e7e5f2d — Versioned workflow entities + migration + designer
4 session trước đó nằm trong STATUS table

Branch: main (tracking origin/main)
Remote: https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp.git

Credentials + URLs

admin@solutionerp.local / Admin@123456

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