Session 18 (16:56 → 19:45, 7 commit `aaa1c6c` → `32a8d4d`): - B1 Pe Duyệt filter cứng "Đã gửi duyệt" - B2 HistoryTab filter Trả lại / Gửi lại - B3 Clone V2 cho B (DuyetNccPhuongAn) — audit reuse pattern - B4 Fix silent 403 ApprovalWorkflowsV2Controller - B5 Fix sidebar highlight queryMatches transient keys - B6 Mig 25 IsUserSelectable + Designer pin toggle + bỏ "(clone)" + Workspace filter - B7 Cleanup orphan zip files Updates: - STATUS — header 24→25 mig + 43→44 gotcha + 1 row Recently Done top + session log link - HANDOFF — TL;DR S18 đầy đủ + cảnh báo S19+ (giữ S17 narrative §6.5) - CLAUDE.md root — count 25 mig + Mig 25 description block - schema-diagram §14 — heading 22→25 + cột IsUserSelectable + filter logic section + Pending S19+ Mig 26/27 - gotchas — +#44 silent 403 + checklist debug 21 - migration-todos — Phase 9 S18 done section - session log mới đầy đủ E2E narrative Stats: 25 mig, 58 tables, ~141 endpoints, 81 test pass (no change), 44 gotcha, 14 memory entries, 6 skill, 7 commit S18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
77 KiB
HANDOFF — Brief 5 phút cho session tiếp theo
Last updated: 2026-05-08 19:45 (Session 18 wrap-up — 🎯 PE V2 polish + Clone B (DuyetNccPhuongAn) + 4 bug fix UAT + Mig 25 IsUserSelectable. 7 commit aaa1c6c → 32a8d4d. Audit reuse pattern (memory feedback_audit_reuse_before_clone): clone B chỉ 3 file ~60 LOC vì schema chung qua ApplicableType discriminator. Bug silent 403 từ class-level Authorize policy quá strict — Drafter không list workflow để pick, Workspace dropdown empty không warning. Fix: class-level [Authorize] only, GET endpoint không cần Workflows.Read. Bug sidebar highlight mất khi click row do queryMatches exact-set vs URL có id transient → strip TRANSIENT_QUERY_KEYS trước compare. Mig 25 ALTER ApprovalWorkflows +IsUserSelectable bit (admin pin/unpin per version, multi-select, độc lập IsActive). Designer +badge "Cho user chọn" + button Ghim/Bỏ ghim. Workspace filter only IsUserSelectable. Bỏ "(clone)" auto-suffix khi clone version. Pe Duyệt: bỏ dropdown trạng thái + filter cứng "Đã gửi duyệt". Lịch sử thay đổi: chỉ events Trả lại / Gửi lại / sửa khi phase=TraLai (BE keep audit, FE filter). 81 test pass (no change — UAT defer test §7). 44 gotcha (+1 silent 403).)
TL;DR Session 18 — PE V2 polish + Clone B + 4 bug fix UAT
User UAT live tiếp Session 17, 7 batch nhỏ + 1 feature lớn (Mig 25). Áp memory feedback_uat_skip_verify (skip dotnet test mỗi chunk, push ngay) + lesson 0ae3fe2: rename/remove → BẮT BUỘC npm run build.
B1 (aaa1c6c) — Pe Duyệt filter cứng "Đã gửi duyệt"
User: "Duyệt bỏ cái trạng thái đi, chỉ load những trạng thái 'Đã gửi duyệt' là đc."
- Bỏ dropdown "Tất cả trạng thái" khỏi UI khi
pendingMe=true, thay bằng hint amber "Lọc cố định: Đã gửi duyệt (phiếu đang chờ duyệt)" - Filter cứng client-side:
getPeDisplayStatus(p.phase) === DaGuiDuyet— loại Nháp/Trả lại/Đã duyệt/Từ chối - Header count dùng
rows.lengthkhipendingMe(inbox không paged) - Workaround BE
/inboxloose UAT có thể trả phiếu Nháp (phân quyền strict V2 pending Session 19+) - Mirror fe-admin + fe-user
PurchaseEvaluationsListPage.tsx
B2 (917446d) — HistoryTab filter Trả lại / Gửi duyệt lại
User: "Lịch sử thay đổi: chỉ bắt các dòng thay đổi khi trả lại và gửi duyệt lại thôi nhé, không cần bắt trạng thái duyệt và các thay đổi trước khi trả lại."
- FE filter trong
PeDetailTabs.HistoryTab, BE giữ audit data đầy đủ (reversible nếu user đổi ý / cần audit trail compliance) - Logic giữ:
- Workflow transition về TraLai (
phaseAtChange === 98) - Workflow transition từ TraLai (summary chứa
"TraLai →") - Mọi thay đổi nội dung (Header/Detail/Supplier/Quote/Attachment) khi
phaseAtChange === 98
- Workflow transition về TraLai (
- Bỏ: workflow Approve cùng cấp (Cấp 1→2→DaDuyet), sửa khi phase=Nháp/ChoDuyet ban đầu
- Empty state: "Chưa có lịch sử trả lại / gửi duyệt lại"
B3 (937eb24) — Clone V2 cho B (DuyetNccPhuongAn)
User: "Quy trình chọn thầu phụ - NCC → Duyệt NCC đúng. Plan kế hoạch clone toàn bộ updates sang Duyệt NCC và Giải pháp."
Audit reuse trước thay vì duplicate. Phát hiện 80% đã chung:
- Schema V2 (Mig 22-24) qua
ApplicableTypeenum - BE Service
ApproveV2Asynckhông hardcode type - App CQRS / API
/approval-workflows-v2?applicableType=Ndynamic - FE Designer
ApprovalWorkflowsV2PagecóTYPE_CODE_TO_INTcả 3 type - Layout regex
^AwV2_(.+)$match dynamic typeCode - App.tsx route
/system/approval-workflows-v2/:typeCodedynamic
Chỉ thiếu cho B: menu key + sample seed (3 file ~60 LOC).
MenuKeys.cs+constApprovalWorkflowDuyetNccPhuongAnV2 = "AwV2_DuyetNccPhuongAn"+ add vàoAll[]DbInitializer.SeedMenusAsync+leaf "Duyệt NCC và Giải pháp (Mới)" dưới root ApprovalWorkflowsV2 (Order=2 cạnh leaf A Order=1)DbInitializer +SeedSampleApprovalWorkflowsV2Async(idempotent — skip nếu admin đã tạo workflow B nào, hoặc thiếu test usernv.test/Phòng CCM): seedQT-DN-PA-V2-001 v011 Bước Phòng CCM × 1 Cấp NV testfe-admin/lib/menuKeys.ts+AwV2_DuyetNccPhuongAn
KHÔNG migration / Service / Designer page mới. Memory feedback_audit_reuse_before_clone.md capture pattern.
User feedback "OK khá tốt, 1 phát chạy luôn :))" sau verify → confirm approach.
B4 (f77ea38) — Fix silent 403 ApprovalWorkflowsV2Controller
Triệu chứng: Drafter nv.test Workspace tạo phiếu B → dropdown "Quy trình duyệt" empty mặc dù Admin Designer thấy 2 version (v01 sample + v02 admin clone).
Root cause: Class-level [Authorize(Policy = "Workflows.Read")] → non-admin role 403 Forbidden khi GET /api/approval-workflows-v2. TanStack Query catch error không hiện UI → dropdown rỗng silent.
Fix:
- Class-level đổi
[Authorize]only (any authenticated user) - GET endpoint inherit class policy — Drafter list workflow để pick read-only, không nhạy cảm
- POST + DELETE giữ
[Authorize(Policy = "Workflows.Create")]admin-only Designer
Pattern reusable cho Contract V2 Mig 26 sau.
B5 (a9c0857) — Fix sidebar highlight queryMatches transient keys
Triệu chứng: Ở leaf "Danh sách" /purchase-evaluations?type=1, click chọn 1 phiếu → URL thành ?type=1&id=abc → leaf bị mất highlight box (gotcha #34 cũ tái phát theo cách khác).
Root cause: queryMatches exact-set equality — target {type} (1 key) vs current {type, id} (2 keys) length mismatch → no match → leaf unhighlight.
Fix: TRANSIENT_QUERY_KEYS = {id, q, editHeader, page, phase, awId} — strip trước khi compare. Mọi key navigation identity (type, pendingMe, mode) check exact-set như cũ.
Edge cases verified:
| URL hiện tại | Target leaf | Match |
|---|---|---|
?type=1&id=abc |
Danh sách ?type=1 |
✓ giữ highlight |
?type=1&pendingMe=1 |
Danh sách ?type=1 |
✗ distinct (không cross-highlight Pending) |
?type=1&phase=10 |
Danh sách ?type=1 |
✓ giữ highlight (filter dropdown) |
?type=1&pendingMe=1&awId=xyz |
Duyệt ?type=1&pendingMe=1 |
✓ giữ highlight |
Mirror fe-admin + fe-user Layout.tsx.
B6 (2a53107) — Mig 25 IsUserSelectable + Designer pin toggle + bỏ "(clone)"
User feedback xem Admin Designer: "Bỏ chữ Clone đi nhé, ghi v02, v03... là đủ rồi. Thêm cho tao nút stick để chọn các quy trình nào mà User đc select bên ngoài khi tạo phiếu."
Bỏ "(clone)": Designer auto-fill name = cloneFrom.name (bỏ (clone) suffix). Version số đã đủ phân biệt.
Pin toggle "Cho user chọn":
- Migration 25
AddIsUserSelectableToApprovalWorkflows:ALTER TABLE ApprovalWorkflows ADD IsUserSelectable bit NOT NULL DEFAULT 0; -- Backfill (giữ behavior cũ — active workflows vẫn pickable): UPDATE ApprovalWorkflows SET IsUserSelectable = 1 WHERE IsActive = 1; - Domain
ApprovalWorkflow.IsUserSelectable— independent vớiIsActive, multiple versions có thể cùng selectable (admin có thể "ghim" nhiều version cho user pick). - App CQRS:
AwDefinitionDto+fieldIsUserSelectableCreateAwDefinitionCommandHandler set defaulttruecho version mới (mirror IsActive default)- New
SetAwUserSelectableCommand(Guid Id, bool IsUserSelectable)+ Handler — toggle
- API
PATCH /api/approval-workflows-v2/{id}/user-selectablepolicyWorkflows.Create(admin only) - DbInitializer
SeedSampleApprovalWorkflowsV2Async+IsUserSelectable = true - FE Designer (
fe-admin/ApprovalWorkflowsV2Page.tsx):DefinitionDto+isUserSelectable- Badge amber "📌 Cho user chọn" cạnh badge IsActive/Archived khi
isUserSelectable === true - Button "📌 Ghim cho user / 🚫 Bỏ ghim" trong action group + mutation
toggleSelectable(call PATCH endpoint, invalidate query)
- FE Workspace (cả fe-admin + fe-user
PeWorkspaceCreateView.tsx):- approvalWorkflows query
.filter(w => w.isUserSelectable)— chỉ workflows admin đã ghim hiện trong dropdown user
- approvalWorkflows query
B7 (32a8d4d) — Cleanup orphan zip files
.claude.zip + docs.zip từ harness session start lỡ tay vào git add -A ở B6 commit. Untrack + add *.zip rule .gitignore.
Stats Δ Session 18
| Trước S18 | Sau S18 | |
|---|---|---|
| Migrations | 24 | 25 (+1) |
| DB tables | 58 | 58 (Mig 25 chỉ ALTER cột) |
| API endpoints | ~140 | ~141 (+1 PATCH user-selectable) |
| FE pages | 33 | 33 (modify existing only) |
| Test pass | 81 | 81 (no change — UAT feature defer test §7) |
| Gotchas | 43 | 44 (+1 silent 403) |
| Memory entries | 13 | 14 (+1 audit reuse pattern) |
| Skills | 6 | 6 (no add) |
| Commits | (after S17) | +7 |
⚠️ Điều quan trọng cho Session 19+
-
Contract V2 wire (Mig 26) — pending dedicated session. Pattern audit-reuse áp dụng: phần lớn đã chung. Mirror PE pattern:
- Thêm
Contract.ApprovalWorkflowId+CurrentApprovalLevelOrder(Mig 26) ContractWorkflowService.ApproveV2Asyncmirror PE patternContractCreatePageWorkspace Select V2- Pin V2 mặc định cho ContractType
- Permission GET endpoint đã permissive (Session 18 fix), không cần đụng
- Thêm
-
Phân quyền strict V2 V2 — hiện loose UAT (mọi authenticated thấy mọi phiếu V2). Sau confirm flow:
- List = Drafter + approver any-Step + Admin
- Inbox = chỉ approver Cấp hiện tại (V2 đã đúng —
ResolveV2InboxIdsAsync) - Detail = same as List
- Cũng giải quyết được bug "/inbox loose trả phiếu Nháp" — sau khi strict, B1 FE filter có thể relax nếu BE đã filter đúng
-
Drop legacy V1 cleanup sau khi không còn phiếu pin
WorkflowDefinitionId(V1):- Drop tables
WorkflowDefinitions+WorkflowSteps+WorkflowStepApprovers+ PE versions - Mig 27 cleanup drop column
RejectedAtStepIndex+RejectedFromPhasedeprecated S17 - Drop
ApproveV1LegacyAsyncbranch trong Service
- Drop tables
-
Test V2 wire (defer khi UAT confirm + có sample data) — Domain test
ApproveV2Asyncmatch logic + transient TraLai entry → Cấp 1 reset. -
feedback_audit_reuse_before_clonememory — áp dụng cho mọi "clone X sang Y" / "thêm type Z mới" sau này. List "đã chung" vs "còn thiếu" trước khi propose plan. -
Sample seed B sample chạy với check
hasAnyB— sau UAT có thể remove sample seed (admin đã tạo workflow thật). Hoặc giữ làm fallback. Idempotent skip nếu admin có workflow B → không clobber.
TL;DR Session 17 — PE V2 schema end-to-end
TL;DR Session 17 — PE V2 schema end-to-end
User chốt sau Session 16 (drastic refactor flat Mig 21 vẫn sai intent): viết lại schema riêng + thêm Menu "Duyệt NCC (Mới)" với cấu trúc explicit:
Mã Quy trình - Tên Quy trình
* Bước 1 - Phòng A
* Cấp 1 - NV X ← 1 user CỤ THỂ qua ApproverUserId
* Cấp 2 - NV Y
* Bước 2 - Phòng B
* Cấp 1 - NV Z
Khác Mig 21: mỗi Cấp = 1 NV chính xác, KHÔNG OR-of-many group Dept+PositionLevel/Role/User.
4 commit (3 chunk per-commit + docs):
Chunk A (c847dc0) — Domain + EF + Mig 22 + Menu
- Domain
ApprovalWorkflowsV2/ApprovalWorkflow.cs— 3 entity (ApprovalWorkflow + Step + Level) + enumApprovalWorkflowApplicableType(DuyetNcc=1 / DuyetNccPhuongAn=2 / Contract=3) - EF
ApprovalWorkflowConfiguration.cs— UNIQUE (Code, Version), FK Cascade Step→Workflow + Level→Step, FK Restrict Department + ApproverUserId - ApplicationDbContext +3 DbSet
- Migration 22
AddApprovalWorkflowsV2— 3 CREATE TABLE + 1 UNIQUE + 4 INDEX. Applied cả_Design+_DevLocalDB - DbInitializer SeedMenusAsync: +menu
ApprovalWorkflowsV2root dưới System (icon Workflow) + leafAwV2_DuyetNcc(icon FileCheck, label "Duyệt NCC (Mới)") - MenuKeys.cs +2 const trong All array
Chunk B (f6047d5) — Application CQRS + API
Application/ApprovalWorkflowsV2/ApprovalWorkflowV2AdminFeatures.cs:GetAwAdminOverviewQuery(ApplicableType?)— load 3-level Include + dept/user names mapCreateAwDefinitionCommand+ Validator — auto-increment Version theo Code, deactivate active version cùng ApplicableTypeDeleteAwDefinitionCommand— UAT helper unconditional (chưa pin)- DTO AwDefinition/AwStep/AwLevel + AwTypeSummary
- IApplicationDbContext +3 DbSet
Api/Controllers/ApprovalWorkflowsV2Controller— route/api/approval-workflows-v2, GET ?applicableType=N | POST | DELETE/{id}, reuse policyWorkflows.Read+Workflows.Create
Chunk C (2781c7e) — FE Designer
fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx(~480 LOC)- Overview cards Active+History per ApplicableType
- DefinitionCard read-only: Bước (badge phòng emerald) → Cấp (badge violet C1/C2 + tên NV + email)
- Designer dialog: Mã/Tên/Mô tả + Add/Remove Step + reorder (chevron up/down) + Add/Remove Level + Select Phòng + Select NV duyệt
- Validate: mỗi Step ≥1 Level, mỗi Level phải có ApproverUserId
- Auto-assign code mặc định theo type:
QT-DN-V2-001/QT-DN-PA-V2-001/QT-HD-V2-001
- Layout.tsx resolver +ApprovalWorkflowsV2 root →
/system/approval-workflows-v2, +AwV2_ leaf →/system/approval-workflows-v2/<code> - App.tsx +2 route
- menuKeys.ts +2 const sync với BE
Chunk D — Docs
STATUS + HANDOFF + project_solution_erp.md memory.
Chunk E (UAT iteration, 9 commit) — Designer fix + State machine + Service wire + UX
User UAT iter Designer V2 phát hiện multiple issues + chốt spec dần qua state diagram. Per memory feedback_uat_skip_verify.md UAT mode iterate nhanh:
| Commit | Tóm tắt |
|---|---|
9712778 |
Designer iter 1 lock 3 cấp/bước (sai intent) |
f3bea3c |
Designer iter 2 đúng intent: max 3 cấp × N NV/cấp + sequential gating C2/C3 disabled khi prev empty + filter NV theo Phòng + no-dup same level. Validator BE strict |
ff21120 |
State machine 5 trạng thái Nháp/ĐãGửiDuyệt/TrảLại/TừChối/ĐãDuyệt. TraLai = Phase RIÊNG (không revert DangSoanThao + không jump-back). PE/Contract/Budget Phase enum + Policy + Service Reject branch → TraLai. 4 test mới TraLai entry point |
0a40c65 |
Mig 23 AddApprovalWorkflowIdToPurchaseEvaluation — pin V2 vào PE entity. Workspace Select bắt buộc workflow lúc create. Validate ApplicableType match PE.Type |
b41484b |
Mig 24 AddCurrentApprovalLevelOrderToPe + Service V2 wire — ApproveV2Async iterate Steps/Levels group by Order = Cấp (OR-of-N approvers) match actor.Id ∈ ApproverUserId. Synthetic Policy ForV2Schema() cho FE nextPhases |
d814429 |
DTO CurrentApproval + banner "Đến lượt bạn" / "Không phải lượt bạn" + button Duyệt forward disabled khi V2 + actor không trong cấp + tooltip "chỉ {NV X / Y} duyệt được". Trả lại + Từ chối vẫn enabled |
9e63e2d d250ae4 74745a7 |
List/Inbox V2-aware (ResolveV2InboxIdsAsync precompute IDs). 2 dropdown filter Quy trình + Trạng thái (chỉ ở Duyệt). Inbox endpoint nhận approvalWorkflowId |
ac41d5e |
SQL clean-transactional-uat.sql — clean prod (9 PE + 11 HĐ + 19 Notif xóa) giữ master. Run qua SSH VPS .\SQLEXPRESS |
de0f38d |
Panel 3 thay 4 phase cards bằng flow workflow thực tế: Bước (icon ✓/●/○) → Cấp (label "đang chờ"/"đã duyệt" + tên NV). DTO ApprovalFlow full snapshot với Status Done/Current/Pending |
Stats final Session 17: 24 migration (+3), 58 DB tables (+3), ~140 endpoints (+5), 81 test pass (+4).
Test user UAT (tạo qua API admin):
- Email:
nv.test@solutions.com.vn/ Pass:TestUser@123456/ Role: Drafter / Phòng: CCM
⚠️ Điều quan trọng cho Session 18+
-
Contract V2 wire CHƯA làm — chỉ PE wire xong (Mig 23-24). Session sau mirror pattern PE → Contract:
- Thêm
Contract.ApprovalWorkflowId+CurrentApprovalLevelOrder(Mig 25) - Update
ContractWorkflowServicethêmApproveV2Asyncbranch - Update Workspace Select V2 trong
ContractCreatePage - Pin V2 thành mặc định cho Contract types
- Thêm
-
Phân quyền strict V2 — hiện loose UAT (mọi authenticated user thấy phiếu V2). Sau confirm flow OK:
- List: Drafter + bất kỳ approver any-Step + Admin
- Inbox: chỉ approver Cấp hiện tại (V2 đã đúng —
ResolveV2InboxIdsAsync) - Detail: same as List
-
Drop legacy V1 sau UAT — khi không còn phiếu nào pin
WorkflowDefinitionId:- Drop tables
WorkflowDefinitions+WorkflowSteps+WorkflowStepApprovers+ PE versions - Cleanup migration: drop column
RejectedAtStepIndex+RejectedFromPhase(deprecated từ Session 17) - Drop
ApproveV1LegacyAsyncbranch trong Service
- Drop tables
-
Admin role bypass — hiện code
if (!isAdmin && !isSystem)skip approver check. By design cho UAT + emergency override. Nếu prod cần audit override → option C trong Session 17 thảo luận: thêm flagIsAdminOverride=truetrong approval row + banner đỏ trên detail. -
81 test pass — Domain WorkflowPolicyTests + PurchaseEvaluationPolicyTests + BudgetPolicyTests đã update cho TraLai entry point. KHÔNG có test cho V2 Service wire (defer khi UAT confirm + có sample data).
TL;DR Session 16 (08/05 — Drastic refactor flat workflow EXECUTE)
Resume từ Session 15 defer plan. Per memory feedback_drastic_refactor_scope.md: dedicated session, fresh context, conservative buffer.
Spec: Workflow flat list (Phòng × Cấp × Approvers). Mỗi step = 1 (Phòng × Cấp). Service iterate steps OrderBy Order, advance pointer. Phase enum simplify ChoDuyet=10. Pin WorkflowDefinitionId.
2 chunk per-commit (5-6 chunk plan rút gọn vì BE tightly coupled):
Chunk A (dbb0089) — All BE: Domain + Mig 21 + Service + Tests
Domain entities:
- Phase enum (PE + Contract): + ChoDuyet=10 generic intermediate. Legacy 2-6 + 98 deprecated (giữ enum cho data cũ).
- WorkflowStep + DepartmentId Guid? FK Restrict + PositionLevel int?
- PurchaseEvaluation/Contract + CurrentWorkflowStepIndex int? + RejectedAtStepIndex int?
- DROP class WorkflowStepInnerStep + nav (PE + Contract)
- DROP *DepartmentApproval.InnerStepId column
EF Configurations:
- DROP InnerStep config (PE + Contract) → table dropped
- WorkflowStep config + DeptId/PositionLevel + FK Restrict
- DepartmentApprovals: restore simple unique non-filtered (Mig 19/20 filtered split reverse)
ApplicationDbContext: DROP DbSet<*WorkflowStepInnerStep> × 2
Migration 21 RefactorWorkflowToFlatModel GỘP:
- 4 ALTER (PE/Contract +CurrentStepIndex +RejectedAtStepIndex)
- 2 ALTER (WorkflowStep +DepartmentId +PositionLevel) PE + Contract
- DROP TABLE × 2 (PEWorkflowStepInnerSteps + WorkflowStepInnerSteps Mig 18+20)
- DROP COLUMN × 2 (*DeptApproval.InnerStepId)
- DROP filtered indexes Mig 19/20
- RESTORE simple UNIQUE (TargetId, Phase, Dept, Stage) non-filtered × 2
Service rewrite (PE + Contract WorkflowService.TransitionAsync):
- DangSoanThao → ChoDuyet (Drafter trình, init idx=0)
- ChoDuyet → ChoDuyet (advance idx per approve)
- ChoDuyet → DaDuyet/DaPhatHanh (idx ≥ steps.Count → terminal, gen mã HĐ Contract)
- ChoDuyet → DangSoanThao (Trả lại — save RejectedAtStepIndex)
- ChoDuyet → TuChoi (Từ chối — khoá vĩnh viễn)
- Resume Drafter (DangSoanThao + RejectedAtStepIndex≠null) → ChoDuyet jump-back
- Match approver: actor.Dept == step.Dept AND actor.PositionLevel >= step.PositionLevel (OR-of-many cùng cấp/dept) OR Approvers.Kind=User|Role match
- Admin role bypass policy
App CQRS: WorkflowStepDto + WorkflowStepInput drop InnerStep, add DepartmentId/DepartmentName/PositionLevel (PE + Contract mirror).
Tests:
- DROP
PeNStageApprovalTests.cs(6) +ContractNStageApprovalTests.cs(6) +PeTwoStageApprovalTests.cs(7) — legacy - UPDATE
PeWorkflowAdminTestssignature for new flat input - 96 → 77 test pass (-19 legacy)
3-file rule Mig 21 commit đủ (.cs + Designer + Snapshot).
Chunk B (88a5be1) — FE Designer + types
PeWorkflowsPage + WorkflowsPage rewrite (~210 LOC each):
- Drop InnerStepDto + EditInnerStep types
- Drop PHASE_OPTIONS (auto-assign ChoDuyet=10 behind scenes)
- StepDto + EditStep + departmentId, departmentName, positionLevel
- Designer step UI rewrite: Tên + Phòng Select + Cấp Select + SLA + Approvers (Role/User optional fallback). Drop InnerSteps sub-section.
- DefinitionCard view: badge Phòng emerald + Cấp NV/PP/TP violet
- Save payload: phase=10 (ChoDuyet)
- Hint amber: "User cùng Phòng + Cấp ≥ step → duyệt được (OR-of-many)"
types/purchaseEvaluation.ts (fe-admin + fe-user mirror): + ChoDuyet=10 enum + label "Đang duyệt" + color amber. Legacy 2-6 + 98 keep.
Chunk C (FE PeWorkflowPanel) SKIPPED — existing UI compatible (workflow.nextPhases BE-driven, 3-button Trả lại/Từ chối Session 14 reuse với target=DangSoanThao/TuChoi pattern).
Verify
- ✅ dotnet build SolutionErp.slnx 0 error
- ✅ dotnet ef database update Mig 21 LocalDB applied OK
- ✅ dotnet test 77 pass (54 Domain + 23 Infra)
- ✅ npm build fe-admin + fe-user pass
Cumulative sau Session 16
| Trước S16 | Sau S16 | |
|---|---|---|
| BE LOC | ~15800 | ~15500 (-300 service simplified) |
| Migrations | 20 | 21 |
| DB tables | 57 | 55 (-2 InnerStep tables) |
| Tests | 96 | 77 (-19 legacy N-stage/2-stage) |
| FE pages | 32 | 32 (rewrite existing 2 designer) |
⚠️ CẢNH BÁO Session 17+
-
UAT live test — workflow flat ready. Tạo new workflow definition qua
/system/pe-workflows/:typeCodevới 3 phòng × N cấp setup. Verify Drafter trình → cấp 1 phòng A → cấp 2 phòng A → cấp 1 phòng B → ... → DaDuyet flow. -
Old PE/HĐ pinned legacy workflow definitions (phase=ChoPurchasing/ChoCCM/etc) — service rewrite chỉ handle ChoDuyet=10 + DangSoanThao/DaDuyet. Old data ở phase 2-6 sẽ stuck (admin manual transition required). Recommend: data migration script convert old workflow → new flat model (defer).
-
Approver explicit (Role/User Approvers list) — fallback nếu user không match Dept+PositionLevel của step. Cho phép user external (không thuộc dept) duyệt qua Role match (vd Admin) hoặc User explicit.
-
Bypass cấp dưới cùng dept — User TP với CanBypassReview=true cùng dept và PositionLevel cao hơn step.PositionLevel → duyệt qua. KHÔNG batch upsert NV+PP rows như Mig 18 N-stage trước (đơn giản hóa: 1 step approve = 1 row).
-
N-stage tests dropped — 19 test legacy (Mig 18, 20 N-stage + Mig 16 2-stage). Có thể viết test mới cho flat workflow flow nếu UAT phát sinh bug. Defer.
-
Sample data N-stage seed task vẫn pending (Session 14). Block trên DesignTime vs Runtime DB gotcha + DbInitializer seed flow.
-
Budget N-stage vẫn defer (cần versioned WF migration trước).
-
schema-diagram §17-21 update defer cron audit 2026-06-01.
TL;DR Session 15 (07/05 — Tooltip diagnose + drastic refactor DEFER)
User UAT live screenshot báo button "Lưu & Gửi Duyệt" KHÔNG hoạt động + suy đoán "trùng ID" giữa các phiếu.
Diagnose (commit 835cc7f):
- Root cause: button silent disabled khi
evaluation.workflow.nextPhaseskhông có forward phase (chỉ TuChoi/TraLai). Cause khả năng: workflow definition pinned thiếu adjacent step →policy.NextPhasesFrom(DangSoanThao)return empty. - Improvement: tooltip + dialog hiển thị reason rõ ràng:
submitDisabledReasontext: "Phiếu đã ở phase X — chỉ Bản nháp/Trả lại mới sửa+gửi" / "Workflow không có phase tiếp theo từ X. Liên hệ admin kiểm tra cấu hình"- Button title attribute → hover show reason hoặc forward phase label
- Dialog confirm show forward phase explicit ("Sẽ chuyển sang Chờ Purchasing")
- Mirror fe-admin + fe-user. Build pass cả 2. KHÔNG đụng BE — chỉ FE diagnostic UX.
- "Trùng ID" KHÔNG phải bug FE —
PurchaseEvaluationWorkspacePageURL state đúng, mỗi PE row unique GUID + MaPhieu. Suy đoán user do button silent.
Plan drastic refactor → DEFER:
User confirm "bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking" — refactor workflow từ phase-based + InnerStep nested model sang flat WorkflowStep model (mỗi step = Phòng × Cấp + Approvers users).
Edit working tree 12 files (Domain entities + EF Configurations + DbContext):
- Phase enum +ChoDuyet=10, legacy values 2-6 deprecated
- WorkflowStep +DepartmentId, +PositionLevel
- Drop class WorkflowStepInnerStep + nav (PE + Contract)
- PE/Contract +CurrentWorkflowStepIndex int?, +RejectedAtStepIndex int?
- *DepartmentApproval drop InnerStepId column
- EF Configurations: drop InnerStep config + nav, restore simple unique non-filtered
- DbContext: drop DbSet × 2
Reality check scope realistic ~8-10h:
- Domain + EF + DbContext (~50min) ✓ done in working tree
- PolicyRegistry rewrite PE+Contract (~45min)
- App CQRS DTOs rewrite (~45min)
- Service rewrite PE+Contract (~2-3h)
- Tests rewrite — drop 12 N-stage tests + update remaining (~1.5h)
- Migration 21 + LocalDB apply + verify (~30min)
- FE Designer rewrite (~1.5h)
- FE PeWorkflowPanel + workflow timeline (~1h)
- Docs/Skill update (~45min)
Vượt session boundary + risk session deep ~30 commits → REVERT working tree về 835cc7f clean state. Test 96 pass intact.
Decision memorized: add memory feedback_drastic_refactor_scope.md — drastic refactor cần dedicated session, scope estimation conservative (2x buffer), tránh mid-session big refactor.
⚠️ CẢNH BÁO Session 16+
- Drastic refactor flat workflow chưa làm — DEFER với plan chi tiết. Khi resume:
- Plan kỹ 6 chunk per-commit
- Buffer 2x estimate (~16h thực tế)
- Tests rewrite biggest risk
- Hoặc fall back Approach Y (FE Designer flat UI giới hạn 5 phòng) ROI 1-2h nếu user OK trade-off
- Task 2 sample data seed N-stage vẫn pending (block trên DesignTime vs Runtime DB gotcha + DbInitializer seed flow)
- schema-diagram §17-19 Mig 18-20 vẫn defer cron audit 2026-06-01
- Hard blockers Ops giữ nguyên 6 task
TL;DR Session 14 (07/05 — PE 3-button approval workflow)
User chỉ thị thay 2-button approval (Duyệt + Reject mơ hồ) bằng 3 hành động rõ ràng cho approver:
- Duyệt = forward phase tiếp theo (decision=Approve)
- Trả lại = về DangSoanThao + Drafter sửa (decision=Reject + target=DangSoanThao). Smart reject pattern Mig 16 + clear N-stage rows + Drafter resume jump-back tới phase đã reject.
- Từ chối = phase=TuChoi (decision=Reject + target=TuChoi). Phiếu khoá vĩnh viễn (17 handler Mig 16 lock edit). Drafter phải tạo phiếu mới.
1 commit (0d77698):
- Domain
PurchaseEvaluationPolicy.cs: NccOnly + NccWithPlan thêm(X → TuChoi)transition cho mọi phase trung gian (ChoPurchasing/ChoDuAn/ChoCCM/ChoCEODuyetPA/ChoCEODuyetNCC) với roles của phase đó. FromDefinition expand: mỗi step (trừ DangSoanThao) thêm (step.Phase → TuChoi) với roles step. - Service
PurchaseEvaluationWorkflowService.TransitionAsync— Reject branch tách 2 case:if (decision == Reject) { if (target != TuChoi) { // Trả lại RejectedFromPhase = fromPhase target = DangSoanThao // force clear N-stage rows tại fromPhase } // else target=TuChoi: giữ nguyên, KHÔNG set RejectedFromPhase, KHÔNG clear } - FE PeWorkflowPanel (admin + user mirror): render 3 button rõ:
- "✓ Duyệt → " brand
- "← Trả lại (về Drafter sửa)" red khi
target=DangSoanThao && fromPhase != DangSoanThao - "✗ Hủy / Từ chối" red khi
target=TuChoi - Decision logic FE:
isReject = target=TuChoi || isSendBack
- Dialog confirm: title rõ theo loại, Cancel case warning đỏ "Phiếu sẽ bị khoá hoàn toàn", SendBack case hint amber "Phiếu về DangSoanThao, Drafter sửa rồi trình lại — workflow tự jump tới phase này".
- Tests update + add 1:
- rename
Reject_Sets_RejectedFromPhase_And_Forces_DangSoanThao→Reject_To_DangSoanThao_Sets_RejectedFromPhase_TraLai(change target TuChoi→DangSoanThao) - NEW
Reject_To_TuChoi_Locks_Permanently_No_RejectedFromPhase(Phase=TuChoi + RejectedFromPhase null) - update
NStage_Reject_Clears_InnerStep_Rows_At_Phasetarget → DangSoanThao
- rename
- 95 → 96 test pass (+1 Từ chối).
Task 2 sample seed in-progress (defer Session 15+):
- Phát hiện gotcha:
DesignTimeDbContextFactoryhardcodedSolutionErp_Designconnection —dotnet ef database updatetừ session 12-13 thực ra apply lên_DesignDB, KHÔNG phải_Devruntime DB. Khi user run API → DbInitializer auto-MigrateAsync apply lên_Dev. - Đã apply Mig 9-20 lên
SolutionErp_Devquadotnet ef database update --connection. - Start API để DbInitializer auto-seed 30 demo user. API exit 255 sớm khi log buffer full (~100 lines) — seeding dở dang (chỉ seed Roles, chưa Users).
- Defer Task 2 cho session sau. Cần: user manual
dotnet runAPI 1 lần hoàn thành seed, hoặc Claude resume với output redirect to file.
⚠️ CẢNH BÁO Session 15+
-
Phase TraLai = 98 (orphan) — Domain enum đã có từ S11+++++++ nhưng KHÔNG dùng (user chốt Session 14 không cần phase trung gian, chỉ DangSoanThao + TuChoi). Để giữ enum không phá nhưng ko wire vào policy → orphan. Có thể remove migration sau (không gây hại).
-
Task 2 sample seed pending: 2 việc:
- Update
Users.PositionLevel(1=NV, 2=PP, 3=TP) cho 30 demo user đã seed (mapping email pattern:tpb./ccm./pro./fin./act./equ./hra.→ 3 (TP),pp.*→ 2 (PP),nv./qs./else → 1 (NV),bod./pm./admin./huy.duong/chau.le→ null) - INSERT N-stage WorkflowDefinition v2 cho DuyetNcc với InnerSteps (PRO + CCM × 3 cấp NV/PP/TP)
- Update
-
DesignTime vs Runtime DB distinction: dev có 2 DB:
SolutionErp_Design—dotnet ef migrations add/update(DesignTimeDbContextFactory hardcode)SolutionErp_Dev— runtime API (appsettings.Development.json) Khi muốn apply migrations lên Dev:dotnet ef database update --connection "...SolutionErp_Dev..."hoặcdotnet runAPI (DbInitializer auto-MigrateAsync).
-
Budget N-stage defer — chưa wire vì Budget chưa có versioned WorkflowDefinition. Cần migration
AddBudgetVersionedWorkflow+AddBudgetWorkflowInnerSteps. User quyết riêng (feature mở rộng module lớn). -
schema-diagram §17-19 Mig 18-20 chưa update — defer cron audit 2026-06-01.
TL;DR Session 13 (07/05 — Mirror N-stage workflow sang Contract)
User chỉ thị mirror N-stage từ PE (Mig 18+19) sang Contract. Budget defer (cần migration AddBudgetVersionedWorkflow trước — hardcoded BudgetPolicy.Default chưa có WorkflowDefinition entity).
5 chunk per-commit (Chunk E skipped — WorkflowsController auto-bind record qua [FromBody]):
- Chunk A (
951ffa3) DomainWorkflowStepInnerStep+ nav WorkflowStep.InnerSteps + ALTER ContractDeptApproval.InnerStepId + EF config + Migration 20 GỘP 1 (CREATE TABLE + ALTER + DropIndex old + Recreate filtered legacy/N-stage) - Chunk B (
04cf2a0) Application CQRS DTO/Input/Validator/Handler mirror PE Chunk B (default null backward compat) - Chunk C (
e247b67) ContractWorkflowService refactor — load InnerSteps eager + reject clear N-stage rows + dept block split hasInnerSteps→N-stage / else→legacy 2-stage - Chunk D (
7c0772a) ContractNStageApprovalTests 6 test mirror PE + helper SeedWorkflowDefinitionAsync 2 step adjacent + FakeChangelogService + FakeContractCodeGenerator stubs. 89→95 test pass - Chunk E SKIP — auto-bind no code change
- Chunk F (current) FE WorkflowsPage Designer extend InnerSteps sub-section mirror PeWorkflowsPage + Docs
Verify: dotnet build pass + dotnet ef database update Mig 20 LocalDB applied + dotnet test 95 pass + npm build fe-admin pass.
Cumulative sau Session 13:
| Trước S13 | Sau S13 | |
|---|---|---|
| BE LOC | ~15300 | ~15700 (+400) |
| Migrations | 19 | 20 (+Mig 20) |
| DB tables | 56 | 57 (+1 WorkflowStepInnerSteps) |
| DB columns mới | — | +1 (ContractDeptApproval.InnerStepId) |
| API endpoints | ~134 | ~134 (no change — auto-bind) |
| FE pages | 32 | 32 (extend existing WorkflowsPage) |
| Tests | 89 | 95 (+6 Contract N-stage) |
| Commits | (after S12) | +5 (A→F per-chunk, E skipped) |
⚠️ CẢNH BÁO Session 14+
-
Budget N-stage defer — Budget chưa có versioned WorkflowDefinition entity (hardcoded
BudgetPolicy.Default). Để mirror N-stage Budget cần migrationAddBudgetVersionedWorkflowtrước (4 bảngBudgetWorkflowDefinitions/Steps/StepApprovers/InnerSteps+Budget.WorkflowDefinitionId?). Defer cho user quyết riêng — feature mở rộng module Budget lớn. -
PE-only deploy first — Contract N-stage giờ đã có nhưng PE đã được test trước. UAT cả 2 module song song để verify pattern reusable.
-
Sample data N-stage — pending (Task 2). Khi user yêu cầu sẽ seed PositionLevel cho 30 demo user + 1 N-stage workflow definition active cho DuyetNcc + 1 cho ContractType (ví dụ HopDongThauPhu).
-
TraLai BE wire — pending (Task 4). Phase TraLai = 98 enum đã có ở Session S11+++++++ FE side, nhưng BE workflow service chưa wire button "Trả lại" cho approver. Khi user yêu cầu sẽ thêm decision
SendBack(or reuse Reject với target=TraLai) + Service logic + FE button trong Workflow Panel. -
schema-diagram §17 Mig 20 chưa update — defer cron audit 2026-06-01.
-
Skill ef-core-migration Mig 20 row chưa add — defer cron audit.
-
Skill contract-workflow N-stage cross-ref chưa add — defer cron audit.
TL;DR Session 12 (07/05 — N-stage workflow approval per phase × dept × cấp)
User yêu cầu mở rộng từ 2-stage Mig 16 sang N-stage cấu hình động: 1 phase (WorkflowStep cha) có thể cấu hình chuỗi InnerSteps con theo Department × PositionLevel sequential. Mỗi phòng có NV → PP → TP duyệt thứ tự, có thể bypass cùng dept khi role cao + CanBypassReview.
6 câu spec defaults chốt:
- Q1 enum
PositionLevel { NhanVien=1, PhoPhong=2, TruongPhong=3 }+User.PositionLevel int? - Q2 Sequential pure (Order asc, mỗi inner step = 1 cấp duyệt)
- Q3 TP có CanBypassReview → skip NV+PP cùng dept (audit IsBypassed=true cho cấp dưới)
- Q4 Smart reject về DangSoanThao + RejectedFromPhase + clear N-stage rows tại phase reject
- Q5 PE first (Contract+Budget mirror sau khi PE stable UAT)
- Q6(a) Designer UI 1 sub-section "Cấp duyệt nhỏ trong phòng" drag-list per step
6 chunk per-commit:
- Chunk A (
13ab533) Domain + Migration 18 — enum PositionLevel + entity InnerStep + ALTER User.PositionLevel + ALTER PEDeptApproval.InnerStepId + EF config Cascade/Restrict + 3-file rule - Chunk B (
0e56bd0) Application CQRS DTO — extend PeWorkflowStepDto + InnerStepDto + Validator + Handler atomic batch + UserDto +PositionLevel + SetUserPositionLevelCommand (default null backward compat existing PeWorkflowAdminTests) - Chunk C (
0c62e24) Service N-stage logic + Migration 19 filtered unique (drop UNIQUE Mig 16 → recreateWHERE InnerStepId IS NULLlegacy + newWHERE InnerStepId IS NOT NULLN-stage). Refactor TransitionAsync: load InnerSteps eager + reject clear rows + hasInnerSteps→N-stage / else→legacy 2-stage. Match firstPending Order asc, exact match upsert 1 row, bypass batch upsert NV+PP+TP audit IsBypassed - Chunk D (
3d76c6b) 6 test PE N-stage (FirstInner blocks / 3-level sequential pass / TP bypass skips / wrong dept 403 / reject clears rows / legacy fallback no inner). IdentityFixture extend +positionLevel arg. Helper SeedWorkflowDefinitionAsync 2 step adjacent. 83→89 test pass - Chunk E (
83ffabd) APIPATCH /users/{id}/position-levelmirror SetBypassReview - Chunk F (current) FE-Admin types/users.ts +positionLevel + PositionLevel const + Label/Short. PeWorkflowsPage Designer + sub-section InnerSteps drag-list { Phòng × Cấp + required } + button "+ Thêm cấp duyệt" emerald + departmentsList query + payload include. UsersPage column "Cấp" badge + cycle button. KHÔNG đụng fe-user (admin-only). Docs/Skill update.
Verify: dotnet build pass + dotnet ef database update Mig 18+19 LocalDB applied + dotnet test 89 pass + npm build fe-admin pass.
Cumulative sau Session 12:
| Trước S12 | Sau S12 | |
|---|---|---|
| BE LOC | ~14850 | ~15300 (+450 — Domain enum + entity + EF config + Service N-stage + App CQRS) |
| Migrations | 17 | 19 (+Mig 18 + Mig 19) |
| DB tables | 55 | 56 (+1 PEWorkflowStepInnerSteps) |
| DB columns mới | — | +2 (User.PositionLevel + PEDeptApproval.InnerStepId) |
| API endpoints | ~133 | ~134 (+1 PATCH /users/{id}/position-level) |
| FE pages | 32 | 32 (no change — extend existing 2 page) |
| Tests | 83 | 89 (+6 N-stage PE) |
| Commits | (after S11+++) | +6 (A→F per-chunk) |
⚠️ CẢNH BÁO Session 13+
-
N-stage chỉ áp PE first — Contract + Budget vẫn 2-stage Mig 16 cũ. Mirror sau khi UAT PE 2-3 tuần ổn (giữ pattern, lặp lại Domain entity + Service logic + Tests).
-
User.PositionLevel chưa seed cho 30 demo user hiện có — admin phải set tay qua UsersPage cycle button. Hoặc seed migration sau (mapping Position text → PositionLevel int).
-
Designer InnerSteps optional — workflow definition KHÔNG có InnerSteps fallback logic 2-stage Mig 16 cũ. Backward compat 100%. Khi muốn enable N-stage cho 1 phase: clone version + add inner steps + Save.
-
Bypass kế thừa cùng dept only — TP với CanBypassReview chỉ skip NV+PP CÙNG dept của TP. KHÔNG cross-dept (NV.A bypass không skip NV.B).
-
Reset N-stage rows khi reject — clear chỉ rows tại phase reject. Phase trung gian khác vẫn giữ approvals nếu có. Resume sẽ jump tới RejectedFromPhase + cần re-approve N-stage tại phase đó.
-
Wrong-level error message — đã localize "phòng X cấp Y không khớp". UAT user cần feedback nếu confused.
-
schema-diagram §15 Mig 18 + §16 Mig 19 chưa update — defer cho audit cron 2026-06-01 (small drift, không major).
-
Skill
ef-core-migrationMig 18+19 row chưa add — defer audit 2026-06-01.
TL;DR Session phase 2 (08/05 — B12-B14 polish iterate sau wrap-up 6e7a6db)
User UAT live tiếp tục, áp rule strict verify khi rename/remove (lesson hotfix CI 0ae3fe2).
- B12 (
378c993) PE detail polish 5 changes:- "Lưu" no-close (chỉ toast + invalidate sync, KHÔNG đóng workspace)
- "Xóa phiếu" red bottom button CHỈ Bản nháp (soft-delete IsDeleted=true)
- Bỏ header bar workspace mode "Sửa header"/"Xóa"/"Đóng" (chuyển hết xuống bottom action bar)
- Section 4 column header:
s.supplierName(master NCC name) thaydisplayName - Section 3 row chặn xóa NCC khi đã có quotes + tooltip "xóa báo giá trước"
- B13 (
e320027) InfoTab auto re-edit + pencil active visual:- useEffect watch
[autoEdit, canEdit, ev.id, ...]→ re-trigger edit khi pencil click phiếu khác - Sync values từ ev mới (tránh stale state)
- Pencil "sáng lên" active state (
bg-brand-100 + ring) khieditingRowId === p.id
- useEffect watch
- B14 (
d2306b8) QuoteDialog + winner column highlight + loading feedback:- Bỏ checkbox "Chọn NCC này cho hạng mục" (consolidate winner ở Section 2.a)
- Winner column Section 4 matrix LUÔN xanh emerald (header
✓prefix + cells full column) - QuoteDialog full overlay loading + spinner khi save có delay
- NccSelectorRow inline spinner "Đang chọn NCC + sync cột giá Section 4…"
Verify: npm run build × 2 app pass · dotnet test 83 pass (KHÔNG regression). Push gitea OK.
⚠️ CẢNH BÁO Session 12+ (cập nhật)
Bổ sung từ wrap-up trước (xem TL;DR S10-11+++++++ phía dưới):
-
isSelectedper-quote BE field — vẫn còn trong API payload nhưng UI ẩn. Nếu sau này muốn cho user chọn winner per-item (khác với winner per-phiếu) → re-enable checkbox QuoteDialog. Hiện tại chỉ gửiexisting?.isSelected ?? falseđể giữ legacy data nếu có. -
Section 4 cell winner highlight dùng
ev.selectedSupplierId === s.supplierId(per-supplier check). Nếu BE workflow có multi-winner per-item (ít khả năng), cần điều chỉnh logic. -
Loading overlay QuoteDialog chỉ áp QuoteDialog. Các dialog khác (AddSupplierDialog/EditSupplierDialog/DetailDialog) chỉ có button text feedback. Nếu UAT thấy delay → mở rộng pattern overlay sang các dialog khác.
-
InfoTab re-edit useEffect deps include 4 ev fields (tenGoiThau/diaDiem/moTa/paymentTerms). Có thể trigger re-set values nếu BE cập nhật ev (vd auto-save từ chỗ khác). Trade-off: chấp nhận để sync state. Nếu UAT thấy "values mất" → debug deps.
TL;DR Session S10-11+++++++ (07/05 — PE Workspace UX overhaul đầy đủ)
User UAT live mode iterate liên tục. Áp rule feedback_uat_skip_verify skip dotnet test sau mỗi chunk, push ngay. Lesson hotfix CI 0ae3fe2: rename/remove → BẮT BUỘC npm run build trước commit.
11 batch deliverable (23 commit total — xem STATUS Recently Done row đầu tiên cho narrative đầy đủ):
- B1 PE Thao tác 2-panel workspace (S10)
- B2 Migration 17 manual budget fields PE + HĐ (S11)
- B3 BudgetFieldRow inline editor Section 2.b
- B4 InfoTab inline edit Section 1 + PeListPanel pencil hover
- B5 Workspace "new" sectioned create view 5 sections
- B6 Danh sách view disable toàn bộ tương tác
- B7 Workspace "new" lock Loại quy trình + payment preset Select
- B8 PE display status meta (Bản nháp / Đã gửi duyệt / Trả lại / Đã duyệt / Từ chối)
- B9 Phase TraLai = 98 + pencil always visible + edit gating
- B10 Hotfix CI TS errors (forcedPhase rename + unused import)
- B11 PE detail polish — NCC selector dropdown + Section 3 winner protect + Bottom action bar Lưu/Gửi Duyệt
Stats sau wrap-up
| Trước S10 | Sau S11+++++++ | |
|---|---|---|
| BE LOC | ~14400 | ~14850 (+450 — Mig 17 + Domain TraLai + App CQRS) |
| Migrations | 16 | 17 (+1 Mig 17) |
| DB columns mới | — | +4 (PE+HĐ × Name+Amount manual budget) |
| FE pages | 31 | 32 (+1 Workspace 2-panel) |
| FE components mới | — | +5 (PeListPanel, PeHeaderForm, PeWorkspaceCreateView, BudgetFieldRow, NccSelectorRow inline) |
| PE phase enum | 8 | 9 (+TraLai = 98) |
| PE display status | — | 5 (gom phase chi tiết) |
| Tests | 83 | 83 (no test added — UAT iteration, áp rule §7 test-after defer) |
| Commits S10-S11+++++++ | — | +23 |
⚠️ CẢNH BÁO Session 12+
-
Workflow transition vào TraLai — Domain enum đã có (98), FE label/color/badge sẵn. NHƯNG chưa wire button "← Trả lại" trong PeWorkflowPanel approver view + workflow service BE chưa transition logic vào TraLai. Khi UAT cần → thêm: button "Trả lại" trong Chuyển tiếp (hoặc Reject Dialog), workflow rule
Decision=3 (TraLai)set phase=TraLai + ghiRejectedFromPhase. FE đã ready accept hiển thị TraLai phase. -
"Đã gửi duyệt" multi-phase filter — display status "Đã gửi duyệt" gom 5 phase trung gian (ChoPurchasing/ChoDuAn/ChoCCM/ChoCEODuyetPA/ChoCEODuyetNCC). BE chưa hỗ trợ multi-phase param trong list endpoint → FE filter dropdown ẩn option này (chỉ exact-match Bản nháp/Đã duyệt/Từ chối). TODO: BE thêm
?phases=2,3,4,5,6query param hoặc derivedisplayStatusfield. -
feedback_uat_skip_verifyexception — khi commit có rename/remove, BẮT BUỘCnpm run build1 lần trước push (lesson hotfix0ae3fe2). Chỉ skip verify khi pure ADD (props, JSX, new file). -
Workspace + Danh sách + Duyệt matrix:
- Workspace (Pe_*_Create): 2-panel, Section 5 disabled, edit only Bản nháp+Trả lại, pencil bright/disabled, bottom bar 2 nút Lưu/Gửi Duyệt
- Danh sách (Pe_*_List): 3-panel, readOnly=true toàn bộ (no edit/transition)
- Duyệt (Pe_*_Pending): 3-panel, PeDetailTabs readOnly + PeWorkflowPanel có Chuyển tiếp + Section 5 nhập opinion (chỗ này hiện vẫn disabled do code path cũ — verify nếu UAT user cần sign opinions ở đây)
-
Mig 17 columns nullable — backward compat OK. Cả 2 (BudgetId + manual fields) cùng null acceptable. Validation BE chưa XOR.
-
CreateContractFromEvaluation carry-forward manual fields PE→HĐ — đã wire. Verify UAT.
-
CI deploy status — sau hotfix
0ae3fe2các run từ commit4c0625ctrở đi sẽ xanh + deploy. User UAT live trên prod ngay sau push.
TL;DR Session 11++ housekeeping (07/05 — InfoTab inline edit + pencil hover)
User feedback after BudgetFieldRow: muốn thêm nút edit kế bên row trong Panel 1 list, click sáng nội dung Section 1 lên cho sửa header inline.
- ✅ InfoTab inline edit — display mode + button "✎ Sửa" góc phải Section 1. Editing mode: card border brand-200 + 4 input (Tên / Dự án locked / Địa điểm / Mô tả / Payment) + Save (PUT /pe/:id) / Hủy.
- ✅ PeListPanel pencil hover — icon absolute right-2 top-2 mỗi row, group-hover opacity-100. Click → callback onEditClick(id).
- ✅ URL flag
?editHeader=1— set bởi pencil click → autoEditHeader prop chain xuống PeDetailTabs → InfoTab tự open editing mode mount-time. - ✅ fe-admin Chunk 1 (
5a89dd2) · fe-user mirror Chunk 2 (this).
Defer (chưa làm): Refactor workspace "new" mode wrap PeHeaderForm trong sectioned layout giống detail view (5 sections visible). PeHeaderForm hiện tại single-card đủ dùng — chỉ làm thêm khi user feedback rõ.
TL;DR Session 11+ housekeeping (07/05 — inline budget editor)
User feedback after Session 11: muốn toggle + 2 fields hiển thị trực tiếp trong Section 2 "b. Ngân sách" của PeDetailTabs, không chỉ ở "Sửa header" page. Empty values cứ empty.
- ✅ BudgetFieldRow component (~125 LOC) thay FormRow tĩnh cũ ở
b. Ngân sách. canEdit=!readOnly && isDraft. Save dùng existing PUT endpoint (KHÔNG cần BE thay đổi). - ✅ fe-admin Chunk 1 (commit
19712d8) · fe-user mirror Chunk 2 (commitd5c6f12). - ✅ Verify: 2 build pass + 0 TS error.
UX:
- Workspace + Danh sách + isDraft → editable inline: toggle + Select OR 2 input + nút Lưu khi dirty
- Duyệt + !isDraft → display only (link card / manual values / "—" empty)
- Empty state: hiển thị "—" thay vì "(chưa link)" verbose (per user)
TL;DR Session 11 (07/05 — Migration 17 manual budget fields)
Output session 11 — UX improvement cho user nhập ngân sách không cần Budget entity approved:
- ✅ Migration 17
AddManualBudgetFieldsToPeAndContract— 4 ALTER (PE + HĐ × BudgetManualName nvarchar(200) + BudgetManualAmount decimal(18,2)). Applied LocalDB OK. Mirror logic PE ↔ HĐ (Q3 user chốt). - ✅ BE Domain 2 entity (PE + Contract) +2 property + EF config HasMaxLength/HasPrecision. App CQRS Create/Update commands + DTO + Validator + diff log + carry-forward CreateContractFromEvaluation pe→contract.
- ✅ FE Toggle checkbox "Nhập tay (không link)" cạnh Label Ngân sách trong 4 chỗ: PeHeaderForm (workspace + /new page wrap), ContractCreatePage NewForm + EditForm. Khi ON: hide Select, show 2 input field grid 2-col (Tên + Số tiền formatted VND). Khi OFF (default): Select Budget approved cũ. Auto-detect manual mode khi load existing có manual data + !budgetId. Display fallback ở PeDetailTabs Section 1 "b. Ngân sách" + Contract EditForm read-only branch.
- ✅ 5 chunk per-commit (build + 83 test pass mỗi chunk): C1 Domain+Infra Migration 17 (
ecd5f7e) · C2 App CQRS (0f7901c) · C3 FE-Admin (bab5031) · C4 FE-User mirror (14f8d9d) · C5 Docs (current).
Validation Q2 chốt: cả BudgetId + manual fields có thể cùng null (PE/HĐ chưa có ngân sách gì). KHÔNG XOR enforce — BE prefer link nếu có (Phase=DaDuyet guarantee), manual fallback only.
KHÔNG đụng: Budget entity / Phase=DaDuyet validation (giữ invariant "PE/HĐ link Budget approved only"). Manual fields chỉ là note/display, KHÔNG join với Budget.Details cho per-row comparison ở PE matrix Section 4 (cột "So với ngân sách" vẫn require ev.budgetId — không có detail rows để compare).
⚠️ CẢNH BÁO session tiếp (Session 12+)
- UAT manual budget flow — toggle ON/OFF + save + reload + verify auto-detect mode. Đặc biệt edit existing PE/HĐ có manual data → toggle phải auto-checked. Edit existing có Budget link → toggle auto-unchecked.
- Section 4 PE matrix "So với ngân sách" vẫn require Budget link — manual amount KHÔNG render comparison column. Document giới hạn này nếu UAT thắc mắc.
docs/database/schema-diagram.mdchưa cập nhật count 16→17 migration + 4 column mới. Defer cho audit định kỳ 2026-06-01 (per §6.4 cron) — nhỏ enough không phải selective rewrite.- CreateContractFromEvaluation đã carry forward manual fields PE→Contract. Verify khi UAT: PE có manual budget → tạo HĐ từ phiếu → HĐ inherit luôn.
- No new test added (rule §7 — feature mới = test-after, soak UAT 2-3 tuần ổn → viết happy path).
- Schema fields nullable — không phá HĐ/PE cũ (legacy null OK). Backward compatible.
TL;DR Session 10 (07/05 — PE workspace 2-panel)
Output session 10 — restructure leaf "Thao tác" PE từ page Create header riêng sang workspace 2-panel mirror HĐ Thầu phụ pattern:
- ✅ Spec chốt 5 câu trước code — Q1 Panel 2 chỉ data entry (KHÔNG Workflow/Approvals/History); Panel 1 pure picker (no inline edit/delete); Q2 mirror HĐ Thầu phụ (sticky "+ Thêm mới" + new→edit Panel 2); Q3 leaf "Danh sách" + "Duyệt" giữ 3-panel hiện tại; Q4 route mới
/workspace; Q5 Section 5 Ý kiến 4PB disable trong workspace (nhập khi duyệt). - ✅ Chunk 1 fe-admin (commit
ee0d360) — 3 file mới:PeListPanel.tsx(~180 LOC pure picker reuse-able + sticky "+ Thêm mới"),PeHeaderForm.tsx(~210 LOC extract header form từ CreatePage),PurchaseEvaluationWorkspacePage.tsx(~120 LOC 2-panel[320px_1fr]). 3 file sửa:PeDetailTabs.tsxthêm propmode?: 'detail' \| 'workspace'+ Section 5 hint amber khi workspace + forceopinionsReadOnly.Layout.tsxresolverPe_*_Create→/workspace?type=N.App.tsxroute mới. - ✅ Chunk 2 fe-user mirror (commit
ecf3c59) — 6 file y hệt content (rule §3.9 duplicate có chủ đích). - ✅ Verify: 2 build pass + dotnet test 83 vẫn pass mỗi chunk.
KHÔNG đụng BE / migration / schema / endpoint. Route /new cũ giữ tồn tại cho deep-link "Sửa header" button.
⚠️ CẢNH BÁO session tiếp (Session 11+)
- UAT live workspace với anh Kiệt + 2-3 user — feature mới UX khác (page Create cũ thành Dialog implicit qua sticky button + transition new→edit auto).
- Section 5 Ý kiến 4PB hint banner amber ở workspace mode chỉ là text gợi ý — KHÔNG block. User vẫn thấy existing opinions read-only. Nếu UAT thấy confused → có thể đổi thành collapsible section.
- Mobile: workspace KHÔNG có fallback — màn hình
<lg(< 1024px) Panel 2 ẩn (lg:block). Cần test mobile redirect →/purchase-evaluations/:idnếu UAT phát hiện. Hiện chỉ admin desktop dùng workspace. - Pe_*_Pending vẫn /purchase-evaluations?pendingMe=1 — leaf "Duyệt" 3-panel (giữ Panel 3 Workflow + readOnly Section 5 cho phép sign). UAT verify Drafter trình → TPB duyệt thấy Section 5 enable đúng.
- PE WorkflowPanel duplicate ở 2 chỗ — leaf "Danh sách" + "Duyệt" Panel 3 (3-panel). KHÔNG có ở "Thao tác" workspace. Verify route resolver active state highlight đúng (queryMatches helper từ session 3).
- Sửa header trong workspace — vẫn navigate sang
/new?id=(button "Sửa header" trong PeDetailTabs). Chưa wire inline edit qua Dialog. Nếu UAT yêu cầu → thêm proponEditHeadercho PeDetailTabs trigger Dialog reusePeHeaderForm.
Housekeeping today (sau Session 9)
- ✅ Audit định kỳ 2026-05 — combined skill + doc drift (commit
7dc0233). 5 drift patch (count 77→83 + 52→55 bảng), 1 skill content patch (contract-workflow Phase 9 cross-ref). KHÔNG tạo skill mới. Logdocs/changelog/skill-audit-2026-05.md. - ✅ Optional polish — fe-user Inbox PE section (commit
332a90f). useQuery/purchase-evaluations/inbox+ Panel 1 chia 2 section sticky header (HĐ + PE). PE click → navigate page riêng. - ✅ User Manual 7 file rewrite compact (commit
16c2c9c). User feedback "ko cần quá đầy đủ chi tiết, cho end-user họ làm". Bỏ field validation table + error troubleshoot table + FAQ chi tiết. Giữ tổng quan ngắn + numbered steps đơn giản. ~86 KB / 7 file (cũ ~123 KB ↓30%). Setuppackage.json+npm install docx@9.5.0+npm run gen:all. Lần sau sửa: edit_gen-*.js→ run gen:all.
Cron audit định kỳ: Claude SDK CronCreate auto-expire 7 days → KHÔNG fit monthly cadence. User cần setup OS Task Scheduler .bat script ping API hoặc manual trigger mỗi đầu tháng. Lần kế: 2026-06-01.
User Manual style rule (mới chốt session này): end-user docs BỎ field/error tables, FAQ chi tiết, phím tắt — GIỮ tổng quan ngắn + numbered steps + note/warn/tip critical. Phân biệt với rule §6.5 (KHÔNG cắt narrative) — đây là rule cho audience end-user, không phải agent dev đọc rationale.
TL;DR Session 9 (04/05 — Chunk E-bis sau Session 8)
Output session 9 — đóng tất cả Chunk E-bis defer từ session 8:
- ✅ FE PE WorkflowPanel — Section "Tiến trình duyệt 2-cấp phòng ban" group by phase × dept, highlight amber chờ TPB confirm, badge fuchsia bypass (cả fe-admin + fe-user).
- ✅ FE UsersPage UserManager — Column "Bypass" + button ShieldCheck toggle CanBypassReview, badge fuchsia khi enabled. UserDto thêm field.
- ✅ HĐ 2-stage logic —
ContractWorkflowServicethêm UserManager DI + mirror logic từ PE service.ContractDepartmentApprovalFeatures.csList query. EndpointGET /contracts/{id}/department-approvals. FEWorkflowHistoryPanelsection mới. - ✅ Budget 2-stage logic —
TransitionBudgetCommandHandlerthêm INotificationService + IDateTime DI + 2-stage.BudgetDepartmentApprovalFeatures.cs+ endpoint + FEBudgetWorkflowPanelsection. - ✅ 6 test PE 2-stage —
IdentityFixturesetup full Identity stack (DbContext SQLite + AddIdentityCore + AddRoles) reusable. 6 scenario: NV_Review_Blocks / TPB_Confirm_Allows / NV_Bypass / Admin_Skip / Reject_Sets / Resume_Jumps_Back. - ✅ Verify: Build pass + 83 test pass mỗi commit (54 Domain + 29 Infra: 17 codegen + 6 PE WF Application + 6 PE 2-stage).
- ✅ 5 commit pushed Gitea (E2 → E6).
⚠️ CẢNH BÁO session tiếp (Session 10+)
- UAT live ngay với anh Kiệt + 2-3 user — feature 2-stage đầy đủ cả 3 module + UX.
- Tests Contract + Budget 2-stage skipped — logic identical PE (cùng pattern, cùng entity shape). Pattern
PeTwoStageApprovalTestsreusable nếu UAT phát hiện regression riêng. - Bypass toggle audit — chưa log Changelog khi admin toggle CanBypassReview. Audit qua Identity standard column UpdatedAt only. Có thể cần thêm audit row riêng nếu UAT yêu cầu.
- Notify TPB cùng dept dùng
UserManager.GetRolesAsyncfilterDeptManager— verify production có user role DeptManager đúng (data already seeded). - fe-user KHÔNG có UsersPage — admin-only function. Bypass toggle chỉ ở fe-admin.
- 3 endpoint mới List dept-approvals PE/HĐ/Budget cùng pattern, reuse policy authz
*Read. - Cron audit định kỳ 2026-05-01 vẫn EMPTY (
No scheduled jobs). Có thể recreate khi user yêu cầu.
TL;DR Session 8 (04/05 — code lớn, 5 commit per-chunk)
TL;DR Session 8 (04/05 — code lớn, 5 commit per-chunk)
Output session 8 — đóng bug anh Kiệt + thêm 3 ràng buộc workflow:
- ✅ Migration 16
AddTwoStageDeptApprovalAndSmartReject— 4 ALTER (3 RejectedFromPhase int + Users.CanBypassReview bit) + 3 CREATE TABLE (Contract/PE/Budget DepartmentApprovalsUNIQUE (TargetId, Phase, Dept, Stage)) + 12 indexes. - ✅ Lock edit 17 handler thêm guard Phase != DangSoanThao (Contract Detail × 15 qua helper, PE Detail × 5 qua helper mới, Budget Detail × 3 inline).
- ✅ Smart reject + Resume 3 module — Reject = lưu phase nguồn + force về DangSoanThao. Resume = jump straight tới phase đã reject (skip phase trung gian, bypass policy guard).
- ✅ PE 2-stage logic trong
PurchaseEvaluationWorkflowService— TPB/CanBypass → Confirm; NV → Review only, BLOCK transition cho đến khi TPB confirm. - ✅ 3 endpoint mới:
GET /pe/{id}/department-approvals(List),PATCH /users/{id}/bypass-review(toggle), Notify TPB cùng dept khi NV review. - ✅ Verify: Build + 77 test pass mỗi commit. Migration applied LocalDB OK. Schema verified.
- ✅ 6 commit pushed (2 docs S7 + 5 code S8).
⚠️ CẢNH BÁO session tiếp (Session 9+)
- Bug fix anh Kiệt chỉ áp PE workflow. HĐ + Budget 2-stage scope DEFER cho khi UAT PE OK.
- FE Workflow Panel chưa update — workflow vẫn block đúng (BE), nhưng UX chưa hiển thị 2-stage progress. User test sẽ thấy phase không đổi mà không hiểu tại sao "stuck". Phải UAT với hint cho user trước khi code FE.
- FE UserManager toggle CanBypassReview chưa làm — tạm thời SET qua HTTP PATCH:
PATCH /api/users/{userId}/bypass-review Authorization: Bearer <admin> { "canBypassReview": true } - Test thực tế bug fix flow:
- Login
phuong.nguyen(NV.PRO, role=Procurement, DeptId=PRO) tạo phiếu PE type A - Trình DangSoanThao → ChoPurchasing
- phuong.nguyen click Duyệt phase ChoPurchasing → expect: phase KHÔNG đổi, có row Stage=Review
tra.bui(TPB.PRO, role=DeptManager) click Duyệt → expect: phase chuyển ChoCCM
- Login
- Notify TPB cùng dept dùng
UserManager.GetRolesAsyncfilterDeptManager. Best effort, fail OK. - Cron audit định kỳ 2026-05-01 đã quá hạn 3 ngày, vẫn EMPTY runtime. Cần manual trigger.
- Smart reject test: Reject phase ChoCCM → DangSoanThao + RejectedFromPhase=ChoCCM. Drafter sửa Detail + trình lại → jump straight tới ChoCCM (skip ChoPurchasing).
- Lock edit test: HĐ ở Phase=DangGopY → cố sửa Detail → expect 409 Conflict "đã trình duyệt, không thể chỉnh sửa".
TL;DR Session 6 (30/04 — không code, chỉ docs)
Output session 6 — pure docs work, không thay đổi code/test:
- ✅ Compact 3 file core: STATUS -27%, HANDOFF -32%, migration-todos -35% (-288 dòng tổng). Archive 51 row Recently Done Phase 0-7 →
changelog/recently-done-archive-2026-04.md. - ✅ Refresh 3 skill stale:
form-engine(Phase 2 MVP → Tier 3 feature-complete),permission-matrix(12 menu → ~60 + inheritance roots),ef-core-migration(24 DbSet → 52 bảng). - ✅ Rule mới §7 Timing test: 1 bảng 5-row compact — feature mới = test-after, bug = test-before, critical algorithm = test-before, spec change = update test cũ, skip 5 loại.
- ✅ Rule mới §6.4 Audit + compact MD định kỳ: cadence + checklist + anti-pattern. Cross-ref §9.4 skill audit. KHÔNG rewrite toàn bộ.
- ✅ 77 test vẫn pass (Domain 54 + Infra 23). 0 thay đổi code.
- ✅ Cron 2026-05-01 fire mai — combined audit (skill + doc drift) theo §6.4 + §9.4.
Session 5 (29/04) đóng gần hết feature gap + bật test gate:
🎯 Headline outcomes
- ✅ Budget feature-complete — FE 3-panel pages cả 2 app, BE đã sẵn từ session 4
- ✅ PE/HD ↔ Budget integration — form select Budget filter Phase=DaDuyet + Project, cột "So với ngân sách" ở PE matrix với delta indicator
- ✅ PE Detail UI restructure match form chính thức PHIẾU TRÌNH KÝ (4 section đánh số)
- ✅ PE Workflow Designer admin UI
/system/pe-workflows/:typeCodeversioned với clone/edit/+Role/+User - ✅ Ý kiến 4 phòng ban — migration 15 + section sign-off 2x2 grid (Phê duyệt/CCM/MuaHàng/SM-PM)
- ✅ Tests Phase 1-2 — 71 unit test pass + CI gate fail-fast (Domain policy + Infra code generator)
Session 4 (28/04) đã có:
A. Module Ngân sách (Budget) BE — migration 14, +4 bảng, +11 endpoint
- 4 entity:
Budget(Header) +BudgetDetail(flat row) +BudgetApproval(history) +BudgetChangelog(audit log). - Enum
BudgetPhase5 state (DangSoanThao→ChoCCM→ChoCEO→DaDuyet + TuChoi). BudgetPolicy.Defaulthardcoded simple 3-step (Drafter→CCM→CEO) — chưa versioned (TODO khi user cần admin config UI).- Mã
NS-YYYYMM-XXXXRandom.Shared (chưa atomic — TODO khi format chính thức). - Link nullable:
Contract.BudgetId?+PE.BudgetId?đã có FK + index, FE chưa wire form. - Menu seed
Budgetsroot + 3 leaf (Bg_List/Bg_Create/Bg_Pending) order=27 icon Wallet. - Application: 11 CQRS handler ~340 LOC (Create/UpdateDraft/Transition/List/GetDetail/Delete + Detail CRUD auto-recompute TongNganSach + ListChangelogs).
- Api:
BudgetsController11 endpoint REST. - FE chưa làm — Priority 0 session 5.
B. 14 demo user Solutions thật
- PRO 5 (TPB tra.bui + 4 NV) + CCM 7 (TPB ngocanh.huynh + 6 NV) + ISO 1 (chau.le) + CEO 1 (huy.duong).
- Pwd
User@123456. Reconcile pattern (gotcha #38 4-field rename). - Tổng 30 user (16 sample cũ giữ + 14 Solutions thật mới).
Tổng cumulative: 52 DB tables, ~128 endpoints, 15 migrations, 41 gotchas (+3 CI fixes), 77 unit test (+6 PE WF Application), 11+ commit session 5 push lên Gitea.
🆕 CI/CD optimize (29/04 tối)
- ✅ Manual checkout bypass github.com — fix #108/#109 transient TCP timeout 21s. Run #110 pass.
- ✅ Path filter docs-only skip —
paths-ignoretrong on:push, commit MD-only KHÔNG trigger CI (verify512880c→ no run #113). - ⏸️ npm junction cache — thử ở #111 fail
tsc not found, rollback. Cần debug session sau (gotcha #40 doc rồi). - ✅ Tests Phase 3 mini (PE Workflow Designer) — 6 test versioning logic. Total 77.
⚠️ CẢNH BÁO session tiếp (Session 7+)
- CI test gate active — code →
dotnet test SolutionErp.slnxlocal → commit → push. Test fail = NO deploy. Workflow user §7 rules.md. - Timing test rule live (§7): feature mới = test-after, bug = test-before BẮT BUỘC, critical algorithm = test-before merge, spec change = update test cũ + code chung commit.
- Doc audit cadence live (§6.4): cuối phase compact, đầu tháng cron audit, KHÔNG rewrite toàn bộ. Cron fire mai (2026-05-01) — combined skill + doc drift theo checklist.
- Login email
admin@solutionerp.local/Admin@123456— domain default chưa đổi sang@solutions.com.vn(chỉ demo user rebrand). - Chưa xóa binding cũ
.huypham.vn— vẫn fallback. Sau verify stable →.\migrate-domains.ps1 -RemoveOld -SkipCerttrên VPS. - win-acme scheduled task "unhealthy" — fix trước cert expire 2026-06-18.
- Export phiếu PDF/Excel PE — pending vô thời hạn (user nói không quan trọng).
- 3 feature mới chưa test (PE Opinion Upsert / Budget validate / Contract BudgetId carry) — đợi UAT phát sinh bug → áp rule §7 (regression test before fix). Hoặc soak 2-3 tuần ổn → viết happy path.
- G-084: VPS shared VietReport — mọi reverse proxy mới phải
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 |
| PE polish iter 2 — flat layout + per-NCC attachments + readOnly Duyệt + email rebrand | ✅ Done |
| Domain rebrand huypham.vn → solutions.com.vn — 3 subdomain + cert + CORS + FE bundle | ✅ Done |
| Module Ngân sách BE + 30 user — 4 bảng + 11 endpoint + workflow simple | ✅ Done (S4) |
| Module Ngân sách FE — 3-panel pages + Detail tabs + Workflow Panel cả 2 app | ✅ Done (S5) |
| PE/Contract → Budget integration — form Select + cột "So với ngân sách" PE matrix | ✅ Done (S5) |
PE Workflow designer admin UI /system/pe-workflows/:typeCode |
✅ Done (S5) |
| Ý kiến 4 phòng ban PE — migration 15 + section sign-off 2x2 grid | ✅ Done (S5) |
| Tests Phase 1-2 + CI gate — 71 test (Domain policy + Infra code generator) | ✅ Done (S5) |
| CI manual checkout bypass github.com + Path filter docs-only skip | ✅ Done (S5) |
| Tests Phase 3 mini — 6 test PE Workflow Designer versioning (total 77) | ✅ Done (S5) |
Export phiếu PDF/Excel PE — IDocumentConverter + template |
📝 Pending (không quan trọng) |
| Tests Phase 3 full — Application handlers (UpsertOpinion + Budget link validation, cần UserManager setup) | 📝 Pending |
| Tests Phase 4-5 — API smoke + FE Vitest | 📝 Pending (làm khi cần) |
| npm cache CI optimize (debug junction Move-Item issue #40) | 📝 Pending |
| 9+ 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 — Session 6 (Ops + UAT focus)
Đa số feature gap đã đóng. Còn lại chủ yếu Ops/UAT + optional polish.
A. Hard blockers (chờ user / ops):
- UAT thật 1 tuần với 2-3 user — Drafter (QS/NV.PB) + CCM + BOD ghi bug/friction
- SMTP config → bật Email outbox (BLOCKED chờ user cấp host/user/pass)
- Rotate credentials — admin + 30 demo + SA + vrapp + JWT secret
- Schedule SQL backup daily —
scripts/backup-sql.ps1chưa schedule Task Scheduler
B. PE feature gap còn lại:
- Export phiếu PDF/Excel PE — pending (user nói không quan trọng lắm). Khi cần: tái dùng
IDocumentConverter+ templatePE-TrinhDuyet.docx.
C. Optional polish (làm khi UAT phát sinh):
- Budget MaNganSach atomic sequence — chốt format → migration
AddBudgetCodeSequencesmirror Contract/PE - Budget versioned workflow — admin config UI thay hardcoded
BudgetPolicy.Default - Payment terms PE tách field — JSON blob → 6 column riêng
- Auto-map PE Details → Contract per-type Details khi gen HĐ
- Matrix Quotes bulk paste từ Excel
- fe-user Inbox thêm section "Phiếu Duyệt NCC chờ tôi"
D. Tests Phase 3-5 (làm khi gặp bug recurring để justify ROI):
- Phase 3 — Application handler tests (CQRS + EF InMemory) ~15 test
- Phase 4 — API smoke tests (WebApplicationFactory) ~7 test
- Phase 5 — FE Vitest cho lib utility (queryMatches, fmtMoney) ~10 test
Đã xong session 5 (check STATUS Recently Done):
Module Ngân sách FE — 3-panel pages cả 2 app✅PE/Contract → Budget integration + cột "So với ngân sách"✅PE Detail UI restructure 4 section đánh số✅PE Workflow Designer admin UI✅Ý kiến 4 phòng ban (migration 15)✅Tests Phase 1-2 + CI gate (71 test)✅
A. Hard blockers (chờ user / ops)
- UAT thật 1 tuần với 2-3 user — hard requirement từ roadmap. Kiến nghị:
- User A: Drafter (QS/NV.PB) — tạo 3 HĐ mỗi type, đi hết 9 phase
- User B: CCM — duyệt phase 6
- User C: BOD — duyệt phase 7
- Ghi bug / friction / đề xuất → backlog iter 2
- SMTP config để bật Email outbox:
Khi có → thêm
"Email": { "Host": "smtp.gmail.com", "Port": 587, "Username": "...", "Password": "...", "From": "noreply@solutionerp.local" }MailKit,IEmailSender, hook vàoNotificationService.CreateAsyncngay trước khi enqueue realtime push. - Rotate credentials — SA SQL password, vrapp password, JWT secret prod, Gitea runner registration token
- Schedule SQL backup —
schtasks /create /tn "SolutionErp Backup" /tr "powershell -File C:\...\scripts\backup-sql.ps1" /sc DAILY /st 03:00
A1. Định kỳ — Skill audit
Cadence: Mỗi đầu tháng (4 tuần). Lần audit kế tiếp: 2026-05-01.
Workflow xem docs/rules.md §9.4. Tóm tắt:
- Cross-check 6 skill hiện có với STATUS / gotchas / migration-todos
- Check repo nguồn 3rd party (alirezarezvani/claude-skills) có gì mới
- Update / archive / add skill nếu cần
- Log vào
docs/changelog/skill-audit-2026-05.md
Trigger: user nói "audit skill" hoặc tự chạy đầu Phase mới.
B. Polish iterations (optional — khi UAT phát sinh)
- Roles CRUD — admin tạo custom role ngoài 12 hardcoded (
Domain.Identity.AppRoles) - User-kind approver runtime — data model
WorkflowStepApprover.Kind=User+AssignmentValue=userIdđã có, chỉ cần:// ContractWorkflowService.TransitionAsync (bổ sung): var userApprovers = step.Approvers.Where(a => a.Kind == ApproverKind.User) .Select(a => Guid.Parse(a.AssignmentValue)); if (userApprovers.Any() && !userApprovers.Contains(actorUserId)) throw new ForbiddenException(); - Grant
Workflows.Readcho non-admin role trong PermissionsPage → menu Wf_* auto-visible (inheritance đã có) - Warning notification 20% SLA — job emit khi
SlaDeadline - now < sla * 0.2 && !SlaWarningSent, set flag - Reject → DangSoanThao E2E test với 3 role khác nhau
- Deps scan CI —
dotnet list package --vulnerable+npm audit --audit-level=high
C. Non-goals / parked
- E-signature (VNPT CA / FPT CA) — Phase 6
- Bravo/SAP import NCC — Phase 6
- Mobile app — Phase 6
- AI OCR scan HĐ — Phase 6+
Lưu ý kỹ thuật quan trọng
Đọc gotchas.md (41 bẫy) trước khi:
- Thêm package mới → .NET 10 compat (MediatR 14 fail → dùng 12.4.1)
- Debug TS enum error → dùng const-object pattern (
erasableSyntaxOnly) - Expression tree lỗi → tách switch ra ngoài LINQ
- Deploy Windows Feature (WebSockets, etc.) → unlock section ở applicationHost (gotcha #25)
- Workflow transition 403 → check
Contract.WorkflowDefinitionIdpin đúng không - Migration lỗi → 3 file đầy đủ (Designer + Migration + Snapshot)
Versioned workflow — quick ref
Contract.WorkflowDefinitionId pin tại Create. ContractWorkflowService.LoadAsync resolve order: pinned def → admin override → hardcoded Registry.For(type). Invariant: HĐ cũ giữ policy cũ qua FK Restrict. Detail: workflow-contract.md §7bis.
File đang active
Cấu trúc thư mục + file map: PROJECT-MAP.md. Git state: git log --oneline -10.
Credentials + URLs
admin@solutionerp.local / Admin@123456 ← Admin (QTV)
Demo users — 30 user (User@123456):
── 16 sample @solutionerp.local (giữ cho test legacy) ──
bod.huynh, bod.le Tổng GĐ + Phó GĐ NĐUQ
pm.nguyen GĐ Dự án FLOCK 01 (PM)
ccm.tran, pro.pham, fin.do TPB CCM/PRO/FIN
act.vu, equ.bui, hra.dang Kế toán trưởng / TPB EQU / TPB HRA
qs.hoang, qs.ngo QS công trường (NV.PB)
nv.cao, nv.dinh, nv.truong NV Cung ứng/Tài chính/CCM (NV.PB)
bod.tran, pm.le Bonus NĐUQ + PM thứ 2
── 14 Solutions thật @solutions.com.vn (session 4) ──
PRO 5: tra.bui (TPB) + phuong.nguyen, binh.lethanh, danh.huynh, dat.tran (NV)
CCM 7: ngocanh.huynh (TPB) + ha.dao, cuong.do, long.le, ha.nguyen,
dung.nguyen, anh.nguyen (NV)
ISO 1: chau.le
CEO 1: huy.duong
⚠ Rotate ALL passwords trước UAT thật
- API prod: https://api.solutions.com.vn —
/health/live,/health/ready - Admin FE prod: https://admin.solutions.com.vn
- User FE prod: https://eoffice.solutions.com.vn
- API dev: http://localhost:5443 —
/swagger(Dev only) - Admin FE dev: http://localhost:8082
- User FE dev: http://localhost:8080
- SQL dev:
(localdb)\MSSQLLocalDB/SolutionErp_Dev - SQL prod:
.\SQLEXPRESS/SolutionErp/vrapp(⚠️ rotate)
Đánh giá nhanh
Tốt:
- 3 domain HTTPS prod live, CI/CD xanh
- Tier 3 feature-complete: attachment, realtime, form builder (upload + DynamicForm + PDF), versioned workflow (admin-configurable per ContractType, pin per contract), nested menu per type, 3-panel permissions
- Clean-arch 3-project split đúng cho 2 cross-cutting service (realtime + document-converter)
- 26 gotchas tích lũy, 8 session log, 40 docs — agent onboard nhanh
- Invariant critical: "HĐ cũ giữ quy trình cũ" guaranteed by pinning (reference-based immutability, không snapshot copy)
Rủi ro còn:
- UAT thật chưa chạy → có thể phát hiện edge case missing
- SMTP chưa có → notification chỉ in-app (toast + bell), không email
- User-kind approver chưa enable guard runtime (designer cho pick, nhưng transition dùng Role fall-back)
- Credentials chưa rotate
- SQL backup chưa schedule Task Scheduler