Files
solution-erp/docs/changelog/sessions/2026-04-23-2200-roles-demo-pending-cleanup.md
pqhuy1987 a7ea6ad3d6
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
[CLAUDE] Docs: chốt session 2026-04-23 tối — UAT-ready 100%
5 commit feature work session 2 (072ad6dbcdc007) — clear hết task
pending high-impact, sẵn sàng UAT.

## Session log mới

`docs/changelog/sessions/2026-04-23-2200-roles-demo-pending-cleanup.md`
~270 dòng — outcome A→G (RolesPage + 7 demo HĐ + User-kind guard +
Warning SLA + Edit detail + Master expand + Deps script), stats,
architectural notes, next priority.

## STATUS.md

- Last updated 2026-04-23 22:30 — UAT-ready 100%
- Header phase: "Pending tasks cleared — UAT-ready"
- 5 Recently Done row mới (RolesPage, demo HĐ, user-kind+SLA, edit
  detail+deps, master expand+backfill)
- Cumulative table thêm cột "+RolesPg+Demo+Pending":
  - LOC ~7800 → ~8800
  - Endpoints ~80 → ~93
  - FE pages ~22 → ~23 (RolesPage)
  - Scripts 4 → 5 (deps-audit.ps1)
  - Demo data 5+3 → 15+8+7+13+4 (NCC/Project/HĐ/User/Catalogs)
  - Commits ~47 → ~52
- Session log link mới
- Skills count = 14 file

## HANDOFF.md

- TL;DR cập nhật: UAT-ready 100%, demo data đầy đủ inventory
- Phase table thêm 6 row Done (RolesPage, demo HĐ, user-kind+SLA,
  edit detail, master expand, deps script)
- Git state 8 commit gần nhất

## migration-todos.md

- Section "Session 2026-04-23 (tối)" với 8 ticked checkbox + commit refs
- 3 Roles CRUD checkbox legacy → ticked với commit 072ad6d ref

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 16:07:20 +07:00

236 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Session 2026-04-23 ~22:00 — RolesPage + 7 demo HĐ + clear pending tasks
**Focus:** Phiên 3 trong ngày — sau session toolkit/4-bảng/master/roles VN
(15:00, ff5e35f). Build trang `/system/roles` đã trỏ về placeholder, sau
đó xử lý hết task pending (demo HĐ + user-kind approver + warning SLA +
edit detail row + deps script + mở rộng master + backfill HĐ).
5 commit (072ad6d → bcdc007), 0 migration mới, BE pass.
## Outcomes
### A. RolesPage CRUD `/system/roles` ✓
User screenshot: trang trỏ "Trang này chưa được build". Build full CRUD:
**BE (`PermissionFeatures.cs` + `RolesController.cs`):**
- `CreateRoleCommand` — Name regex `^[A-Za-z][A-Za-z0-9_]*$` (chỉ chữ/số/
underscore, bắt đầu chữ), throw ConflictException nếu code đã tồn tại
- `UpdateRoleCommand` — CHỈ update ShortName + Description (Name là FK
trong UserRoles + WorkflowStepApprover.AssignmentValue + [Authorize]
attr — không đổi)
- `DeleteRoleCommand` — block 2 trường hợp:
* Role thuộc AppRoles.All (12 hardcoded)
* Còn user assigned (UserManager.GetUsersInRoleAsync count > 0)
- 3 endpoint mới (POST/PUT/DELETE Authorize Admin)
**FE (`fe-admin/src/pages/system/RolesPage.tsx` ~280 dòng):**
- Table list 12 mặc định + custom với 5 column (Mã code / Mã viết tắt /
Tên đầy đủ / Loại badge / Ngày tạo) + Edit + Delete actions
- Edit dialog: Name disabled với hint, ShortName + Description editable
- Delete: HARDCODED_ROLES set client-side check → toast error nếu role
mặc định, BE double-check
- Create dialog: 3 field + regex pattern HTML5
- Banner amber warning Mã code FK constraint
- Loại badge: Mặc định (slate) vs Tùy chỉnh (brand)
Commit: `072ad6d`
### B. SeedDemoContractsAsync — 7 HĐ varied phases ✓
User feedback: "Demo User đi" → tạo demo data toàn diện cho UAT-ready.
7 HĐ covering 7 ContractTypes + varied final phases:
| # | Type | Final Phase | Tên | Giá trị | Details |
|---|---|---|---|---|---|
| 1 | ThauPhu | DangSoanThao | Thi công móng + cột tầng 1 — FLOCK 01 | 850M | 3 hạng mục |
| 2 | GiaoKhoan | DangGopY | Khoán nhân công xây/trát/sơn | 320M | 3 CV + 2 comments |
| 3 | NhaCungCap | DangInKy | Cung cấp xi măng + sắt thép Q2/2026 | 1.2B | 3 SP |
| 4 | DichVu | DangTrinhKy | Thuê cẩu tháp 6 tháng | 540M | 1 DV |
| 5 | MuaBan | DaPhatHanh | Mua máy phát điện 250kVA | 850M | 2 SP có VAT 10% |
| 6 | NguyenTacNCC | DangGopY | HĐ nguyên tắc cung cấp vật tư 2026 | 0 (framework) | 2 SP với khung giá |
| 7 | NguyenTacDV | DangSoanThao | HĐ nguyên tắc bảo trì TB 2026 | 0 (framework) | 1 DV với SLA |
Workflow simulation: loop transition phases tới finalPhase, insert
ContractApproval row mỗi phase với ApproverUserId mapping đúng role
(CCM → ccm.tran, BOD → bod.huynh, HRA → hra.dang, Drafter → qs.hoang).
SkipCcm policy bỏ DangKiemTraCCM khỏi flow. Mã HĐ auto gen RG-001.
Idempotent: skip nếu đã có HĐ tên `[DEMO]` (path 1 create / path 2
backfill — xem section F).
Commit: `8bc9565`
### C. User-kind approver runtime 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)
- Fallback User-kind: actorUserId.ToString() có trong UserTransitions?
**ContractWorkflowService.TransitionAsync** dùng IsTransitionAllowed
helper. Error message thêm "{N} user explicit" nếu policy có User-kind.
**FromDefinition** 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 (cấu hình broken).
Commit (gộp với D): `4edcd58`
### D. Warning 20% SLA notification ✓
`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 7d → 33.6h
remaining trigger; In ký 1d → 4.8h)
- Nếu remaining ≤ threshold + còn slot → notify Drafter via
INotificationService với NotificationType.SlaWarning + title icon ⚠
- Set SlaWarningSent = true để chỉ warning 1 lần per phase (reset trong
TransitionAsync khi chuyển phase mới)
Commit: `4edcd58`
### E. Edit detail row inline ✓
User cần edit hạng mục mà không phải xóa-thêm lại.
**BE — 7 typed UpdateXxxDetailCommand handler trong ContractDetailsFeatures:**
- EnsureContractType guard + log ChangelogAction.Update với summary
"Sửa <hạng mục/SP/CV/...>"
- 7 PUT endpoints `/contracts/{id}/details/{slug}/{detailId}`
**FE — `ContractDetailsTab.tsx` refactor:**
- DeleteBtn → ActionBtns (Pencil + Trash) với onEdit + onDelete callbacks
- 7 XxxTable signatures + onEdit prop + pass row data
- New `EditRowDialog` component:
* useEffect populate form từ row data khi target thay đổi
* Reuse FIELDS_BY_TYPE config + buildPayload (compute thanhTien)
* Date field convert ISO → yyyy-MM-dd cho input[type=date]
* PUT /contracts/{id}/details/{slug}/{detailId}
- Parent state editTarget — open dialog, close khi save thành công
Mirror fe-admin (file copy).
Commit: `e53cd3a`
### F. Mở rộng master data + backfill demo HĐ ✓
User feedback: "thêm master data NCC + Project + cập nhật HĐ đang
thiếu/trùng nhau".
**SeedDemoMasterDataAsync — per-Code idempotent (thay vì per-table):**
- 15 demo suppliers (5 SupplierType × 3 entities mỗi loại):
- NhaCungCap: VLXD-ABC, Xi măng Hà Tiên, Thép Hòa Phát, Cadivi, Tiền Phong
- NhaThauPhu: Thăng Long XD, Cô Công, MEP Hà Nội, Next Stage
- ToDoi: Hoàng Nam, Bắc Ninh xây trát, Hà Trang ốp lát
- DonViDichVu: Clean Pro, Hồng Phát Vận chuyển, Long An Bảo vệ
- ChuDauTu: Vingroup, Sun Group, Masterise Homes
- 8 demo projects (đa dạng quy mô + giai đoạn):
- FLOCK01 / SKYGARDEN / INDUSTRIAL (existing)
- VHOMES-OP (350B), ECOPARK-VL (180B), BWTOWER (95B),
WAREHOUSE-LB (32B), RESORT-PQ (220B)
**Backfill demo HĐ (BackfillDemoContractsSupplierProjectAsync):**
Trước: 7 HĐ tất cả dùng cùng 1 supplier+project (FirstOrDefault) → list
nhìn không thực tế.
Sau: maps `DemoSupplierByType` + `DemoProjectByType` đa dạng theo loại:
| ContractType | Supplier | Project |
|---|---|---|
| HopDongThauPhu | NTP-XD | FLOCK01 |
| HopDongGiaoKhoan | TD-NEHOANG | FLOCK01 |
| HopDongNhaCungCap | NCC-XIMANG | VHOMES-OP |
| HopDongDichVu | DV-VANCHUYEN | RESORT-PQ |
| HopDongMuaBan | NCC-DIEN | BWTOWER |
| HopDongNguyenTacNCC | NCC-THEP | ECOPARK-VL |
| HopDongNguyenTacDichVu | DV-CLEAN | WAREHOUSE-LB |
`SeedDemoContractsAsync` 2 path:
1. Lần đầu (no [DEMO] HĐ): tạo mới với supplier+project diverse per type
2. Đã có [DEMO]: gọi BackfillDemoContractsSupplierProjectAsync — loop
từng demo HĐ, update supplier_id + project_id nếu mismatch (idempotent)
Commit: `bcdc007`
### G. Deps audit helper script ✓
`scripts/deps-audit.ps1` — chạy thủ công hoặc CI gate:
- `dotnet list SolutionErp.slnx package --vulnerable --include-transitive` (BE)
- `npm audit --audit-level=moderate` (fe-admin + fe-user)
- Color-coded output (green/red), summary cuối
- `-FailOnHigh` switch để CI gate
Skill ref: `.claude/skills/dependency-audit-erp/SKILL.md` cho pin
constraints + workflow fix.
Commit (cùng E): `e53cd3a`
## Stats
| | Trước session | Sau session | Δ |
|---|---|---|---|
| BE LOC | ~7800 | ~8800 | +1000 |
| DB tables | 36 | 36 | 0 (chỉ add seed data) |
| Migrations | 11 | 11 | 0 |
| API endpoints | ~80 | **~93** | +3 Roles + 7 Update Detail + 3 dùng cũ vẫn |
| FE pages | ~22 | **~23** | +RolesPage |
| FE components | many | + EditRowDialog | refactor ActionBtns |
| Scripts PS | 4 | **5** | +deps-audit.ps1 |
| Demo data | 5 NCC + 3 Project + 0 HĐ | **15 NCC + 8 Project + 7 HĐ** | massive expand |
| Commits session này | — | **5** | 072ad6d → bcdc007 |
## Architectural notes
1. **Role.Name English giữ nguyên** — FK + [Authorize] attr không break.
ShortName + Description VN cho display layer. Edit chỉ 2 field này.
2. **DeleteRole defense in depth** — FE check HARDCODED_ROLES + BE check
AppRoles.All + BE count UserRoles → 3 lớp prevent illegal delete.
3. **User-kind approver guard** = optional fallback, không thay Role-
based. Step có cả Role + User → bất kỳ match nào pass.
4. **Demo data per-Code idempotent** — admin add custom supplier/project
không bị clobber khi restart. Backfill chỉ update mismatched [DEMO] HĐ.
5. **Edit detail tách typed UpdateCommand** — verbose nhưng TS strict
FE bắt typo + EF tracker không nhầm entity.
## Cron audit fire 2026-05-01
`solution-erp-skill-audit-monthly` (đã set ở session 2026-04-23 09:00)
sẽ chạy 9:00 AM 1/5 — log vào `docs/changelog/skill-audit-2026-05.md`
+ commit auto. Self-contained prompt, không cần context session hiện tại.
## Next session priority (chỉ còn blockers user/ops)
1. **UAT 2-3 user thật** — giờ có 13 demo accounts + 7 demo HĐ + 15 NCC
+ 8 Project + 4 catalogs đầy đủ → UAT-ready 100%
2. Email outbox (chờ SMTP)
3. Rotate creds (admin + 13 demo + SA + vrapp + JWT)
4. SQL backup Task Scheduler (script `scripts/backup-sql.ps1` đã có)
**Optional minor:**
- PermissionsPage grant `Workflows.Read` cho non-admin role → menu Wf_*
visible (1 click trong matrix)
- E2E test với 13 demo users (login từng role, chuyển phase đầy đủ flow)
- Run `pwsh scripts/deps-audit.ps1` mỗi đầu tuần check vuln
**Done in this session (clear hết high-impact pending):**
- ✅ Roles CRUD admin
- ✅ User-kind approver runtime guard
- ✅ Warning 20% SLA notification
- ✅ Edit detail row inline (7 typed Update commands + EditRowDialog)
- ✅ 7 demo contracts seed varied phases + details + comments
- ✅ Master data expand 5→15 NCC + 3→8 Project
- ✅ Backfill demo HĐ assign supplier+project diverse
- ✅ Deps audit helper script