[CLAUDE] Workflow: wire ApproveV2 + LevelOpinions cho 4 WorkflowApps module (Phase 11 P11-A)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m6s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m6s
Wire full approval workflow V2 cho Leave/OT/Travel/Vehicle — cookie-cutter
mirror Proposal (Mig 38). Trước đây skeleton Phase 1 (Create+List), giờ
ApproveV2 advance-level + UPSERT LevelOpinion + atomic codegen.
Schema (Mig 41 WireWorkflowAppsApprovalV2 — 84→89 tables, pure additive):
- 4 bảng {Leave,Ot,Travel,Vehicle}LevelOpinions (UNIQUE composite + Cascade
parent + Restrict Level — mirror ProposalLevelOpinion)
- 1 bảng WorkflowAppCodeSequences (shared atomic MaDonTu, Prefix-keyed)
- 4 cột RejectedFromStatus (smart return tracking)
- enum ApprovalWorkflowApplicableType.TravelRequest = 9
Application (LeaveOt + TravelVehicle ApprovalFeatures.cs — 30 handler):
- GetById detail (Include LevelOpinions + JOIN Step/Level) · UpdateDraft
- Submit (gen MaDonTu + DaGuiDuyet + level=1, verify ApplicableType per module)
- Approve (verify actor==ApproverUserId OR Admin, UPSERT opinion latest-write-wins,
advance level OR terminal DaDuyet, empty comment → placeholder)
- Reject (→TuChoi) · Return (→TraLai + RejectedFromStatus)
Api: 4 controller +6 route mỗi cái (GET/{id}, PUT/{id}, submit/approve/reject/return)
Infra: DbInitializer seed 4 workflow V2 mẫu (QT-NP/OT/CT/XE-V2-001) → UAT test ngay
FE: WorkflowAppDetailPage.tsx declarative 4-kind (fe-admin+fe-user SHA256 identical)
— workflow status + opinion timeline + action buttons; gỡ banner skeleton + row nav
Tests: +11 WorkflowAppApproveV2Tests (130→141 PASS) — state machine + UPSERT
invariant + guards + codegen + forbidden + placeholder (Leave full + Ot smoke)
Verify: build 0 error · 141 test PASS · FE build ×2 · reviewer checklist
(ApplicableType per-module + cross-module DbSet + [Authorize] — no copy-paste bug)
Known-minor (unreachable): Reject/Return actor-check skip nếu CurrentApprovalLevelOrder
null — nhưng DaGuiDuyet luôn có set (defer hardening).
ItTicket KHÔNG đụng (kanban, no workflow V2).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -66,7 +66,7 @@ UI `disabled={!canX}` + BE helper `EnsureCanXAsync(id, userId)` throw 403 (NOT i
|
||||
|
||||
## 🧠 SOLUTION_ERP BE conventions (S40)
|
||||
- **BE .NET 10:** PascalCase entities + DTO records + command names. CQRS+MediatR+FluentValidation+AutoMapper. Repository qua `IApplicationDbContext`. `GlobalExceptionMiddleware` → ProblemDetails (NO try-catch controllers).
|
||||
- **State S40:** 40 mig (last `AddAttendances`) · 84 SQL tables · ~211 endpoints · 130 test baseline (test-specialist owns). Phase 9 UAT skip per chunk (`feedback_uat_skip_verify`).
|
||||
- **State S41:** 41 mig (last `WireWorkflowAppsApprovalV2`) · 89 SQL tables · ~211 endpoints · 130 test baseline (test-specialist owns). Phase 9 UAT skip per chunk (`feedback_uat_skip_verify`).
|
||||
- **Build:** `dotnet build SolutionErp.slnx` clean 0 err. Commit `[CLAUDE] <scope>: <msg>` + Co-Authored-By Claude Opus 4.8 (1M context).
|
||||
- **Pin (KHÔNG `*`/latest):** MediatR `12.4.1` (14 fail DI) · Swashbuckle `6.9.0` · Node CI `20.x` · LibreOffice `25.8.6` · @microsoft/signalr `8.0.7`.
|
||||
|
||||
@ -74,6 +74,10 @@ UI `disabled={!canX}` + BE helper `EnsureCanXAsync(id, userId)` throw 403 (NOT i
|
||||
|
||||
## 📅 Recent activity (FIFO — older → archive/git)
|
||||
|
||||
- **S42 P11-A SEED — 4 sample ApprovalWorkflow V2 for WorkflowApps (DbInitializer.cs only, +4 method ~210 LOC + 4 call):** Case 1 mechanical mirror `SeedSampleProposalWorkflowV2Async` EXACT × 4 (Leave5/Ot6/Travel9/Vehicle7). Each: idempotent `AnyAsync(ApplicableType==X)` guard → resolve approver `binh.le@solutions.com.vn` (SAME user as Proposal/Contract seed, null→LogWarning+return) → 1 ApprovalWorkflow (Version=1, IsActive+IsUserSelectable=true, ActivatedAt=UtcNow) + 1 Step (Order=1, Name="Cấp duyệt", DepartmentId=CCM?.Id) + 1 Level (Order=1, ApproverUserId). Codes QT-NP/OT/CT/XE-V2-001. Wired 4 calls after SeedSampleProposalWorkflowV2Async (NOT gated DemoSeed, gotcha #51 infra seed). Enum verified Grep. Build 0 err 0 warn. Bash tool = bash NOT PowerShell despite env hint (use `cd && cmd | grep`). Spec deterministic 100% → ACCEPT Case 1. NOT touched App/Controller/FE/test/Mig. Tag `[s42, p11-a, seed, mirror-proposal-exact]`.
|
||||
- **S42 P11-A Wave 2b APP — wire ApproveV2 CQRS Travel+Vehicle (`TravelVehicleApprovalFeatures.cs` ~830 LOC + 2 controller edit):** Cookie-cutter mirror Wave 2a / ProposalFeatures Region 2. 1 new file ns `Application.Office`: 2 module × (DetailDto + LevelOpinionDto + GetById JOIN Step/Level metadata + UpdateDraft + Submit + Approve UPSERT+advance + Reject TuChoi + Return TraLai+RejectedFromStatus) + 1 shared `internal static TravelVehicleCodeGen.GenerateMaDonTuAsync` (Serializable tx, `WorkflowAppCodeSequences` Prefix-keyed, prefix `DT/CT/{year}` Travel & `DX/XE/{year}` Vehicle, format `{prefix}/{seq:D3}` — D3 no year segment per spec). KEY gotcha: WorkflowAppStatus enum DIFFERS from ProposalStatus int values (DaGuiDuyet=2 not 1, TraLai=3) → mirror by SEMANTIC enum member not literal. Owner = `RequesterUserId` (not DrafterUserId). Submit verify wf.ApplicableType==Travel9/Vehicle7 else Conflict. 2 controller +6 route each (GET{id}/PUT/submit/approve/reject/return) nested body records, CreatedAtAction. KHÔNG sửa WorkflowAppsFeatures.cs/Leave/Ot/FE/test/seed. Build 0 err. Spec deterministic ~98% em main → ACCEPT Case 1/2. Tag `[s42, p11-a, wave-2b, mirror-proposal-region2]`.
|
||||
- **S42 P11-A Wave 2a APP — wire ApproveV2 CQRS Leave+Ot (`LeaveOtApprovalFeatures.cs` ~770 LOC + 2 controller edit):** Pattern 4 (UPSERT in Approve, 0 opinion endpoint) + cookie-cutter mirror ProposalFeatures Region 2. 1 new file ns `Application.Office`: 2 module × (DetailDto + LevelOpinionDto + GetById JOIN Step/Level metadata + UpdateDraft + Submit + Approve UPSERT+advance + Reject TuChoi + Return TraLai+RejectedFromStatus) + 1 shared `internal static WorkflowAppCodeGen.GenerateMaDonTuAsync` (Serializable tx + `WorkflowAppCodeSequences` Prefix-keyed, prefix DT/LR & DT/OT, format `{prefix}/{year}/{seq:D3}`). Approve: flatten allLevels OrderBy Step→Level, currentSlot=allLevels[order-1], actor==ApproverUserId OR Admin, comment empty→placeholder, advance OR terminal DaDuyet. Submit verify wf.ApplicableType==Leave5/Ot6 else Conflict + gen MaDonTu nếu null. 2 controller +6 route each (GET{id}/PUT/submit/approve/reject/return) mirror ProposalsController nested body records (`WorkflowActionBody`). KHÔNG sửa WorkflowAppsFeatures.cs (Region 1 Create/List ở đó). Build 0 err (2 warn DocxRenderer pre-existing). Spec deterministic 100% em main → ACCEPT Case 1. Travel/Vehicle (Wave 2b) + test (Wave 4) deferred. Tag `[s42, p11-a, wave-2a, mirror-proposal-region2]`.
|
||||
- **S41 P11-A Wave 1 SCHEMA — wire ApproveV2+LevelOpinions 4 WorkflowApps (Mig 41 `WireWorkflowAppsApprovalV2`):** Pattern 12-bis cookie-cutter mirror Proposal Mig 38, 13× cumulative. 11 file: 5 entity (4 `{Leave,Ot,Travel,Vehicle}RequestLevelOpinion` + shared `WorkflowAppCodeSequence` Prefix-PK) + 5 EF config (auto-discover ApplyConfigurationsFromAssembly, no manual register) + edit 4 parent (nav LevelOpinions + `WorkflowAppStatus? RejectedFromStatus`) + edit enum (`TravelRequest=9`) + 2 DbSet (IAppDbContext+ApplicationDbContext, 5 each). Mig diff CLEAN: 5 CreateTable + 4 AddColumn (no drift). FK Cascade parent + Restrict Level + UNIQUE composite ({Parent}Id, ApprovalWorkflowLevelId). Applied BOTH DB (Dev + Design). Build 0 err. Wave 2 (App/Controller) + Wave 4 (test) deferred per spec. Spec deterministic 100% (em main chose schema) → ACCEPT Case 1. Tag `[s41, p11-a, 12-bis-13x, mig41]`.
|
||||
- **S35 G-H2 BE CRUD 4 catalog (HrmConfigFeatures.cs 372 LOC + Controller 134 LOC, 16 endpoint):** Pattern 12-bis 3rd application catalog-mega. 4 sub-resource × 4 verb. KEY: HRM no HasQueryFilter → `.Where(!IsDeleted)` manual; Validator MaxLength = EF source-of-truth (Code=50 not spec 20). 130 test baseline preserve. ACCEPT clean spec 95%. Tag `[s35, be-crud, hrm, 12-bis-3x]`.
|
||||
- **S29 Plan B Chunk C Contract V2 mirror (Mig 33 ContractLevelOpinions):** Pattern 12-bis 1st — 8 file +4265 LOC (Designer autogen 95%, handcraft ~232 LOC). Em main spec deterministic 100% → ACCEPT. Tag `[s29, plan-b, 12-bis]`.
|
||||
- **Archived FE/test + older BE entries → `archive/2026-05-q4.md` + git d2f52ba (S40 curate):** S35 FE inline forms 5 satellite (→ frontend domain) · S34 test bundle +10 [Fact] 130 PASS (→ test-specialist domain) · S33 Task 5 EmployeesListPage · S32 wrap/startup. KEY absorbed in Patterns above + split pointers.
|
||||
|
||||
@ -70,6 +70,7 @@ Bearer từ `POST api.solutions.com.vn/api/auth/login` → status matrix expecte
|
||||
|
||||
## 📅 Recent activity (FIFO — older → archive/git)
|
||||
|
||||
- **2026-05-30 (P11-A WorkflowApps wire pre-flight):** 4 module Leave/OT/Travel/Vehicle. Schema pin ĐÃ CÓ SẴN (Mig 39): `Office/{Module}.cs` đều có `ApprovalWorkflowId?`+`CurrentApprovalLevelOrder?`+`WorkflowAppStatus` (5-state khớp ProposalStatus). SKELETON tại `Application/Office/WorkflowAppsFeatures.cs:11-15` (chỉ Create+List, KHÔNG Approve/Reject/Return/GetById). **Proposal = mirror HOÀN HẢO** (cùng Office ns, Mig 38): `ProposalFeatures.cs:403-486` ApproveHandler = flatten `Steps.OrderBy(Order).SelectMany(Levels.OrderBy(Order))` global level index → `allLevels[CurrentApprovalLevelOrder-1]` → actor match `Level.ApproverUserId==uid||Admin` → UPSERT LevelOpinion → advance++/DaDuyet. ⚠️ `ApprovalWorkflow.cs:72` nói Level **KHÔNG OR-of-N** (1 ApproverUserId/Level) — KHÁC memory cũ "OR-of-N", verify lại. Gap: 4 bảng `{Module}LevelOpinion` mới (Mig 41+), 3 route/controller, 4 seed WF, FE Detail+Opinion (chỉ có WorkflowAppsListPage chung). ⚠️ enum `ApplicableType` THIẾU Travel (có Leave=5/OT=6/Vehicle=7/ItTicket=8); `ExtendApplicableTypeForWorkflowApps` mig empty Up/Down (enum-only). Tag `[pre-flight, p11-a, workflowapps]`.
|
||||
- **2026-05-29 (S40 STATE GROUNDING):** 7 metric verify. ✅ Migrations=**40** (path `.../Persistence/Migrations/*.cs`, last `AddAttendances`). ✅ Gotchas=**55** (`### N.`). ✅ git clean. DbSet=77 nhưng **SQL tables=84** (em main verify `.ToTable()` ModelSnapshot — 77+7 Identity, "84 docs ĐÚNG", DbSet count sai −7). Endpoints=**211** (docs ~223). FE pages fe-admin **36**+fe-user 29=**65** (docs 53 under-count). Menu keys=**53** const (docs 85 over-count). 3 số tin cậy nhất = mig/gotcha/git. Lesson: tables phải count ToTable KHÔNG DbSet. Tag `[state-grounding, docs-drift, s40]`.
|
||||
- **2026-05-29 (S39 BVAAU 7-agent extract):** Đọc 8 file BVAAU `.claude/agents/` ~22K. Split 4→7 trục research(2)/implement(2)/quality(3). Boundary: repo interface=domain, EF config=infra, test=test-specialist. Tool: cả 7 agent 5 RAG MCP (+search_code BM25 +store_memory +list_projects). BVAAU Phase 0 codebase RỖNG → aspirational template chưa battle-test; SOLUTION_ERP giữ 6 skill + backend/frontend split (thay domain/infra cho 2-FE fit). VIPIX guide claim KHÔNG verify được (file miss). Tag `[cross-project, bvaau, port]`.
|
||||
- **2026-05-28 (S37 G-O3 Proposal pre-flight):** PE pattern mirror cho Mig 38. LevelOpinion UNIQUE per-LEVEL `(EntityId, LevelId)` FK Cascade+Restrict. CodeSequence 1 row/Prefix (`DX/YYYY` 3 col Prefix PK+LastSeq, tx SERIALIZABLE). ApproveV2Async 7 step (group Levels by Order=Cấp OR-of-N → match actor.Id∈ApproverUserId + Admin bypass → UPSERT LevelOpinion sync → F2 skipToFinal → advance levelOrder++ → DaDuyet). NamGroup TblRequest generic → **SOL clean-room MẠNH HƠN**. CategoryId nullable FK + free text fallback (lesson Plan C 8 FK ZERO populated). Tag `[pre-flight, g-o3, proposal]`.
|
||||
|
||||
@ -15,7 +15,7 @@ WRITE specialist độc quyền `tests/**`. xUnit + FluentAssertions 7.2 + EF SQ
|
||||
- ❌ NOT: production code `src/Backend/**` + `fe-*/**` → test reveal bug → REPORT em main, KHÔNG fix
|
||||
- ❌ NOT: decide WHAT to test (test plan) → em main + reviewer chốt priority
|
||||
|
||||
## 📊 Baseline 130 PASS (58 Domain + 72 Infra)
|
||||
## 📊 Baseline 141 PASS (58 Domain + 83 Infra) ← S42 +11 WorkflowApp ApproveV2
|
||||
Run: `dotnet test SolutionErp.slnx --nologo --verbosity minimal`
|
||||
|
||||
## ⏱️ Timing rules (docs/rules.md §7)
|
||||
@ -51,6 +51,7 @@ Test theo CODE (single source truth), document mismatch header comment + report.
|
||||
|
||||
- **2026-05-29 (S39 agent split setup):** NEW dedicated agent. Seeded test patterns (10 reflection authz + 11 infra helper + 12 InternalsVisibleTo + #48 SQLite tie-break + spec drift S34). Inherited coverage gap backlog 4 priority items từ S36 Reviewer audit (130 PASS baseline). First spawn pending em main S39+ test bundle task (recommend Gap 1 Holiday composite UNIQUE first).
|
||||
- **2026-05-29 (S40 baseline audit smoke):** CONFIRMED 130 PASS (Domain 58 + Infra 72), 0 fail/skip, ~15s. Runner count authoritative; raw `[Fact]/[Theory]` attr = 48+70 (Theory→InlineData expand). Infra spread 15 files. Gap re-verified vs prod: EmployeesController+HrmConfigsController EXIST, authz regression chỉ ApprovalWorkflowsV2Controller (gotcha #44 gap real). Proposal = Domain entity + EF config only, CHƯA có ApproveV2Async service (S37 skeleton, defer đúng). Agent load OK. AUDIT-only, no write.
|
||||
- **2026-05-30 (S42 P11-A Wave4):** +11 test `tests/.../Application/WorkflowAppApproveV2Tests.cs` → **141 PASS** (Infra 72→83). LeaveRequest 8 case full (Submit happy/guard×2, Approve advance/terminal/UPSERT-invariant/forbidden/empty-comment-placeholder, Reject→TuChoi, Return→TraLai+RejectedFromStatus) + OtRequest smoke (submit→approve single-level→DaDuyet). **No prod bug** — LeaveOt ApproveV2 wire correct, all PASS first run. **NEW Pattern:** WorkflowApps handlers = CQRS MediatR (KHÔNG service) → instantiate handler trực tiếp `new ApproveLeaveRequestHandler(db, AsUser(u), clock).Handle(cmd,ct)`, chỉ 3 dep (IApplicationDbContext + TestCurrentUser + FixedDateTime) — nhẹ hơn 6-dep Contract service. MaDonTu format "DT/LR/2026/001". Gap #4 (Workflow Apps) PARTIAL done — Travel/Vehicle mirror pending. ⚠️ Lesson: CWD drift (fe-user) → ghi MEMORY nhầm path, em main relocate. Verify CWD root trước Write memory.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user