Commit Graph

21 Commits

Author SHA1 Message Date
5d94bb449a [CLAUDE] PE: Workflow designer admin UI + Ý kiến 4 phòng ban (P1 Session 5)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m51s
==== Task 1: PE Workflow Designer admin ====

BE (mirror Contract WorkflowAdminFeatures pattern):
- Application/PurchaseEvaluations/PeWorkflowAdminFeatures.cs ~250 LOC:
  - GetPeWorkflowAdminOverviewQuery → list 2 EvaluationType (DuyetNcc / DuyetNccPhuongAn) với Active + History versions + count phiếu đang dùng
  - CreatePeWorkflowDefinitionCommand + Validator: auto-increment Version per Code, deactivate Active cũ trong cùng EvaluationType (1 active per type invariant)
  - DTOs: PeWorkflowStepApproverDto / PeWorkflowStepDto / PeWorkflowDefinitionDto / PeWorkflowTypeSummaryDto / PeWorkflowAdminOverviewDto
  - Phase validation 1..7 (state thường, không bao gồm 99=TuChoi)
- Api/Controllers/PeWorkflowsController.cs: 2 endpoint GET /api/pe-workflows + POST. Reuse policy "Workflows.Read" + "Workflows.Create" (admin chung quyền cho cả 2 nhóm WF).

FE:
- pages/system/PeWorkflowsPage.tsx ~500 LOC mirror WorkflowsPage:
  - Landing 2-card grid khi /system/pe-workflows (chưa pick type)
  - TypePanel khi /system/pe-workflows/:typeCode (DuyetNcc / DuyetNccPhuongAn)
  - DefinitionCard read-only view với active badge + version + steps + approvers (Role/User chip)
  - PeWorkflowDesigner dialog: clone từ existing, edit Code/Name/Description, add/remove steps, +Role / +User approvers per step, save → version mới + deactivate cũ
- App.tsx route /system/pe-workflows + /system/pe-workflows/:typeCode
- Layout đã có resolver PeWf_<Code> → /system/pe-workflows/<code> từ session 3

==== Task 2: Ý kiến 4 phòng ban PE ====

Domain:
- PurchaseEvaluationDepartmentOpinion entity (AuditableEntity) — PEId + Kind + Opinion text + SignedAt + UserId + UserName denorm
- PeDepartmentKind enum (PheDuyet / Ccm / MuaHang / SmPm)
- PE entity + collection navigation DepartmentOpinions

Infrastructure:
- PurchaseEvaluationDepartmentOpinionConfiguration EF: UNIQUE(PEId, Kind) — max 1 row per phòng ban per phiếu (UPDATE in-place)
- ApplicationDbContext + IApplicationDbContext DbSet
- Migration 15 AddPurchaseEvaluationDepartmentOpinions (15 migration total / 52 DB tables)

Application:
- PeDepartmentOpinionFeatures.cs: UpsertPeDepartmentOpinionCommand (sign=true → set SignedAt+UserId, sign=false chỉ lưu text giữ chữ ký cũ) + DeletePeDepartmentOpinionCommand
- DTO bundle update: + DepartmentOpinions list trong PurchaseEvaluationDetailBundleDto
- GetPurchaseEvaluationQueryHandler load DepartmentOpinions + KindLabel resolution

API:
- POST /api/purchase-evaluations/{id}/opinions (upsert)
- DELETE /api/purchase-evaluations/{id}/opinions/{kind}

FE:
- types/purchaseEvaluation.ts: + PeDepartmentKind enum + PeDepartmentKindLabel + PeDepartmentOpinion type + departmentOpinions vào bundle
- PeDetailTabs Section "5. Ý kiến 4 phòng ban (sign-off)" — 2x2 grid OpinionBox per kind:
  - Read mode (readOnly menu Duyệt): hiển thị text + chữ ký
  - Edit mode: textarea + 2 button "Lưu text" / "Lưu & Ký"
  - Badge "Đã ký" emerald + tên người ký + ngày khi signedAt != null

==== Task 3: User seed verify ====

Seed `SeedDemoUsersAsync` đã match đúng user list authoritative (5 PRO TPB+NV / 7 CCM TPB+NV / 1 ISO / 1 CEO) từ prior commit. DbInitializer reconcile sẽ tự sync khi API restart. Typo trong list user (soluttions / trương) đã fixed sensibly trong seed.

==== Build verify ====
- dotnet build clean (0 error)
- fe-admin TS build pass (1 module mới PeWorkflowsPage)
- fe-user TS build pass (PE detail mirror)

Total: 8 file mới (BE 4 + FE 1 + Migration 2 + 1 Domain) + 13 file modified.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 11:17:14 +07:00
a05c57b081 [CLAUDE] Domain+App+Api: Module Ngan sach (Budget) - 4 bang + workflow simple
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m11s
User request: 'Them cho tao 4 bang luu ve ngan sach: Header / Chi tiet
/ Quy trinh duyet / Lich su thay doi'.

Domain (5 file + 1 enum):
 - Budget (header) — Aggregate root, AuditableEntity. Field: MaNganSach,
   TenNganSach, Description, NamNganSach, ProjectId FK, DepartmentId?,
   DrafterUserId, Phase (BudgetPhase 5-state), TongNganSach (sum auto
   tu Details), SlaDeadline, SlaWarningSent.
 - BudgetDetail — flat row pattern (GroupCode/GroupName + Item +
   KhoiLuong/DonGia/ThanhTien). 18,4 precision KhoiLuong, 18,2 money.
 - BudgetApproval — workflow history (FromPhase/ToPhase/Decision/Comment)
 - BudgetChangelog — audit log unified (EntityType: Header/Detail/Workflow)
 - BudgetPhase enum 5 state: DangSoanThao(1) → ChoCCM(2) → ChoCEO(3) →
   DaDuyet(4) | TuChoi(99)
 - BudgetPolicy hardcoded (no versioned WF, simple default per user
   confirm 'tam thoi don gian'): Drafter/DeptManager → CCM → CEO/
   AuthorizedSigner. Reject path back to DangSoanThao.

Migration 14 AddBudgets:
 - 4 bang moi: Budgets + BudgetDetails + BudgetApprovals + BudgetChangelogs
 - Index: Phase+IsDeleted, ProjectId, NamNganSach, SlaDeadline,
   MaNganSach unique filtered. Cascade delete child.
 - +2 cot FK ngoai bang (per user 'lien ket ca 3'):
   * Contracts.BudgetId Guid? + index
   * PurchaseEvaluations.BudgetId Guid? + index
   Cho phep doi chieu chi phi HD/PE vs ngan sach goi thau.

Application CQRS (BudgetFeatures.cs ~340 line):
 - CreateBudget + UpdateBudgetDraft + TransitionBudget + ListBudgets
   (filter Phase/Project/Year + search + paging) + GetBudget bundle
   (Header + Details + Approvals + Workflow summary)
 - DeleteBudget (only DangSoanThao/TuChoi)
 - AddBudgetDetail + UpdateBudgetDetail + DeleteBudgetDetail (auto
   recompute TongNganSach = sum Details.ThanhTien)
 - ListBudgetChangelogs

Api: BudgetsController 11 endpoint REST /api/budgets:
 - GET /  /{id}  /{id}/changelogs
 - POST /  /{id}/transitions  /{id}/details
 - PUT /{id}  /{id}/details/{detailId}
 - DELETE /{id}  /{id}/details/{detailId}

DbContext + IApplicationDbContext: 4 DbSet new (Budgets/Details/
Approvals/Changelogs).

MenuKeys + DbInitializer: 4 menu key (Budgets root + Bg_List/Create/
Pending leaves) seed dau order=27 'Ngan sach' icon Wallet. Auto-grant
admin permission via SeedAdminPermissionsAsync (MenuKeys.All).

MaNganSach format don gian 'NS-YYYYMM-NNNN' Random.Shared (chua atomic
sequence - user said 'tam thoi chua co').

Workflow chua versioned, hardcode BudgetPolicy.Default. Tuong lai admin
config qua UI: them BudgetWorkflowDefinition tables tuong tu PE.
2026-04-28 11:37:45 +07:00
a336997cfe [CLAUDE] PE: section Bang so sanh + rename demo email @solutions.com.vn
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m10s
PART A: Section 'Bang so sanh' (file tong ho so so sanh)

User request: 'theo them cho thong tin ve Bang so sanh, cho dinh kem
file so sanh tong len'.

BE:
 - PurchaseEvaluationAttachmentPurpose.ComparisonTable = 4 (new enum value)
   Backend validator IsInEnum pass, khong can migration (int column).

FE types (2 app):
 - PeAttachmentPurpose.ComparisonTable + Label '4: Bang so sanh'.

FE PeDetailTabs:
 - Them section thu 4 'Bang so sanh (file tong)' sau 'Hang muc + Bao gia'.
 - Component GeneralAttachmentsSection: upload KHONG truyen supplierRowId
   (BE luu NULL) → purpose=ComparisonTable default. Filter attachments
   co supplierRowId===null de render.
 - Card layout khac SupplierAttachmentsCell: full-width card + brand color
   + purpose chip + date. Upload button to hon ([+ Tai len bang so sanh]).
 - readOnly hide upload + delete, giu download.

PART B: Demo email rebrand @solutionerp.local → @solutions.com.vn

User request: 'tao nguoi dung demo theo email cua ben nay'.

BE DbInitializer:
 - Rename 18 email in source: AdminEmail const + 17 demo users
   (bod/pm/ccm/pro/fin/act/equ/hra/qs/nv) — keep password + role unchanged.
 - Them BackfillUserEmailDomainAsync (idempotent): scan user co email
   @solutionerp.local, rename sang @solutions.com.vn, update Email +
   NormalizedEmail + UserName + NormalizedUserName. Skip neu co conflict
   user da ton tai voi email moi. Chay truoc SeedAdmin de tranh tao
   duplicate admin.

Admin permission tao user da co san qua /system/users page.
Comment input khi duyet da co san o PeWorkflowPanel (Ghi chu tuy chon
Textarea) + ContractDetailContent (Yeu cau sua / Duyet tiep dialog).
2026-04-24 15:08:00 +07:00
7ee105df12 [CLAUDE] PE: rename menu + workflow Phuong An -> Giai phap
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m27s
User request session 3 mở đầu: đổi label menu:
  'Quy trình Duyệt NCC - Phương Án' -> 'Duyệt NCC và Giải pháp'
  'Quy trình Duyệt NCC' -> 'Duyệt NCC' (bỏ prefix 'Quy trình')

Giữ nguyên (không breaking):
  - Enum value PurchaseEvaluationType.DuyetNccPhuongAn = 2
  - Menu key Pe_DuyetNccPhuongAn_* (FK ref)
  - typeCode string 'DuyetNccPhuongAn' (URL routing)
  - Policy name 'NccWithPlan' + WorkflowDefinition.Code QT-DN-B

Changed (display-only):
  - DbInitializer SeedMenuTreeAsync peTypeLabels: 2 entry
  - DbInitializer SeedPurchaseEvaluationWorkflowsAsync WF name B
  - PurchaseEvaluationPolicy.cs NccWithPlan Description
  - fe-admin + fe-user types PurchaseEvaluationTypeLabel[2]

Backfill existing DB (seed idempotent skip-if-exists không update):
  - MenuItems Pe_DuyetNcc group + Pe_DuyetNccPhuongAn group + PeWf_* leaf
    → UPDATE Label nếu khác expected
  - WorkflowDefinitions QT-DN-B v01 → UPDATE Name Replace 'Phương Án' → 'và Giải pháp'
  Run mỗi start API — idempotent, fast no-op sau lần đầu.
2026-04-24 10:43:26 +07:00
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
2c6f0cabfb [CLAUDE] Domain+Infra: PurchaseEvaluation module — 10 bảng + 2 workflow seed (migration 12)
Module Duyệt NCC (tiền-HĐ): phiếu trình duyệt so sánh giá N NCC × M hạng
mục trước khi ký HĐ. 2 quy trình: A DuyetNcc (3-step: Purchasing→CCM→CEO),
B DuyetNccPhuongAn (5-step: Purchasing→DựÁn→CCM→CEO PA→CEO NCC).

Domain (7 core + 3 workflow admin):
 - PurchaseEvaluation (header, AuditableEntity, pin WorkflowDefinitionId,
   SelectedSupplierId, PaymentTerms JSON, ContractId? FK kế thừa)
 - PurchaseEvaluationSupplier (N:M Phiếu × Supplier + contact + payment term)
 - PurchaseEvaluationDetail (hạng mục + ngân sách, group A.I/A.II/...)
 - PurchaseEvaluationQuote (báo giá per NCC per hạng mục + IsSelected)
 - PurchaseEvaluationApproval (workflow history, reuse ApprovalDecision)
 - PurchaseEvaluationChangelog (audit log, reuse ChangelogAction)
 - PurchaseEvaluationAttachment (file upload — báo giá NCC + spec...)
 - PurchaseEvaluationWorkflowDefinition/Step/StepApprover (config y như HĐ,
   tách table riêng vì Phase là PurchaseEvaluationPhase enum riêng)

Policy:
 - PurchaseEvaluationPolicy record + PurchaseEvaluationPolicies.NccOnly/
   NccWithPlan (default hardcoded) + FromDefinition(def) build runtime policy
   từ DB admin-authored. Default SLA: soạn 3d, step 1-2d, CEO 1d.

EF: 10 configurations với index phase+isDeleted, SupplierId, ProjectId,
SlaDeadline, WorkflowDefinitionId, ContractId. UX index (PeId, SupplierId)
+ (DetailId, SupplierId). HasQueryFilter soft delete cho header.

Migration 12 AddPurchaseEvaluations tạo 10 bảng. Idempotent seed:
 - SeedMenuTreeAsync +13 menu item (Pe_* root + 2 group + 6 action leaf
   + PeWorkflows root + 2 admin leaf)
 - SeedPurchaseEvaluationWorkflowsAsync seed QT-DN-A-v01 + QT-DN-B-v01
2026-04-23 16:37:55 +07:00
4edcd588d8 [CLAUDE] Domain+Infra: User-kind approver runtime guard + Warning 20% SLA
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m41s
## User-kind approver guard

Trước: WorkflowDefinition Designer cho admin pick User cụ thể vào step
approver, nhưng runtime guard bỏ qua (User-kind treat như DeptManager
fallback per skill doc).

Bây giờ: enable đầy đủ. WorkflowPolicy + UserTransitions parallel dict
(default null cho hardcoded Standard/SkipCcm, populated qua
FromDefinition khi WorkflowStepApprover Kind=User).

IsTransitionAllowed signature update: (from, to, actorRoles, actorUserId?)
- Check Role first (existing behavior)
- Fallback User-kind: actorUserId.ToString() có trong UserTransitions[(from,to)]?

ContractWorkflowService.TransitionAsync dùng IsTransitionAllowed thay
inline check. Error message thêm "{N} user explicit" nếu policy có
User-kind approvers cho transition đó.

FromDefinition cũng update: nếu step CHỈ có User-kind (không Role),
không fallback DeptManager nữa — guard sẽ check user-level. Chỉ
fallback DeptManager nếu step thiếu cả 2.

## Warning 20% SLA

SlaExpiryJob.ProcessWarningsAsync mới — chạy trước ProcessAsync
(auto-approve quá hạn):
- Pull Contracts WHERE !SlaWarningSent && SlaDeadline > now &&
  Phase NOT IN (DaPhatHanh, TuChoi, DangDongDau)
- Per phase, threshold = 20% × default SLA (vd Soạn thảo 7 ngày → 33.6h
  remaining trigger warning; In ký 1 ngày → 4.8h)
- Compute remaining = SlaDeadline - now; nếu remaining <= threshold
  + còn slot → notify Drafter via INotificationService
- Set SlaWarningSent = true để chỉ warning 1 lần per phase (reset trong
  TransitionAsync khi chuyển phase mới)
- NotificationType.SlaWarning (đã có trong enum) + title icon ⚠

## Build

dotnet build BE pass (0 error)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 15:11:34 +07:00
330d529c92 [CLAUDE] Domain+App+Infra: Role ShortName + User Department/Position + 13 demo users (migration 11)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m53s
User feedback: chi tiết hóa Users/Phòng ban + gán roles. Roles label
tiếng Việt có Mã (ShortName) + Tên đầy đủ (Description).

## Entity changes

### Role (Domain/Identity/Role.cs)
+ ShortName (max 50)  — Mã viết tắt VN: QTV/BOD/CCM/PRO/FIN/...
+ Description (đã có) — Tên đầy đủ VN
- Identity Name = code English giữ nguyên (FK + [Authorize] attr)

### User (Domain/Identity/User.cs)
+ DepartmentId Guid? FK Departments (Restrict — không xóa dept nếu user reference)
+ Position string? max 200 — chức vụ free text

## Migration 11: AddRoleShortNameAndUserDepartment

3-file rule. Apply LocalDB OK. DB total: 36 tables (không tăng — chỉ
thêm cột vào existing).

## Seed VN labels (12 roles)

| Code | ShortName | Description |
|---|---|---|
| Admin | QTV | Quản trị viên hệ thống |
| Drafter | NV.PB | Nhân viên phòng ban (soạn thảo HĐ) |
| DeptManager | TPB | Trưởng phòng ban |
| ProjectManager | PM | Giám đốc dự án |
| Procurement | PRO | Phòng Cung ứng |
| CostControl | CCM | Phòng Kiểm soát chi phí |
| Finance | FIN | Phòng Tài chính |
| Accounting | ACT | Phòng Kế toán |
| Equipment | EQU | Phòng Thiết bị |
| Director | BOD | Ban Giám đốc |
| AuthorizedSigner | NĐUQ | Người được Ủy quyền ký HĐ |
| HrAdmin | HRA | Phòng Nhân sự - Hành chính |

SeedRolesAsync idempotent + backfill (existing role thiếu ShortName/
Description → update).

## Seed 13 demo users

Default password: User@123456 (warn log để rotate prod). Coverage full
org chart:
- bod.huynh (Tổng GĐ — Director, BOD dept)
- bod.le (Phó GĐ NĐUQ — AuthorizedSigner, BOD dept)
- pm.nguyen (PM FLOCK 01 — ProjectManager, PM dept)
- ccm.tran (TPB CCM — CostControl + DeptManager, CCM dept)
- pro.pham (TPB PRO — Procurement + DeptManager, PRO dept)
- fin.do, act.vu, equ.bui, hra.dang (TPB respective dept)
- qs.hoang, qs.ngo (Drafter — QS dept)
- nv.cao, nv.dinh (Drafter — PRO/FIN dept)

Idempotent (skip nếu email đã tồn tại).

## DTOs + Commands updated

- RoleDto + ShortName field
- UserDto + DepartmentId/DepartmentName/Position
- CreateUserCommand + DepartmentId/Position params (defaults null)
- UpdateUserCommand + DepartmentId/Position
- ListUsersQueryHandler load dept names denormalize per page
- UpdateUserCommandHandler set UpdatedAt

## Note

FE updates (UsersPage dept dropdown + role label VN) ở commit kế tiếp.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 14:24:12 +07:00
e27c54702a [CLAUDE] Domain+App+Api: 4 master catalogs cho Details (migration 10)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
User feedback: thêm Master Data cho phần Chi tiết (line items) — autocomplete
khi user nhập field thay vì gõ tay free text.

## 4 entities mới (Domain/Master/Catalogs/)

| Entity | Bảng | Dùng cho HĐ Detail |
|---|---|---|
| UnitOfMeasure | UnitsOfMeasure | Tất cả 7 type (DonViTinh) |
| MaterialItem | MaterialItems | NCC + Mua bán + Nguyên tắc NCC (MaSP/TenSP) |
| ServiceItem | ServiceItems | Dịch vụ + Nguyên tắc DV (MaDichVu/TenDichVu) |
| WorkItem | WorkItems | Thầu phụ + Giao khoán (HangMuc/MaCongViec) |

Common pattern: Code unique (filter IsDeleted=0) + Name + Category +
DefaultUnit + AuditableEntity (soft delete) + IsActive flag.

## Migration 10: AddMasterCatalogs

3-file rule (gotcha #17). Total DB: 32 → 36 tables. Apply LocalDB OK.

## Seed defaults (idempotent — skip per-table nếu có row)

- 20 UnitsOfMeasure: m2, m3, kg, tấn, lít, ngc (ngày công), giờ, gói, ...
- 15 MaterialItems demo: xi măng PCB40, cát vàng, đá 1x2, thép D10, gạch
  4 lỗ, sơn lót, ống PVC...
- 10 ServiceItems demo: vận chuyển, bảo trì, tư vấn, kiểm định, vệ sinh...
- 15 WorkItems demo: đào móng, đổ bê tông, xây tường, trát, lát gạch,
  sơn nước, lắp điện, lắp nước, thấm chống...

## CQRS (Application/Master/Catalogs/CatalogsFeatures.cs ~290 dòng)

Mỗi catalog 5 handlers (List filter q+category, Create với unique code
guard, Update, Delete soft). FluentValidation max length per spec EF.

## Controller (Api/Controllers/CatalogsController.cs)

13 endpoints:
- GET  /api/catalogs/{units|materials|services|work-items}
- POST /api/catalogs/{kind}                  (Admin role)
- PUT  /api/catalogs/{kind}/{id}             (Admin role)
- DELETE /api/catalogs/{kind}/{id}           (Admin role)

Read open cho mọi role (FE Details add form autocomplete cần list).

## Menu (5 mới)

- Catalogs (group, Master parent, order 24, "Library" icon)
  - CatalogUnits "Đơn vị tính" (Ruler)
  - CatalogMaterials "Vật tư / SP" (Package)
  - CatalogServices "Dịch vụ" (Wrench)
  - CatalogWorkItems "Hạng mục công việc" (ListChecks)

Admin auto-grant tất cả CRUD action (qua SeedAdminPermissionsAsync
loop MenuKeys.All).

## Build

dotnet build BE pass (0 error)

## Note

FE admin page CatalogsPage + datalist autocomplete trong Details form
sẽ ở commit kế tiếp.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 12:21:39 +07:00
70810e1b34 [CLAUDE] Domain+Infra: 7 ContractType-specific Details + ContractChangelog (migration 9)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m37s
User decision: Option B — bảng riêng cho mỗi loại HĐ (chuẩn nhất, schema
chuyên biệt). Plus: ContractChangelog audit log thống nhất Header /
Detail / Workflow / Comment / Attachment.

## 8 entities mới

### Details (7) — Domain/Contracts/Details/

| Bảng | Loại HĐ | Field đặc trưng |
|---|---|---|
| ThauPhuDetails | 1 (Thầu phụ) | HangMuc, KhoiLuong, DonGia, ThoiGianHoanThanh |
| GiaoKhoanDetails | 2 (Giao khoán) | MaCongViec, KhoiLuong, YeuCauKyThuat |
| NhaCungCapDetails | 3 (NCC) | MaSP, ThongSoKyThuat, SoLuong, ThoiGianGiao, XuatXu |
| DichVuDetails | 4 (Dịch vụ) | MaDichVu, ThoiGian, TuNgay/DenNgay |
| MuaBanDetails | 5 (Mua bán) | MaSP, SoLuong, DonGia, ThueVAT (%), XuatXu |
| NguyenTacNccDetails | 6 (Nguyên tắc NCC) | NhomSP, DonGiaToiThieu/ToiDa, DieuKienGiaoHang |
| NguyenTacDvDetails | 7 (Nguyên tắc DV) | LoaiDichVu, DonGiaToiThieu/ToiDa, PhamViDichVu, SLA |

Common base `ContractDetailBase`: ContractId FK + Order + ThanhTien
decimal(18,2) + GhiChu nvarchar(1000) + audit (BaseEntity).

### ContractChangelog (1) — Domain/Contracts/

Unified audit log. Khác ContractApprovals (workflow-only, dùng cho guard
logic) — Changelog là VIEW LAYER cho user đọc lịch sử thao tác:
- EntityType enum: Contract | Detail | Workflow | Comment | Attachment
- Action enum: Insert | Update | Delete | Transition
- PhaseAtChange snapshot
- UserId + UserName denormalize (log readable)
- Summary human-readable + FieldChangesJson [{Field, Old, New}]
- ContextNote (comment kèm theo)

## EF Configurations

ContractDetailsConfiguration.cs (1 file gộp 7 IEntityTypeConfiguration):
- ToTable + HasMaxLength + HasPrecision per type
- HasOne(Contract).WithMany(<TypeDetails>) cascade delete
- IX (ContractId, Order) cho load timeline

ContractChangelogConfiguration.cs:
- Cascade delete khi Contract xóa
- IX (ContractId, CreatedAt) timeline + IX (ContractId, EntityType) filter

## DbContext + IApplicationDbContext

+ 8 DbSet mới (7 Details + ContractChangelogs).

## Migration 9: AddContractDetailsAndChangelog

3-file rule (gotcha #17): .cs + .Designer.cs + ApplicationDbContextModel
Snapshot.cs đầy đủ. Applied LocalDB SolutionErp_Dev OK — 24 + 8 = 32 bảng
total.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 10:08:42 +07:00
f216169039 [CLAUDE] FE-Admin+Domain+Infra+App: Workflows tab → sidebar menu items
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m37s
User request: 7 tab trong /system/workflows thành menu items riêng.

Domain:
- MenuKeys.WorkflowTypeLeaf(code) helper — `Wf_<TypeCode>` pattern

Infrastructure (DbInitializer):
- Seed 7 leaves dưới Workflows group (order 95..101), label matches
  ContractType (HĐ Thầu phụ / Giao khoán / NCC / Dịch vụ / Mua bán /
  Nguyên tắc NCC / Nguyên tắc Dịch vụ). Idempotent.

Application (GetMyMenuTreeQuery):
- Generalized inherit-perm logic: descendants of Contracts AND Workflows
  inherit parent CanRead flag. Single Workflows.Read grant → all 7
  Wf_* leaves visible; no per-leaf permission rows needed.

FE Layout (admin):
- resolvePath: Wf_<Code> → /system/workflows/<code>. Ct_* still hidden
  on admin side.

FE App.tsx:
- New route /system/workflows/:typeCode?

FE WorkflowsPage:
- Removed horizontal tab bar; type selection now comes từ URL param.
- Landing view (no param): 3-col grid card per type với active version
  badge — so admin có visual overview khi click top-level Workflows
  group without selecting a type.
- TYPE_CODE_TO_INT map drives URL→int conversion.

Result: click `Quy trình HĐ > HĐ Mua bán` trong sidebar → opens
/system/workflows/MuaBan directly với designer scoped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 09:49:42 +07:00
e7e5f2d066 [CLAUDE] Domain+Infra+App+Api+FE-Admin: versioned workflow per ContractType
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Failing after 1m32s
User yêu cầu: mỗi loại HĐ có quy trình riêng với admin add roles + users
vào từng bước. Khi tạo version mới → HĐ tương lai chạy theo, HĐ cũ giữ
version cũ.

Domain:
- WorkflowDefinition (Code + Version + ContractType + IsActive + Steps)
- WorkflowStep (Order + Phase + Name + SlaDays + Approvers)
- WorkflowStepApprover (Kind: Role/User + AssignmentValue)
- Contract.WorkflowDefinitionId — pinned at creation
- WorkflowPolicyRegistry.FromDefinition() — build runtime policy từ DB

Infrastructure:
- EF config + migration AddVersionedWorkflows (3 table mới)
- DbInitializer.SeedWorkflowDefinitionsAsync: v01 per 7 ContractType,
  steps sinh từ hardcoded WorkflowPolicies (Role approvers).
- ContractWorkflowService.TransitionAsync: load pinned WorkflowDefinition
  → FromDefinition(), fallback cho HĐ cũ không có pin.

Application:
- CreateContractCommand pin WorkflowDefinitionId = active version cho type
- ContractFeatures.Get(id): load pinned def cho workflow summary
- WorkflowAdminFeatures: GetWorkflowAdminOverviewQuery (7 types + active
  + history + ContractsUsingCount), CreateWorkflowDefinitionCommand
  (validate payload, auto-increment version, deactivate old).

Api:
- GET /api/workflows trả overview
- POST /api/workflows tạo version mới (deactivate old)

FE /system/workflows:
- Tabs per 7 ContractType, mỗi tab hiện active version + lịch sử
- DefinitionCard: steps với badge role/user + SLA + archived indicator
  hiện "N HĐ còn chạy" cho version cũ
- WorkflowDesigner modal: form code/name/desc + danh sách steps
  (phase/name/SLA) + approvers (+ Role hoặc + User). Drop step ok.
  Clone từ version hiện tại để tạo v02 có điểm start sensible.
- Amber banner: HĐ cũ không bị ảnh hưởng khi tạo version mới

Invariants được giữ:
- Unique (Code, Version) index
- Chỉ 1 version IsActive per ContractType tại 1 thời điểm
- Set default sẽ auto xóa override → respect legacy override table
- Role-kind approvers drive transition guards; User-kind fallback
  DeptManager role cho v1 (user-level targeting = iteration 2)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:57:41 +07:00
5e0f3801a1 [CLAUDE] Move nested-type menu → fe-user; Admin workflow config page
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m41s
User clarified: menu loại HĐ 3-level (Danh sách/Thao tác/Duyệt) thuộc
fe-user. Admin có page riêng để config quy trình per loại HĐ.

fe-admin Layout:
- filterForAdmin() drops Ct_* entries (hide nested type menu).
- Admin sidebar giờ về lại đơn giản: Dashboard / Master / Hợp đồng
  (leaf) / Forms / Reports / System.

fe-user Layout:
- Dynamic menu tree từ /menus/me (thay fixed USER_MENU hardcoded).
- Recursive MenuNodeRenderer (top-level expanded, nested collapsed).
- resolvePath user-specific: Ct_*_List → /my-contracts?type=X,
  Ct_*_Create → /contracts/new?type=X, Ct_*_Pending → /inbox?type=X.
- filterForUser drops admin-only entries (Master/System/Forms/Reports).
- Static USER_FIXED_TOP prepends "Hộp thư" leaf → /inbox.
- MyContractsPage + InboxPage đọc ?type=X param, filter client-side.

Workflow config (Admin side):
- Domain: WorkflowTypeAssignment entity (ContractType → PolicyName
  override). Registry.ForContractWithOverrides() prefer DB override
  else default.
- Infrastructure: EF config + migration AddWorkflowTypeAssignments,
  unique index trên ContractType. ContractWorkflowService load
  overrides dict mỗi transition. ContractFeatures load overrides khi
  build WorkflowSummaryDto.
- Application: GetWorkflowAdminOverviewQuery returns 7 types × current
  policy + available policies. SetWorkflowAssignmentCommand validate
  policy name tồn tại; nếu = default thì delete override (no stale row).
- Api: GET /api/workflows + PUT /api/workflows/{contractType}
  với policy "Workflows.Read" + "Workflows.Update".
- Menu: new key `Workflows` dưới System, label "Quy trình HĐ".
- FE /system/workflows: 7 card per type, dropdown Standard/SkipCcm +
  'Đã override' badge khi khác default, phase sequence timeline,
  explanation banner ở top. Iteration 2 note: admin-authored custom
  policies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:41:05 +07:00
48e91fe7ca [CLAUDE] Domain+Infra+App+FE-Admin: per-ContractType nested sidebar menu
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
User request: mỗi loại HĐ có menu riêng với 3 action Danh sách /
Thao tác / Duyệt.

Sidebar giờ 3-level under "Hợp đồng":
  Hợp đồng (group, expandable)
  ├── HĐ Thầu phụ (sub-group)
  │   ├── Danh sách → /contracts?type=1
  │   ├── Thao tác  → /contracts/new?type=1
  │   └── Duyệt      → /contracts?type=1&pendingMe=1
  ├── HĐ Giao khoán (sub-group)
  ├── HĐ NCC / Dịch vụ / Mua bán / Nguyên tắc NCC / Nguyên tắc DV
  └── ... (7 types × 4 = 28 new menu items)

BE:
- MenuKeys.cs: ContractTypeCodes array + helpers ContractTypeGroup/
  List/Create/Pending → key format Ct_<TypeCode>[_<Action>]
- DbInitializer.SeedMenuTreeAsync: loop seeds 28 entries under Contracts
- GetMyMenuTreeQuery.BuildChildren: descendants of `Contracts` inherit
  parent permission (avoid adding 28 rows to Permissions table per role)

FE:
- Layout.tsx recursive: MenuNodeRenderer dispatches group vs leaf by
  depth; nested groups collapsed by default (top-level expanded).
  Deeper levels get smaller padding/text + left border guide.
- Pattern-based resolvePath: Ct_<Type>_<Action> → URL with query.
- Contract type code → int map (matches Domain ContractType enum).
- ContractsListPage reads ?type + ?pendingMe, filters client-side.
  Header title + description reflect active filter. "← Tất cả loại"
  quick-reset button.
- ContractCreatePage new cho admin (copy từ fe-user), pre-select type
  từ ?type URL param.
- App.tsx route /contracts/new → ContractCreatePage.

Pure navigation UX; no new permissions needed. Admin + any role with
Contracts.Read see full menu; leaves click-through to filtered views.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:25:00 +07:00
cae4d84830 [CLAUDE] Domain+Infra+App+FE: dynamic workflow policy per ContractType
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m42s
Đọc QT-TP-NCC.docx: quy trình 9 bước chỉ áp dụng cho Thầu phụ/NCC/Tổ đội.
Dịch vụ/Mua bán/Nguyên tắc bypass CCM. Thay hardcoded dict bằng policy
registry.

Domain — WorkflowPolicy.cs:
- Record WorkflowPolicy { Name, Description, Transitions, PhaseSla,
  ActivePhases } — pure data, testable.
- WorkflowPolicies.Standard: 9-phase full (Thầu phụ/Giao khoán/NCC)
- WorkflowPolicies.SkipCcm: 7-phase (Dịch vụ/Mua bán/Nguyên tắc)
- WorkflowPolicyRegistry.For(type) map ContractType → policy
- WorkflowPolicyRegistry.ForContract(c) override nếu BypassProcurement
  AndCCM=true (instance-level escape hatch)

Infrastructure — ContractWorkflowService:
- Xóa hardcoded Transitions/PhaseSla dicts → load từ policy.ForContract
- TransitionAsync: validate qua policy.Transitions thay vì dict local
- Error message include policy.Name để debug dễ hơn
- GetPhaseSla trả SLA từ Standard policy (fallback — SLA hiện tại giống
  nhau giữa 2 policy)

Application — ContractDetailDto:
- Field mới `Workflow: WorkflowSummaryDto { PolicyName, Description,
  ActivePhases, NextPhases }` — FE dùng để render nút chuyển phase
  dynamic + timeline card.
- BuildWorkflowSummary helper trong ContractFeatures.

FE (both apps):
- Type WorkflowSummary + ContractDetail.workflow
- ContractDetailPage xóa hardcoded NEXT_PHASES — dùng
  c.workflow.nextPhases từ BE (single source of truth)
- WorkflowSummaryCard: timeline của ActivePhases với check/current/
  future states + policy name/description ở header
- Card hiển thị trong sidebar, phía trên "Lịch sử duyệt"

Docs:
- gotchas.md #21 marked RESOLVED (NEXT_PHASES sync không còn cần)

Foundation: sau này admin có thể edit policy qua UI khi chuyển sang DB-
backed policy — nhưng API contract (WorkflowSummaryDto) đã stable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 21:46:31 +07:00
49c0ddc8f4 [CLAUDE] App+Domain+Infra+Api+FE: Notifications module end-to-end
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m43s
Domain:
- Notification entity + NotificationType enum (stable ints)
- Nullable RefId cho correlation (contract, user, ...)

Infrastructure:
- NotificationConfiguration: bảng Notifications, index theo (UserId, ReadAt)
- NotificationService: ghi vào DbContext, không SaveChanges (để caller quyết
  định unit-of-work — đảm bảo atomic với domain mutation)
- EF migration AddNotifications

Application:
- INotificationService (Notify + NotifyMany)
- CQRS: ListMyNotifications / GetMyUnreadCount / MarkRead / MarkAllRead

Api:
- NotificationsController: GET /api/notifications + unread-count + mark-read

Integration:
- ContractWorkflowService emit notification tới Drafter khi HĐ chuyển phase
  (skip nếu actor chính là Drafter). Title + type theo phase đích:
  DaPhatHanh → ContractPublished, TuChoi → ContractRejected, khác →
  ContractPhaseTransition.

FE:
- Both NotificationBell (admin + user) dùng /api/notifications thật
  (thay cho derived-from-inbox MVP trước đó). 30s refetch, click mark-read,
  'Đọc hết' bulk action.

Foundation sẵn cho SignalR push + email outbox sau này — chỉ cần mở rộng
NotificationService mà không đổi caller.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 15:24:09 +07:00
7e957a7654 [CLAUDE] Phase3: Workflow MVP — 9-phase state machine + code gen + FE Inbox/Detail
Backend Contracts domain (5 entities):
- Contract aggregate: Phase (9 enum), SlaDeadline, MaHopDong, BypassProcurementAndCCM, DraftData, SlaWarningSent
- ContractApproval: FromPhase → ToPhase, ApproverUserId (null = system auto-approve), Decision, Comment
- ContractComment: thread theo Phase current
- ContractAttachment: FileName + StoragePath + Purpose (DraftExport/ScannedSigned/SealedCopy)
- ContractCodeSequence: Prefix PK + LastSeq — atomic gen

EF configs:
- Unique MaHopDong filtered [MaHopDong] IS NOT NULL
- Indexes: Phase+IsDeleted, SupplierId, ProjectId, SlaDeadline, ContractId+ApprovedAt, ContractId+CreatedAt
- Cascade delete Approvals/Comments/Attachments khi Contract xoa
- Query filter IsDeleted
- Migration AddContractsWorkflow (DB 19 tables)

Workflow service:
- IContractWorkflowService.TransitionAsync:
  - Adjacency check qua Transitions Dict<(from,to), roles[]> (12 transitions)
  - Role guard: user phai co role ∈ allowed
  - Admin bypass (role Admin pass moi check)
  - System bypass (userId=null + Decision=AutoApprove → cho SLA job sau nay)
  - Bypass CCM: BypassProcurementAndCCM=true cho phep DangInKy → DangTrinhKy skip phase 6
  - Gen ma HD khi chuyen DangDongDau (idempotent — khong gen lai neu da co)
  - Reset SlaDeadline = UtcNow + PhaseSla
  - Insert ContractApproval row

Code generator (RG-001):
- 7 format theo ContractType: HDTP / HDGK / NCC / HDDV / MB + 2 framework (year prefix)
- BeginTransactionAsync(Serializable) + ContractCodeSequences UPSERT → atomic
- Idempotent: neu MaHopDong da co thi skip

CQRS (8 feature, ContractFeatures.cs):
- CreateContractCommand + Validator + Handler (set SlaDeadline = +7d)
- UpdateContractDraftCommand (chi khi Phase=DangSoanThao)
- TransitionContractCommand (delegate → WorkflowService)
- AddCommentCommand (phase = hien tai)
- ListContractsQuery (PagedResult + filter phase/supplier/project/search)
- GetMyInboxQuery (map Phase → actor roles, filter theo role user)
- GetContractQuery (detail + approvals + comments + attachments + resolve user names)
- DeleteContractCommand (soft, block > DangInKy)

Controller:
- ContractsController 8 endpoint: GET list/inbox/detail, POST create/transition/comment, PUT update, DELETE

Frontend fe-admin (2 page moi):
- types/contracts.ts: ContractPhase const + Label + Color maps + types
- components/PhaseBadge.tsx
- pages/contracts/ContractsListPage.tsx: filter phase + search + click → detail
- pages/contracts/ContractDetailPage.tsx: 2-col layout (info+comments | timeline), action dialog select target phase + comment

Frontend fe-user (4 page moi + 14 file shared):
- cp 14 file shared tu fe-admin (menuKeys, types/*, DataTable, PhaseBadge, Dialog, Textarea, Select, apiError, usePermission, PermissionGuard)
- AuthContext update: load menu tu /menus/me + cache
- Layout: menu fixed 3 muc + user info + roles display
- InboxPage: list HD cho role user xu ly (sort theo SLA)
- ContractCreatePage: form chon loai + template + NCC + du an + gia tri + bypass CDT
- ContractDetailPage: duplicate fe-admin pattern (convention)
- MyContractsPage: list HD cua toi
- App.tsx: 4 route moi

E2E verified:
- Setup Supplier + Project
- POST /contracts → 201 + phase=2
- POST /contracts/{id}/transitions x7 → di het 9 phase
- Final: MaHopDong = "FLOCK 01/HĐGK/SOL&PVL2026/01" dung format RG-001
- Approvals: 7 rows audit day du

Docs:
- .claude/skills/contract-workflow/SKILL.md: placeholder → full spec voi state machine, SLA table, role matrix, 7 code format, code pointers, API, E2E workflow, pitfalls
- docs/changelog/sessions/2026-04-21-1330-phase3-workflow.md: session log
- docs/STATUS.md: Phase 3 MVP done, next Phase 4
- docs/HANDOFF.md: update phase status + file tree + commit log + testing points
- docs/changelog/migration-todos.md: tick Phase 3 MVP items + add iteration 2 list

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 12:26:09 +07:00
5113e4c771 [CLAUDE] Phase2: Form Engine MVP + docs (gotchas, skill, handoff)
Backend Forms:
- Domain/Forms: ContractTemplate (FormCode, Name, ContractType, FileName, StoragePath, Format, FieldSpec JSON, IsActive) + ContractClause
- EF config voi unique FormCode + query filter IsDeleted
- DbSets + IApplicationDbContext update
- Migration AddForms (bang 14 total)
- Packages: DocumentFormat.OpenXml 3.x + ClosedXML 0.105+
- Application/Forms:
  - IFormRenderer interface + RenderResult record
  - FormFeatures.cs: List/Get/Render CQRS
  - IWebHostEnvironmentLocator (abstract IWebHostEnvironment)
- Infrastructure/Forms:
  - DocxRenderer: OpenXml-based placeholder {{field}} replace, handle split runs (gom text tat ca <w:t> trong paragraph, replace, gan lai text dau + clear rest)
  - XlsxRenderer: ClosedXML cell value replace
  - FormRenderer router theo format docx/xlsx
- Api:
  - FormsController: GET /templates (filter type, onlyActive), GET /templates/{id}, POST /templates/{id}/render (return file)
  - WebHostEnvironmentLocator impl
- DbInitializer SeedContractTemplatesAsync: seed 8 template metadata, IsActive=true chi khi file ton tai

Templates vat ly:
- Copy 5 .docx/.xlsx tu FORM/ sang wwwroot/templates/
- 3 .doc (FO-002.02/03/06) chua convert: IsActive=false (Word COM bi stuck luc test, can retry voi DisplayAlerts=0 hoac LibreOffice)
- scripts/convert-doc-to-docx.ps1 (Word COM automation)

Frontend fe-admin:
- types/forms.ts: ContractTemplate + ContractTypeLabel
- pages/forms/FormsPage.tsx: list templates + Render dialog (paste JSON data → download .docx/.xlsx)
- Route /forms them vao App.tsx

Bug fix:
- SpaceProcessingModeValues namespace: wrap EnumValue<> full path
- SaveAs2($path, 16) thay vi SaveAs([ref], [ref]) — PowerShell type issue
- Word COM stuck: kill process, skip .doc cho MVP

Docs (theo yeu cau user):
- docs/gotchas.md MOI: 17 pitfalls nhom theo tech stack / EF Core / OpenXml / JSON / dev workflow
- .claude/skills/form-engine/SKILL.md: placeholder → full spec (algorithm + code pointers + API + limitations)
- .claude/skills/permission-matrix/SKILL.md: placeholder → full spec (BE policy + FE guard + seed + pitfalls)
- docs/HANDOFF.md MOI: brief 5 phut cho session sau (run quickstart + where we are + next steps + file tree + gotchas ref)
- docs/STATUS.md: update cumulative stats + next up Phase 3
- docs/changelog/migration-todos.md: tick Phase 2 iteration 1 items + add iteration 2 list
- docs/changelog/sessions/2026-04-21-1200-phase2-form-engine.md: session log
- CLAUDE.md root: them reference den gotchas + HANDOFF

E2E verified:
- GET /api/forms/templates (onlyActive=false) → 8 templates
- POST /api/forms/templates/{FO-002.05}/render voi data dict → HTTP 200 + file .docx 482KB (Microsoft Word 2007+ OK)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 12:01:11 +07:00
54d6c9ba52 [CLAUDE] Phase1.2: CRUD Master + Permission Matrix + FE admin pages
Backend:
- Domain/Master: Supplier (+ SupplierType 5 loai), Project, Department (AuditableEntity)
- Domain/Identity: MenuItem, Permission, MenuKeys const (12 menu)
- EF Configurations voi unique Code + query filter IsDeleted
- DbSets + IApplicationDbContext interface update
- Application: PagedResult + PagedRequest generic
- Application/Master CQRS CRUD 3 entity (Create/Update/Delete/Get/List voi paging search sort)
- Application/Permissions: GetMyMenuTree (union OR role, filter tree), ListMenuItems, ListPermissionsByRole, UpsertPermission (guard admin khong tu giam quyen), ListRoles
- Api/Authorization: MenuPermissionRequirement + Handler (Admin bypass, query DB)
- Program.cs: register 48 policy {menu}.{action} tu MenuKeys x Actions
- Api/Controllers: Suppliers, Projects, Departments, Menus, Roles, Permissions
- DbInitializer: seed 12 menu + admin full CRUD permissions
- Migration AddMasterData + AddPermissions

Frontend (fe-admin):
- Types: menuKeys.ts const, menu.ts (MenuNode/Role/Permission), master.ts (Supplier/Project/Department + SupplierType const-object)
- AuthContext: load menu from /menus/me, cache localStorage, refreshMenu()
- usePermission hook + PermissionGuard component (wrap button)
- UI kit them: Dialog (modal overlay), Textarea, Select
- Generic: DataTable (column config, sortable, loading, empty) + Pagination
- PageHeader component
- apiError helper extract message tu ProblemDetails
- Layout rewrite: render menu dong tu AuthContext.menu (MenuGroup collapsible + NavLink + lucide icon map)
- Pages: master/Suppliers, master/Projects, master/Departments (CRUD + search + sort + paging + Dialog form)
- Page system/Permissions: ma tran Role x MenuKey x CRUD checkbox (tick tu dong PUT upsert)
- App.tsx them 4 route moi

Bug fix:
- MenuPermissionHandler: EF expression tree khong support switch expression -> tach switch ra ngoai AnyAsync
- TS erasableSyntaxOnly khong cho enum -> SupplierType const-object pattern (typeof[keyof])

E2E verified via Vite proxy:
- GET /menus/me -> 6 root + 6 child nodes (12 menus)
- GET /roles -> 12 roles
- POST/GET/PUT/DELETE /suppliers -> full CRUD, soft delete OK
- tsc -b fe-admin pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 11:30:14 +07:00
702411fcc8 [CLAUDE] Phase1: foundation - BE Clean Arch + Identity + JWT + 2 FE React + login E2E
Backend (.NET 10):
- Domain: BaseEntity/AuditableEntity, ContractType/Phase/ApprovalDecision enums, User/Role (Identity<Guid>), AppRoles (12 const)
- Application: IApplicationDbContext/ICurrentUser/IDateTime/IJwtTokenService, custom exceptions, ValidationBehavior (MediatR pipeline), Auth CQRS (Login/Refresh/Me), DependencyInjection
- Infrastructure: ApplicationDbContext (IdentityDbContext), AuditingInterceptor (auto audit + soft delete), DbInitializer (seed 12 role + admin), DesignTimeDbContextFactory, JwtTokenService, DateTimeService, DI
- Api: CurrentUserService, GlobalExceptionMiddleware (ProblemDetails), AuthController, Program.cs rewrite (Serilog + JWT + CORS + Swagger), appsettings + launchSettings (port 5443)
- Migration Init applied to SolutionErp_Dev LocalDB

Frontend (React 19 + Vite 8 + Tailwind 4):
- fe-admin (:8082 blue) + fe-user (:8080 emerald) - shared structure, khac menu + brand color
- Tailwind 4 via @tailwindcss/vite plugin, theme brand colors
- AuthContext (localStorage token), ProtectedRoute, Layout (sidebar + header)
- UI kit: Button/Input/Label (CVA + Tailwind)
- LoginPage voi toast error, DashboardPage/InboxPage placeholder
- Axios interceptor: auto Bearer + 401 redirect
- TanStack Query client, React Router 7, Sonner toast

Package downgrades (do .NET 10 / TS 6 compat):
- MediatR 14 -> 12.4.1 (v14 breaking changes)
- Swashbuckle 10 -> 6.9.0 (v10 khong tuong thich OpenApi 2)
- Removed Microsoft.AspNetCore.OpenApi (conflict voi Swashbuckle)

E2E verified: POST /api/auth/login qua Vite proxy ca 2 FE -> JWT + user info

Credentials seed: admin@solutionerp.local / Admin@123456

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 10:59:44 +07:00
25dad7f36f [CLAUDE] Scaffold: khoi tao SOLUTION_ERP Phase 0
- .NET 10 Clean Architecture: Domain/Application/Infrastructure/Api (4 project)
- 2 React + Vite + TS app: fe-admin (:8082), fe-user (:8080) voi proxy /api
- Node engines >=20, .nvmrc = 20 cho CI (bai hoc NamGroup)
- SQL Server 2022 qua docker-compose (dev)
- Parse 8 FORM -> docs/forms-spec.md (catalog + ma HD format RG-001)
- Parse QUY_TRINH -> docs/workflow-contract.md (9 phase state machine + role matrix)
- docs: CLAUDE.md, STATUS.md, PROJECT-MAP.md, migration-todos.md (roadmap 5 phase)
- .claude/skills: 3 placeholder (contract-workflow, form-engine, permission-matrix)

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