d436af2971fbc25b962929ae40f0348df447bccb
137 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 07b3f3b284 |
[CLAUDE] Domain+App+Infra+FE-Admin+FE-User: S34 Plan 4 G-H2 Mig 35 schema foundation
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m30s
Phase 10.2 G-H2 Cấu hình HRM — 4 catalog lookup foundation deploy (BE CRUD +
FE forms DEFER S35 cho clean handoff).
Mig 35 `AddHrmConfigs` — 4 table mới:
- LeaveTypes (Code unique + Name + DaysPerYear decimal(5,2) + IsPaid +
RequiresAttachment) — 5 sample seed (ANNUAL 12d + SICK 30d + MATERNITY 180d
+ COMPASSIONATE 3d + UNPAID 0d)
- Holidays (Year + Date UNIQUE composite + Name + IsRecurring + IsPaid) —
10 sample VN 2026 (Tết Dương + 5 Tết Nguyên đán placeholder + Giỗ tổ +
30/4 + 1/5 + 2/9 + Quốc khánh)
- ShiftPatterns (Code unique + Name + StartTime/EndTime TimeOnly + BreakMinutes +
WorkDays comma string) — 3 sample (HC 8-17 T2-T6 + CA1 6-14 T2-T7 + CA2 14-22)
- OtPolicies (Code unique + 3 Multiplier decimal(4,2) + 3 MaxHours int) —
1 sample STANDARD (1.5x/2.0x/3.0x weekday/weekend/holiday + 4h/40h/200h cap
Luật Lao động VN 2019)
Files:
- Domain/Hrm/{LeaveType,Holiday,ShiftPattern,OtPolicy}.cs (4 entity AuditableEntity)
- Infrastructure/Persistence/Configurations/{LeaveType,Holiday,ShiftPattern,OtPolicy}Configuration.cs (4 EF Config UNIQUE indexes)
- IApplicationDbContext + ApplicationDbContext +4 DbSet
- Migrations/20260527075940_AddHrmConfigs.{cs,Designer.cs} + Snapshot updated (3-file rule)
- DbInitializer.SeedHrmConfigsAsync ~120 LOC seed sample (NOT gated DemoSeed per gotcha #51)
- MenuKeys.cs +HrmConfig sub-group + 4 leaf (LeaveTypes/Holidays/Shifts/OtPolicies) Order=2 dưới Hrm
- DbInitializer.SeedMenuTreeAsync +5 entry (sub-group + 4 leaf)
- fe-admin + fe-user menuKeys.ts +5 const mirror BE (Pattern 16-bis sync)
Verify:
- dotnet build PASS (2 warn DocxRenderer baseline, 0 error)
- dotnet test 130/130 PASS baseline preserve
- Mig 35 applied LocalDB SolutionErp_Dev — verified via dotnet ef database update
- 4 catalog table created + 5+10+3+1 = 19 sample row seed
Defer S35:
- Task 2 BE CQRS 4 catalog CRUD (16 endpoint) — Implementer Case 2 cookie-cutter
- Task 4 FE 2 app 4 catalog page (list/create/edit dialog) — Implementer Case 2
Cumulative S34 mig: 34 → 35 (+1). Tables 67 → 71 (+4). Menu keys 64 → 69 (+5).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| ea440da990 |
[CLAUDE] Domain+App+Api+Infra+FE-Admin+FE-User: S34 Plan 2 G-O1 Danh bạ nội bộ
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m46s
Phase 10.2 Văn phòng số — Internal Directory (1 endpoint reuse Users + EmployeeProfiles + Departments, FE card grid avatar/dept/email/phone/Ext). BE Task 1+2 (em main solo): - Application/Office/DirectoryFeatures.cs — GetDirectoryQuery + DirectoryItemDto 12 field LEFT JOIN Users.IsActive + Departments + EmployeeProfiles - Api/Controllers/DirectoryController.cs — GET /api/directory?search=&departmentId= class-level [Authorize] (mọi authenticated NV tra cứu danh bạ nội bộ) - MenuKeys.cs +Off+OffDanhBa const + All[] update - DbInitializer.SeedMenuTreeAsync Off Order=29 + OffDanhBa Order=1 dưới Off FE Task 3 (Implementer Case 2 Pattern 16-bis 4-place mirror cross-app — 5×): - types/directory.ts SHA256 7349d9f64e78 × 2 app IDENTICAL - pages/office/InternalDirectoryPage.tsx SHA256 2aa7e0eed2c8 × 2 app IDENTICAL Card grid responsive 1/2/3/4 col + filter dept dropdown + search input Avatar 14×14 initials gradient PALETTE 6 màu (Pattern 14 Tailwind JIT) EmployeeCode badge + Department emerald badge + email mailto + phone tel Internal phone Ext: amber badge + empty/loading state Vietnamese 100% - App.tsx route /directory × 2 app - lib/menuKeys.ts Off+OffDanhBa const × 2 app - components/Layout.tsx resolvePath staticMap Off_DanhBa:/directory × 2 app (gotcha #50 — 5 places mirror crossapp DON'T MISS) Verify: - dotnet build PASS (2 warn DocxRenderer existing, 0 error) - dotnet test 120/120 PASS (58 Domain + 62 Infra baseline preserve) - npm build × 2 app PASS 0 TS err (fe-admin 1436KB / fe-user 1350KB) Implementer MEMORY Pattern 16-bis reinforced 5× cumulative (S29 Plan CA HF1 + S29 Plan B Chunk D + S33 Plan B G-H1 Task 5 + S34 Plan G-O1 Task 3). Endpoint smoke pending CICD post-deploy Stage 4 (Run #XXX expected ~3m30s). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 0e191deea5 |
[CLAUDE] Domain+App+Api+Infra: Plan B G-H1 Task 4+6 — Hrm CQRS 5 endpoint + Permission menu
Phase 10.1 G-H1 Phase 2 — Task 4 (BE CQRS + REST endpoint) + Task 6
(Permission menu seed) cumulative. Foundation BE side complete cho Hồ sơ
Nhân sự module — FE Task 5 + Reviewer Task 7 + CICD verify next.
## Task 4 — BE CQRS + Controller (3 file new, Implementer Case 2)
src/Backend/SolutionErp.Application/Hrm/EmployeeFeatures.cs (~450 LOC):
- CreateEmployeeProfileCommand + Validator + Handler
- Verify User.Id exists qua UserManager.FindByIdAsync
- UNIQUE 1-1 check: throw ConflictException nếu User đã có EmployeeProfile
- EmployeeCode auto-gen qua IEmployeeCodeGenerator (NV/{YYYY}/{Seq:D4})
- 50+ field assignment từ Command record
- UpdateEmployeeProfileCommand + Validator + Handler (mutable fields, UserId+EmployeeCode immutable)
- DeleteEmployeeProfileCommand + Handler (soft delete IsDeleted=true)
- GetEmployeeProfileQuery + Handler (Include 5 satellite collection)
- ListEmployeesQuery + Handler (paged + JOIN Users+Departments, filter Status/DepartmentId/Search)
src/Backend/SolutionErp.Application/Hrm/Dtos/EmployeeDtos.cs (~110 LOC):
- EmployeeProfileListItemDto (Id, EmployeeCode, UserId, FullName/Email/Department JOIN, Status, Phone, HireDate)
- EmployeeProfileDetailDto (full 50+ field + 5 satellite collection)
- 5 satellite DTO: EmployeeWorkHistoryDto + EmployeeEducationDto +
EmployeeFamilyRelationDto + EmployeeSkillDto + EmployeeDocumentDto
src/Backend/SolutionErp.Api/Controllers/EmployeesController.cs (~70 LOC):
- 5 REST endpoint: GET list / GET detail / POST / PUT / DELETE
- Class-level [Authorize] only Phase 1 (per-action policy Hrm_HoSo_View/Create/
Edit/Delete defer Phase 1.5 per Reviewer recommend)
- Route prefix /api/employees
## Task 6 — Permission menu Hrm_HoSo* (em main solo, 2 file mod)
src/Backend/SolutionErp.Domain/Identity/MenuKeys.cs (+10 LOC):
- +2 const: Hrm root group + HrmHoSo leaf
- Update All[] array → SeedAdminPermissionsAsync auto-grant Admin role CRUD
src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs (+4 LOC):
- SeedMenuTreeAsync +2 entry:
- (Hrm, "Nhân sự", null, 28, "UserCircle") — root group
- (HrmHoSo, "Hồ sơ Nhân sự", Hrm, 1, "ContactRound") — leaf
- Order=28 between Budgets=27 và Contracts=30+ (no collision)
- INFRASTRUCTURE menu seed (NOT gated DemoSeed:Disabled — em main verified
outside gate block per gotcha #51 lesson, mirror Plan B Task 3b
SeedDemoEmployeeProfilesAsync placement)
## Reviewer ae752c0 verdict: PASS Smart Friend 6× clean
- 0 critical, 0 major, 3 minor defer Phase 1.5 (per-action policy + bool
partial update + IDateTimeProvider injection)
- Cumulative Smart Friend track: S22 #44 + S25 #48 + S29 Plan CA ≥12 + S29
Plan B ApplicableType + S33 Plan C BW clean + S33 Plan B Phase 2 clean
- gotcha #51 INFRASTRUCTURE seed gate compliance: ✓
- gotcha #50 Layout staticMap mirror: ✓ (Task 5 commit next)
## Verify
- dotnet build: 0 err 0 warn (1.72s)
- dotnet test: 120/120 PASS baseline preserved
- Endpoint claim verified grep 0 mock marker, 5 mediator.Send real
Pattern 12-bis cross-module entity cookie-cutter mirror PE→Hrm reinforced 4×
cumulative (S29 Plan B Contract Chunk C + S33 Task 3 entity scaffold + Task
3b seed + Task 4 CQRS).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 48a99e14e7 |
[CLAUDE] Domain+App+Infra: Plan B G-H1 Mig 34 EmployeeProfile + seed 30 demo
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m38s
Phase 10.1 G-H1 Hồ sơ Nhân sự Foundation — port từ NamGroup CT_NHANSU
(1675 NV, 10 bảng TblNhanVien*) sau anh main chốt S32 4 quyết định
(scope FULL 11 module + DB single schema dbo + reuse Workflow V2 +
chunk per-module Plan riêng). G-H1 CRITICAL FIRST vì depend by 8/11
module Phase 10 sau (Đề xuất/Đơn từ/OT/Đặt xe/Ticket/Dashboard NS/
Chấm công đều cần EmployeeProfile data).
Investigator pre-flight (a103d20) audit NamGroup confirm:
- Main TblNhanVien 105 cols (drop 35 cols duplicate User/UX legacy)
- 5 satellite Phase 10.1 (defer 3 HĐLĐ Plan H2): WorkHistory + Education
+ FamilyRelation + Skill polymorphic Kind + Document
- 6 enum thay catalog FK (Gender/MaritalStatus/EmployeeStatus/...)
- DiaChi dual-write FK + freetext lesson Plan C NamGroup 1675 NV drift
Em main 4 decision chốt:
1. 5 satellite Phase 10.1 (defer 3 HĐLĐ Plan H2)
2. Skill polymorphic Kind enum (gộp 3 NamGroup table)
3. DiaChi 6 FK Province/District/Ward declare nullable + freetext dual-write
ngày đầu (FK constraint defer G-H2 khi catalog scaffold — Implementer
smart decision documented EmployeeProfile.cs comment line 14-17)
4. MaNhanVien format NV/{YYYY}/{Seq:D4} atomic Serializable reset/year
Implementer Case 2 (a8f4567) Pattern 12-bis cross-module mirror PE → Hrm
cookie-cutter scaffold 17 file mới + 4 modified + 3-file mig rule:
Domain (8 file SolutionErp.Domain.Hrm):
- EmployeeProfile.cs (main ~70 cols inherit AuditableEntity, 1-1 UNIQUE User)
- EmployeeWorkHistory.cs + EmployeeEducation.cs + EmployeeFamilyRelation.cs
- EmployeeSkill.cs (polymorphic Kind=Computer/Language/Other)
- EmployeeDocument.cs (IdCard/Passport/Degree/Certificate/LaborContract/Other)
- EmployeeCodeSequence.cs (PK string Prefix, NOT BaseEntity Id Guid)
- Enums.cs (10 enum gọn 1 file)
Application (1 file):
- IEmployeeCodeGenerator.cs interface (mirror IContractCodeGenerator)
Infrastructure (8 file):
- EmployeeCodeGenerator.cs impl IsolationLevel.Serializable transaction
- 7 EF Configuration file (HasIndex UNIQUE UserId/EmployeeCode/Phone +
HasMaxLength + HasColumnType decimal(18,2) + FK Cascade satellite)
- DependencyInjection.cs (M): register IEmployeeCodeGenerator → impl
Persistence (3 file modified + 2 new mig + 1 snapshot):
- IApplicationDbContext.cs (M): +7 DbSet<EmployeeProfile/...>
- ApplicationDbContext.cs (M): +7 DbSet impl
- ApplicationDbContextModelSnapshot.cs (M): EF auto-update
- 20260526110207_AddEmployeeProfiles.cs (NEW, EF auto-gen)
- 20260526110207_AddEmployeeProfiles.Designer.cs (NEW, EF auto-gen)
DbInitializer.cs (M, em main solo Task 3b ~90 LOC):
- using SolutionErp.Domain.Hrm import added
- SeedDemoEmployeeProfilesAsync method appended (end of class)
- Register call after SeedDemoUsersAsync line 88 (depend user exist first)
- NOT gated DemoSeed:Disabled flag (infrastructure data per gotcha #51 lesson)
- 30 demo profile mirror 30 user @solutions.com.vn + sequential code
NV/{YYYY}/0001..0030 + placeholder masked CMND/BHXH/Bank (bro UAT update
qua FE Page Task 5) + EmployeeCodeSequence row LastSeq=30 → production
gen tiếp 0031+
Verify:
- dotnet build: 0 err 2 unrelated warn DocxRenderer (2.49s + 7.86s rebuild)
- dotnet ef database update _Dev: Mig 34 applied (top of __EFMigrationsHistory)
- dotnet test: **120/120 PASS** baseline preserved (no test add Phase 10
test-after per §7 UAT mode — test bundle defer Task 4+5+6 done)
Stats target Phase 10 end: 33→42 mig (+9 Mig 34-42), 60→85 tables (+25),
~148→250 endpoint (+100), 38→60 FE pages (+22). Current after this commit:
33→34 mig + 60→66 tables + endpoint/FE unchanged (G-H1 Task 4+5 next).
Pattern reusable cross-project:
- Pattern 12-bis cross-module entity cookie-cutter mirror reinforced 3×
(S29 Plan B Contract V2 + S33 Plan B G-H1 EmployeeProfile)
- Infrastructure seed OUT of DemoSeed gate (gotcha #51 lesson, mirror Mig 32
SeedSampleContractWorkflowV2)
- DiaChi dual-write FK + freetext từ ngày đầu (NamGroup 1675 NV drift lesson)
Pending Plan B G-H1 Phase 2 (chờ anh main signal kick off):
- Task 4 — Implementer BE CQRS handler + 6 endpoint controller
- Task 5 — Implementer FE 2 app EmployeesPage 3-panel + 6 section tabs
- Task 6 — Em main Permission menu Hrm_HoSo* seed
- Task 7 — Reviewer pre-commit + CICD post-deploy verify
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 38f1c4d2d9 |
[CLAUDE] Infra: Plan B Hotfix CICD — SeedSampleContractWorkflowV2 OUT of DemoSeed gate
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m32s
CICD Monitor post-deploy verify (Run #231 SUCCESS) caught CRITICAL: SeedSampleContractWorkflowV2Async nested inside `if (!demoSeedDisabled)` branch → prod has DemoSeed:Disabled=true (Plan T S23 t10) → seed SKIP → QT-HD-V2-001 KHÔNG tồn tại prod → Drafter Workspace dropdown V2 EMPTY → V2 contract path BLOCKED end-to-end UAT. Fix: PROMOTE SeedSampleContractWorkflowV2 ra ngoài DemoSeed gate. Lý do architectural: - Sample workflow là INFRASTRUCTURE config (như Roles + Departments + Catalogs) - KHÔNG phải demo data wipeable - Production cần để Drafter create V2 contract - Admin có thể edit/delete/disable qua Designer sau seed (idempotent skip) Pattern lesson reusable cross-project: phân biệt INFRASTRUCTURE seed (always run) vs DEMO seed (gated by flag). Bảng phân loại: - INFRASTRUCTURE always: Roles, Departments, Catalogs, MenuTree, AdminPerms, Templates - DEMO gated: DemoUsers (30 sample, Plan T disabled prod), DemoContracts, DemoPE, SampleWorkflows - INFRASTRUCTURE NEW post-S29: SampleContractWorkflowV2 (cần cho V2 path work) Verify: - dotnet build PASS 0 err - Mig 32 + Mig 33 prod đã apply (Run #231 success) - Sample seed sẽ chạy on next IIS recycle post-push - Idempotent: skip nếu QT-HD-V2-001 already exists (rare race admin tự seed Designer trước) Post-deploy expect: - ApprovalWorkflows table +1 row Code=QT-HD-V2-001 ApplicableType=3 IsActive=1 IsUserSelectable=1 - Drafter login fe-user → /contracts/new → Workspace dropdown "Quy trình duyệt V2" có 1 option CICD Monitor ROI: caught BEFORE bro UAT 401/empty dropdown experience. Smart Friend guard pattern proven 4× cumulative S22 #44 + S25 #48 + S29 Plan B Reviewer ApplicableType + S29 Plan B CICD DemoSeed gate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 1f199b01a5 |
[CLAUDE] Infra: Plan B Chunk B2 — UPSERT ContractLevelOpinion + ResolveActorFullName helper
Replace TODO marker trong Chunk B |
|||
| 26c98d3c11 |
[CLAUDE] Domain+App+Infra: Plan B Chunk C — Mig 33 ContractLevelOpinions cookie-cutter mirror PE Mig 26
- Domain/Contracts/ContractLevelOpinion.cs (NEW entity mirror PE — AuditableEntity, 4 field core + 2 nav) - Domain/Contracts/Contract.cs (+LevelOpinions nav collection) - Migrations/20260522052240_AddContractLevelOpinions.cs (3-file rule: .cs + .Designer.cs + Snapshot) - Configurations/ContractLevelOpinionConfiguration.cs (NEW separate file, mirror PE pattern) - IApplicationDbContext.cs + ApplicationDbContext.cs (+DbSet<ContractLevelOpinion>) UNIQUE composite (ContractId, ApprovalWorkflowLevelId) — 1 row per HĐ × Level. FK Cascade Contract + Restrict ApprovalWorkflowLevel. SignedByUserId KHÔNG nav (denorm SignedByFullName tránh cascade khi xoá user). Mirror PE Mig 26 pattern (S19 2026-05-09) EXACT — UPSERT row khi Approver duyệt qua Service ApproveV2Async (Plan B Chunk B em main |
|||
| 138469db4e |
[CLAUDE] Infra+App: Plan B Chunk B — Service ApproveV2Async branch + gen mã HĐ adapt
Mirror PE PurchaseEvaluationWorkflowService.cs:ApproveV2Async (line 446-634).
V1 legacy giữ behavior cũ — 7 prod contract chạy nhánh này. V2 mới pin
ApprovalWorkflowId chạy ApproveV2Async helper.
Changes:
- ContractWorkflowService.cs:
- TransitionAsync +skipToFinal=false param F2 (Mig 31 Plan K mirror PE)
- Drafter trình init CurrentApprovalLevelOrder=1 nếu V2 schema pin
- APPROVE STEP branch V2/V1 dispatch theo ApprovalWorkflowId
- +ApproveV2Async helper ~150 LOC (mirror PE pattern):
- Load AW.Steps.Levels OR-of-N
- Match approver actor.Id ∈ pendingLevelGroup.ApproverUserId
- Add ContractApproval row + enrich comment skipPrefix
- skipToFinal F2: AllowApproverSkipToFinal guard + advance pointer last
- Advance level/step normal
- Terminal: gen mã HĐ RG-001 + Phase=DaPhatHanh (khác PE just DaDuyet)
- IContractWorkflowService.cs: TransitionAsync +skipToFinal=false param
- ContractFeatures.cs: caller TransitionAsync use named arg ct: ct (skip optional)
TODO Chunk C: UPSERT ContractLevelOpinion (table chưa tồn tại — Mig 33
sẽ scaffold + entity + EF config). Block UPSERT add ở đây sau Chunk C done.
Verify:
- dotnet build SolutionErp.slnx PASS 0 err, 2 pre-existing DocxRenderer warn
- dotnet test 111/111 PASS (58 Domain + 53 Infra) — 0 regression
- V1 legacy path UNCHANGED (7 prod contract giữ behavior)
Plan B chain (6 chunks):
- A1
|
|||
| a85e437478 |
[CLAUDE] Infra: Plan B Chunk A2 — Mig 32 Contract V2 schema + Configuration + Seed sample workflow
Cookie-cutter mirror PE Mig 23+24 GỘP thành 1 Mig 32 (ADD 2 column +
FK + IX). Mirror Mig 26 pattern cho FK Restrict.
Files added/modified:
- Migrations/20260522051059_AddApprovalWorkflowToContract.cs (3-file rule ✅)
- Migrations/20260522051059_AddApprovalWorkflowToContract.Designer.cs
- Migrations/ApplicationDbContextModelSnapshot.cs (updated)
- Configurations/ContractConfiguration.cs (+HasIndex + FK Restrict ApprovalWorkflows)
- Persistence/DbInitializer.cs (SeedSampleContractWorkflowV2 idempotent QT-HD-V2-001)
Mig 32 Up():
- ADD COLUMN Contracts.ApprovalWorkflowId Guid? NULL
- ADD COLUMN Contracts.CurrentApprovalLevelOrder int? NULL
- ADD INDEX IX_Contracts_ApprovalWorkflowId (filtered NOT NULL)
- ADD FK FK_Contracts_ApprovalWorkflows_ApprovalWorkflowId Restrict
Seed sample workflow (UAT smoke + admin Designer default):
- Code: QT-HD-V2-001 Name: "Quy trình duyệt HĐ mẫu UAT V2"
- ApplicableType: 3 (Contract) IsActive: true IsUserSelectable: true
- 1 Step "Bước 1 - Phòng CCM" + 1 Level + Approver Lê Văn Bình CCM
- Idempotent: skip nếu Code+Version existing
V1 coexist: 7 prod contract giữ WorkflowDefinitionId; V2 mới pin
ApprovalWorkflowId. Service ApproveV2Async (Chunk B em main) sẽ branch.
Verify (Implementer):
- dotnet build SolutionErp.slnx PASS 0 err (em main WIP stashed for verify)
- dotnet ef database update Dev PASS (Mig 32 applied)
- 3-file rule Mig: mig.cs + Designer.cs + Snapshot.cs
Plan B chain (6 chunks):
- A1
|
|||
| 68bceddabb |
[CLAUDE] Infra: Plan CA Chunk D2 hotfix — Password ≥12 chars cho catalog.manager (S22+2 policy)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m31s
Reviewer spawn pre-push verify catch CRITICAL bug Chunk D |
|||
| 4a592cfadb |
[CLAUDE] Infra: Plan CA Chunk D — Seed demo user catalog.manager + role CatalogManager
DbInitializer.cs SeedDemoUsersAsync array thêm 1 entry: - Email: catalog.manager@solutions.com.vn (password default User@123456 per SeedDemoUsersAsync logic) - FullName: "NV Quản lý Danh mục" - Dept: PRO (Cung ứng) - Position: "Nhân viên Quản lý Danh mục Dùng chung" - Roles: [AppRoles.CatalogManager] Cấp 1 demo user mặc định để bro UAT login fe-user verify 9 menu danh mục (Master + Suppliers + Projects + Departments + Catalogs + 4 sub-catalogs). Admin có thể tạo thêm user gán role CatalogManager qua /system/users + /system/permissions Matrix tự reflect 9 menu key. Verify: - dotnet build SolutionErp.slnx PASS 0 err, 2 pre-existing DocxRenderer warn - Idempotent: SeedDemoUsersAsync skip nếu user existing email - DbInitializer chạy mỗi lần API startup → demo user auto-seed lên prod sau deploy Plan CA wrap (4 chunk): - A |
|||
| 80d39a06fb |
[CLAUDE] Domain+Infra: Plan CA Chunk A — Add role CatalogManager + seed 9 menu CRUD
- AppRoles.cs +CatalogManager const + update All array (6 LOC)
- DbInitializer.cs RoleLabels +CatalogManager ("DM", "Nhân viên Quản lý danh mục")
- DbInitializer.cs +SeedCatalogManagerPermissionsAsync() method ~50 LOC
- Wire seed call vào SeedAdminPermissionsAsync chain (idempotent, mirror SeedPePermissionDefaults pattern)
Permission scope: 9 menu key CRUD all true
- Master (root) + Suppliers + Projects + Departments
- Catalogs (root) + CatalogUnits + CatalogMaterials + CatalogServices + CatalogWorkItems
Verify:
- dotnet build SolutionErp.slnx PASS 0 err, 2 pre-existing DocxRenderer warn
- Idempotent: skip per-(role,menuKey) existing row
- 0 FE touch (Chunk B Implementer parallel commit
|
|||
| a734bf2b8b |
[CLAUDE] PurchaseEvaluation: Plan AC — fix Lịch sử duyệt panel show Trả lại + Duyệt vượt cấp
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m27s
Bro UAT 2026-05-19 screenshot: panel "Lịch sử duyệt" KHÔNG show Return mode events (Bro Trả lại từ Phan Văn Chương → Trà missing) + KHÔNG distinct event Duyệt vượt cấp (skipToFinal F2). Root cause: - PurchaseEvaluationApprovals.Add() chỉ ở Approve branch (line 472 V2 + 660 V1) - Reject branch line 75-103 NEVER adds Approval row — chỉ log Changelog - skipToFinal advance branch line 532-572 dùng existing line 472 row nhưng comment KHÔNG distinct "vượt cấp" semantic vs approve thường Fix Plan AC: 1. BE Service.cs Reject branch (line 75-103): capture pre-call Step/Level trước ApplyReturnModeAsync mutate pointer, add Approval row sau khi mutate: Decision=Reject + FromPhase + ToPhase=evaluation.Phase + Comment carry from-position + mode summary. Cover cả Trả lại (TraLai+pointer-mode) + Từ chối (TuChoi terminal). 2. BE Service.cs line 472 Approve branch: enrich Comment với prefix "[Duyệt vượt cấp tới Cấp cuối]" khi skipToFinal=true để Lịch sử duyệt distinguish vượt cấp với approve thường. 3. FE PeDetailTabs.tsx × 2 app ApprovalsTab: add Decision badge phân biệt Approve (emerald) / Trả lại (amber) / Từ chối (rose). Vì 3/4 mode Trả lại (OneLevel/OneStep/Assignee) giữ Phase=ChoDuyet → fromPhase→toPhase badge giống Approve. Decision badge bù visual phân biệt. Verify: - dotnet build clean 0 err 2 warn (pre-existing DocxRenderer) - dotnet test 111/111 PASS - npm build × fe-user + fe-admin PASS 0 TS err Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| cdfd54212c |
[CLAUDE] PurchaseEvaluation: Plan AB Chunk A — fix Changelog visibility Bug 1 Budget Adjust + Bug 2 Return Mode
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Failing after 1m6s
- BE ApplyReturnModeAsync 4 mode add Changelog.Add() common path (refactor Drafter early return) - FE PeDetailTabs.tsx HistoryTab filter extend cover Header+ngân sách (B1) + Workflow+Trả lại (B2) - FE empty placeholder + comment update reflect new filter scope - Mirror 2 app §3.9 Bug 1: Budget Adjust handler đã log (Header+Update) nhưng FE filter strict TraLai-only Bug 2: Return mode Service không log Changelog — chỉ approval phase transition Verify: - Build clean 0 err - npm build × 2 app pass 0 TS err - 111 test baseline preserve (UAT skip test-after defer) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| ee776d5745 |
[CLAUDE] Domain+App+Api+FE-User+FE-Admin: Plan AA Chunk A - BE IsUserSelectable filter + menu seed Pe_DuyetNcc_WfView + sidebar widen w-72 xl:w-80 + revert Plan U truncate
BE changes:
- MenuKeys.cs +helper PurchaseEvaluationWorkflowView(typeCode) => "Pe_{typeCode}_WfView"
- DbInitializer.cs SeedMenuTreeAsync:
- tree.Add LuongDuyet (Order=2 first child) cho 2 type PE
- INSERT-only loop -> INSERT-OR-UPDATE-Order (shift existing prod rows Order+1)
- Idempotent: skip nếu Order match, UPDATE nếu mismatch
- DbInitializer.cs SeedPurchaseEvaluationPermissionDefaultsAsync +WfView leaf cho 7 role Read
- ApprovalWorkflowV2AdminFeatures.cs GetAwAdminOverviewQuery +IsUserSelectable bool? = null
+ handler conditional Where(d => d.IsUserSelectable == ius)
- ApprovalWorkflowsV2Controller.cs Overview signature +[FromQuery] bool? isUserSelectable
pass-through to mediator (gotcha #44 fix preserved class-level [Authorize] bare)
FE Layout changes (mirror 2 app rule §3.9):
- fe-user resolvePath regex (List|Create|Pending|WfView) + route
/purchase-evaluations/workflow-matrix?type=N
- fe-user + fe-admin sidebar w-60 xl:w-72 -> w-72 xl:w-80 (+48/+32px gain)
- Revert Plan U S23 t11 truncate × 5 sites (3 fe-user MenuGroup+MenuLeaf+StaticLeaf
+ 2 fe-admin MenuGroup+MenuLeaf). Keep min-w-0 flex-1 + shrink-0 + title
tooltip (no harm). Bro request hiển thị đầy đủ label custom Mig 27 dài.
Why:
- User UAT request 2026-05-15: thêm menu "Luồng duyệt" trên Danh sách hiển thị
ma trận phân quyền workflow V2 admin Designer ghim ra cho user xem trước khi
tạo phiếu. Filter IsUserSelectable=true (Mig 25).
- Sidebar Plan U S23 t11 truncate hiển thị "..." → bro muốn full label.
Widen sidebar +32-48px + bỏ truncate cho phép wrap natural khi cực dài.
Verify:
- dotnet build SolutionErp.slnx PASS clean 0 err 2 warn pre-existing DocxRenderer
- Investigator Pre-A confirm gotcha #44 đã fix permanent từ 2026-05-08
- Reviewer cumulative PASS 0 critical / 0 major / 0 minor blocker
Pending Chunk B: FE WorkflowMatrixViewPage.tsx ~215 LOC + types + App.tsx route.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 0b97840674 |
[CLAUDE] Infra Api: Chunk T — Disable auto re-seed demo data qua DemoSeed:Disabled flag (appsettings)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m22s
Bro UAT post-Plan R+S phát hiện: 4 phiếu [DEMO]-A/B + 2 V1 workflows + 1
V2 sample TỰ ĐỘNG RE-SEED sau BE deploy commits Plan P+Q+R+S → IIS recycle
→ DbInitializer.InitializeAsync auto-seed lại 5 demo seed methods.
Plan T fix root cause: Config flag `DemoSeed:Disabled` trong
appsettings.json (production default true) → DbInitializer check flag →
skip 5 demo seed methods.
Note: appsettings.Production.json bị .gitignore (chứa secrets prod), nên
em set flag mặc định trong appsettings.json (commit qua git) — production
inherit true. Dev override false trong appsettings.Development.json để
test fresh demo seed local.
Methods SKIP khi DemoSeed:Disabled=true:
1. SeedWorkflowDefinitionsAsync (V1 PE workflow QT-DN-A v1 + QT-DN-B v1)
2. SeedPurchaseEvaluationWorkflowsAsync (V1 PE workflow extended)
3. SeedDemoContractsAsync ([DEMO] HĐ 7-type sample)
4. SeedDemoPurchaseEvaluationsAsync ([DEMO] PE 4 sample with V1 pin)
5. SeedSampleApprovalWorkflowsV2Async (V2 sample mẫu UAT type B)
Methods KEEP (luôn chạy):
- SeedRoles + SeedAdmin + SeedDepartments + SeedDemoUsers (30 UAT users)
- SeedMenuTree + SeedAdminPermissions + SeedDemoMasterData (Supplier/Project)
- SeedContractTemplates + SeedCatalogs + BackfillContractCodes
- BackfillUserEmailDomain (Phase 6 rebrand migration helper)
Files changed:
- src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs:
+ using Microsoft.Extensions.Configuration
+ Read DemoSeed:Disabled flag từ IConfiguration
+ Log "DemoSeed:Disabled=true — skip ..." khi flag true
+ Wrap 5 method seed conditional `if (!demoSeedDisabled) { ... }`
- src/Backend/SolutionErp.Api/appsettings.json:
+ "DemoSeed": { "Disabled": true } + comment narrative
- src/Backend/SolutionErp.Api/appsettings.Development.json:
+ "DemoSeed": { "Disabled": false } override cho dev
Workflow expected sau deploy:
1. CI deploy commit T → IIS recycle app pool
2. BE startup → DbInitializer reads DemoSeed:Disabled=true
3. Skip 5 demo seed methods → DB state preserved
4. Bro UAT clean slate hoàn toàn — Designer setup workflow mới from scratch
Pending T5: Final DELETE current state (4 PE + 2 V1 + 1 V2 mẫu UAT) sau
deploy applied flag. T6 verify no re-seed loop sau re-deploy.
Verify:
- dotnet build SolutionErp.slnx clean (0 err, 2 warn pre-existing)
- dotnet test SolutionErp.slnx **111/111 PASS** unchanged
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| ae01ca56f2 |
[CLAUDE] PurchaseEvaluation Tests: Chunk O1-O5 — HOTFIX 4 lookup sites cùng pattern per-NV (Plan N point 9 cascade)
Bro UAT 2026-05-15 sau Plan N deploy phát hiện 2 bug mới:
1. Actor NV Test trong OR-of-N slot click "Trả lại Người chỉ định" → toast
"Không phải lượt bạn — chỉ NV Cấp duyệt hiện tại mới được Trả lại / Từ
chối phiếu" mặc dù NV Test đúng trong slot.
2. F2 Duyệt thẳng Cấp cuối → trỏ đến Phan Văn Chương Bước 2 Cấp 2 thay vì
Nguyễn Văn Trường Bước 3 Cấp 1 (BOD) — defer follow-up vì F2 logic line
483-524 đã đúng (lastStepIdx + lastLevelMaxOrder), cần verify workflow
v14 DB structure.
Audit em main: Plan N chỉ fix 1/5 lookup sites — còn 4 sites cùng bug pattern:
1. Service.cs:201 EnsureCanRejectV2Async — bug bro UAT 1 ROOT CAUSE
2. Service.cs:248 ApplyReturnModeAsync — read Allow flag từ row đầu
3. DetailFeatures.cs:72 F3 EnsureEditableForDetailsAsync — cùng bug
4. Features.cs:311 F4 AdjustBudgetCommand — cùng bug
4 fix surgical (~30 LOC BE total):
**Site 1** (`PurchaseEvaluationWorkflowService.cs:201`):
```diff
- var currentLevel = step.Levels.FirstOrDefault(l => l.Order == curLvl);
- if (currentLevel?.ApproverUserId != actorId)
+ var currentLevel = step.Levels.FirstOrDefault(l =>
+ l.Order == curLvl && l.ApproverUserId == actorId);
+ if (currentLevel is null)
throw new ForbiddenException("Không phải lượt bạn — ...");
```
**Site 2** (`PurchaseEvaluationWorkflowService.cs:248`): ApplyReturnModeAsync
+`Guid? actorUserId` param 4th + caller TransitionAsync:94 update. Filter
`l.ApproverUserId == actorUserId` trong FirstOrDefault. Non-admin actor
KHÔNG match slot → currentLevel=null → validation skip (mode logic switch
KHÔNG dùng currentLevel object — chỉ dùng curStepIdx + curLevel int values).
Admin bypass validation existing line 252.
**Site 3** (`PurchaseEvaluationDetailFeatures.cs:72`):
```diff
- var level = step?.Levels.FirstOrDefault(lv => lv.Order == levelOrder);
- if (level is null) throw ConflictException("schema lỗi");
- if (!level.AllowApproverEditDetails) throw ConflictException(...);
- if (level.ApproverUserId != actorUserId) throw ForbiddenException(...);
+ var level = step?.Levels.FirstOrDefault(lv =>
+ lv.Order == levelOrder && lv.ApproverUserId == actorUserId);
+ if (level is null) throw ForbiddenException(...);
+ if (!level.AllowApproverEditDetails) throw ConflictException(...);
```
**Site 4** (`PurchaseEvaluationFeatures.cs:311`):
```diff
- var level = step.Levels.FirstOrDefault(l => l.Order == curLvl);
- if (level is null) throw ConflictException("schema lỗi");
- if (!level.AllowApproverEditBudget) throw ConflictException(...);
- if (level.ApproverUserId != actorId) throw ForbiddenException(...);
+ var level = step.Levels.FirstOrDefault(l =>
+ l.Order == curLvl && l.ApproverUserId == actorId);
+ if (level is null) throw ForbiddenException(...);
+ if (!level.AllowApproverEditBudget) throw ConflictException(...);
```
**Regression test** (`PurchaseEvaluationPerNvLookupRegressionTests.cs` 3 test):
1. `TransitionReject_ActorD_LastInSlot_AllowsRejectViaDrafterMode` —
Actor D (non-first-row trong OR-of-N) trả lại Drafter mode → no throw.
Pre-fix: throw "Không phải lượt bạn" vì handler check row đầu A.
2. `TransitionReject_Outsider_NotInSlot_ThrowsForbidden` — Outsider không
trong slot → throw đúng intent (verify fix KHÔNG over-permissive).
3. `TransitionRejectOneLevel_ActorC_HasFlagWhileOthersDont_AllowsMode` —
Actor C only tick AllowReturnOneLevel, 3 NV khác KHÔNG. Actor C click
"Trả lại 1 Cấp" → mode allowed. Pre-fix: read flag từ row A (false) →
throw ConflictException "không bật mode OneLevel".
Pattern reinforced: per-NV admin opt-in flag wire **5 lookup sites** đều
phải discriminate ApproverUserId. Plan N chỉ catch 1/5. Plan O catch 4/5
còn lại. Memory user-level cần update danh sách 5 sites cho future audit.
Verify:
- dotnet build SolutionErp.slnx clean (0 err, 2 warn pre-existing DocxRenderer)
- dotnet test SolutionErp.slnx **111/111 PASS** (+3 từ 108 baseline Plan N)
Pending Chunk O7: docs + memory update commit + push.
Pending Chunk O8: CICD Monitor post-deploy verify.
Pending follow-up Bug 2 F2 đến Phan Văn Chương: verify workflow v14 DB.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| c2042ef956 |
[CLAUDE] PurchaseEvaluation: Chunk M1 — Fix F1.OneLevel/OneStep edge case Bước 1 → giữ ChoDuyet (KHÔNG fallback Drafter)
Bro UAT S23 t3: "Các tính năng trả lại 1 cấp hoặc chỉ định hoặc edit cho xử lý ở trạng thái đang gửi duyệt luôn, không về draft." Investigator audit confirm 4 mode F1.OneLevel/Assignee + F2 + F3+F4 main path đã giữ Phase=ChoDuyet (Mig 28-31 cumulative). Edge case duy nhất còn fallback Drafter (Phase=TraLai): - F1.OneLevel khi đang Bước 1 Cấp 1 (curStepIdx=0, curLevel=1) — no further back - F1.OneStep khi đang Bước 1 (curStepIdx=0) Logic cũ (line 303-310 OneLevel + 325-332 OneStep): ``` evaluation.Phase = PurchaseEvaluationPhase.TraLai; // 98 evaluation.CurrentWorkflowStepIndex = null; evaluation.CurrentApprovalLevelOrder = null; evaluation.SlaDeadline = null; return "Trả về Người soạn thảo (fallback — đang Bước 1 Cấp 1)"; ``` Logic mới — reset (0, 1) giữ ChoDuyet: ``` evaluation.CurrentWorkflowStepIndex = 0; evaluation.CurrentApprovalLevelOrder = 1; summary = "Action 'Trả lại 1 Cấp/Bước' không lùi được — phiếu reset về Approver Bước 1 Cấp 1"; // SLA reset 7d ở cuối hàm cho 3 mode còn lại ``` Semantic mới (per bro chốt AskUserQuestion Plan M): - Phase giữ ChoDuyet (KHÔNG TraLai=98) - Pointer reset về (0, 1) = chính Approver A hiện tại (effectively no-op) - SLA reset 7d (cuối hàm switch áp dụng cho cả 3 mode F1 non-Drafter) - Audit log rõ "không lùi được" để Drafter/Admin biết action không hiệu lực KHÔNG đụng: - F1.Drafter (line 268-275) giữ nguyên semantic Phase=TraLai - F1.Assignee (line 335-360) giữ nguyên throw nếu không match - F2 ApproveV2Async skipToFinal (line 483-524 Plan K L1 vừa fix) - F3 EnsureEditableForDetailsAsync (PurchaseEvaluationDetailFeatures.cs:42) - F4 AdjustBudgetCommand handler (PurchaseEvaluationFeatures.cs:272-329) Verify: - dotnet build src/Backend/SolutionErp.Infrastructure clean (0 err, 2 warn pre-existing DocxRenderer) - Service.cs 13+/13- LOC change (1 file, surgical edit) - Pending Chunk M2: 2 edge case test (Implementer Case 3 spawn) + verify K7 Approver F2 không cascade - Pending Chunk M3: FE label rename Phase=TraLai "Trả lại" → "Cần chỉnh sửa lại" (Implementer Case 2 spawn × 2 app) - Pending Chunk M4: docs + memory update + push + CICD verify Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| f3db9e6cc0 |
[CLAUDE] PurchaseEvaluation: Chunk L1 — Fix F2 skipToFinal semantic: skip pointer tới NV cuối (KHÔNG terminate DaDuyet)
Bro UAT S23 t2 catch: Plan K K2 implement F2 SAI semantic — set Phase=DaDuyet terminal auto-approve. Bro intent: "Duyệt thẳng đến CEO, bỏ qua các bước khác chứ ko phải chuyển sang đã duyệt." Refactor Service.cs ApproveV2Async F2 branch: - Resolve lastStepIdx = steps.Count - 1, lastLevelMaxOrder = max(LevelOrder) trong Step cuối - Advance pointer: CurrentWorkflowStepIndex = lastStepIdx + CurrentApprovalLevelOrder = lastLevelMaxOrder - Phase GIỮ NGUYÊN ChoDuyet — NV cuối (CEO/last approver) vẫn cần ký thật để tiến DaDuyet - Audit log "Approver skip thẳng tới Bước X Cấp Y (NV cuối) — bỏ qua các Bước/Cấp trung gian" - Guard no-op: actor đã ở slot cuối → fall through advance logic (normal → DaDuyet) (KHÔNG double-advance khi skipToFinal=true ngay slot cuối) - Reset SLA 7d cho NV cuối nhận lại FE × 2 app PeWorkflowPanel.tsx (mirror rule §3.9): - Description text update: "Phiếu sẽ skip tới NV cuối (CEO/cấp ký cuối) — NV cuối vẫn cần duyệt thật để hoàn tất." - Amber warning update: "Bỏ qua mọi Cấp/Bước trung gian, phiếu chuyển thẳng tới NV cuối. NV cuối vẫn phải ký duyệt thật để phiếu thành 'Đã duyệt'." Verify: - dotnet build production projects clean (0 err, 2 pre-existing warn) - npm run build × 2 app pass Pattern lesson saved memory: Service skipToFinal semantic = advance pointer NOT terminate. K7 tests TODO update: 3 Approver F2 tests assert pointer moved to last slot, NOT Phase=DaDuyet. Defer test fix sau UAT confirm UX. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 2ea8977d0f |
[CLAUDE] Backout: Chunk D — K5 cleanup F2 zombie endpoint + UsersPage column + DTO field
Reviewer K2 Major #1: PATCH /api/users/{id}/allow-skip-final endpoint Admin tick = NoOp swallow silent (K1 sentinel → confusion UX). Full backout Plan D S22 stack: BE drop (7 files): - UsersController.cs: DELETE PATCH /allow-skip-final endpoint + SetAllowDrafterSkipToFinalBody record - UserFeatures.cs: DELETE SetUserAllowDrafterSkipToFinalCommand + Handler + UserDto.AllowDrafterSkipToFinal field + list/get DTO mapping sentinel-false references - ApprovalWorkflow.cs: REWRITE stale narrative line 78-80 (Reviewer Major #2 Mig 31 semantic) + docstring AllowApproverSkipToFinal line 108 clean stale Users storage ref - PurchaseEvaluationFeatures.cs: REWRITE Command DTO comment line 401 (Reviewer Minor #3) - ApprovalWorkflowConfiguration.cs: APPEND Mig 31 narrative line 22-24 (Reviewer Minor #4) + clean storage move comment line 87 - ApprovalWorkflowV2AdminFeatures.cs: clean DTO comment line 58 stale "F2 xuống User table" - IPurchaseEvaluationWorkflowService.cs + PurchaseEvaluationDtos.cs: clean stale "storage Users.AllowDrafterSkipToFinal" comments FE Admin drop (2 files): - UsersPage.tsx: DELETE "Skip cuối" column + FastForward badge + FastForward import + allowSkipMut mutation hook + FastForward toggle button - types/users.ts: DELETE allowDrafterSkipToFinal field fe-user KHÔNG đụng (no UsersPage admin-only; K6 sẽ handle Workspace Drafter checkbox). FE Designer page KHÔNG đụng (K3 done; 2 stale comment leftover deferred K6). Plan K refactor F2 storage Users → Levels (Mig 31) complete cumulative cleanup. Pattern reusable: post-refactor full cleanup (BE endpoint + Command + DTO + FE column + types + stale narratives) atomic 1 commit thay vì leak zombie state. Verify: - dotnet build production projects 0 err (2 pre-existing DocxRenderer warn) - npm build fe-admin 0 TS err (no new warning) - Grep AllowDrafterSkipToFinal + allow-skip-final + allowDrafterSkipToFinal zero results across src/Backend (excl Migrations history) + fe-admin/src Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 364aef63fd |
[CLAUDE] PurchaseEvaluation: Chunk B — Mig 31 K2 Approver F2 branch APPROVE STEP + DTO refactor
Service ApproveV2Async +skipToFinal 8th param. APPROVE STEP branch sau UPSERT PEL opinion: check admin OR matchingLevel.AllowApproverSkipToFinal → set Phase=DaDuyet terminal directly, clear pointer + SLA, audit "[Approver duyệt thẳng Cấp cuối — Bước X Cấp Y → DaDuyet]". Non-admin + flag off → ConflictException. ApproveV1LegacyAsync: throw nếu skipToFinal=true non-admin (V1 legacy không hỗ trợ per-Approver-slot flag). Caller TransitionAsync line ~144 pass skipToFinal vào ApproveV2Async. Drafter SUBMIT branch ignore skipToFinal (K1 đã remove F2 Drafter semantic stub) — Mig 31 marker comment cleanup. DTO ApprovalWorkflowOptionsDto +bool AllowApproverSkipToFinal (7th field). DTO PurchaseEvaluationDetailBundleDto -DrafterAllowSkipToFinal field. GetPe handler populate 7 Allow* từ curLevel (Mig 29+30+31 cumulative). Sentinel `var drafterAllowSkipToFinal = false;` cleanup từ K1. IPurchaseEvaluationWorkflowService.cs comment skipToFinal semantic refactor: Drafter from Nháp → Approver during ChoDuyet skip thẳng Cấp cuối. Pattern reusable: feedback_per_nv_permission_scope.md reinforced 3× cumulative (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2). Verify: - dotnet build production projects clean (0 err, 2 warnings pre-existing DocxRenderer) - Test fail at K1 expected (test file references removed prop, K7 sẽ fix) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| db6625304a |
[CLAUDE] Domain: Chunk A — Mig 31 swap F2 storage Users→ApprovalWorkflowLevels (Approver scope ChoDuyet)
Mig 31 RefactorSkipToFinalToApproverLevel — 2 stage manual reorder: - ADD ApprovalWorkflowLevels.AllowApproverSkipToFinal bit NOT NULL DEFAULT 0 - DROP Users.AllowDrafterSkipToFinal (semantic mới khác hẳn — admin re-config qua Designer) - NO BACKFILL (Option A — accept lose 4 prod user value per K0-bis audit) Plan K refactor F2 semantic: Drafter from Nháp → Approver during ChoDuyet skip thẳng Cấp cuối. Mirror F3+F4 admin opt-in per-Approver-slot pattern (Mig 29 + Mig 30) reinforced 3× cumulative. Service line 121-157 F2 Drafter SUBMIT branch REMOVED stub (K2 sẽ add Approver F2 branch trong APPROVE STEP line ~393-525). TransitionAsync skipToFinal param 8th KEPT cho K2 repurpose. Application layer compile-break fix transient: UserDto field mapping + GET handler + LIST handler + SetUserAllowDrafterSkipToFinalCommandHandler NoOp + PurchaseEvaluationFeatures drafter flag → sentinel false. DTO + Command signature UNCHANGED (K2 chunk Chủ trì sẽ refactor DTO/Command theo plan). 4 prod user (fin.pp + pm.nv + nv.test + truong.nguyen) lose AllowDrafterSkipToFinal=true per bro Option A. Audit trail trong session log K8. Verify: - dotnet ef migrations add pass - dotnet ef database update Dev + Design pass (Mig 31 applied both DB) - dotnet build src/Backend/SolutionErp.Api production projects clean (0 err, 0 warn) - dotnet test SKIPPED per UAT mode (memory feedback_uat_skip_verify) — K7 chunk fix remaining PurchaseEvaluationWorkflowServiceReturnModeTests.cs:253 reference Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| b079b27343 |
[CLAUDE] PE-Workflow: S22+5 Chunk A — Mig 30 +AllowApproverEditBudget per-Level slot
Bro clarify spec S22+4: - KHÔNG đổi logic edit ngân sách (Drafter Nháp/TraLai vẫn duy nhất default) - Thêm flag per-NV slot trong Designer: "Cho phép NV này edit Section ngân sách lúc đang duyệt" (mirror pattern F3 AllowApproverEditDetails Mig 29) Mig 30 `AddAllowApproverEditBudgetToLevels`: - ALTER ApprovalWorkflowLevels +AllowApproverEditBudget bit NOT NULL DEFAULT 0 - 3-file rule (mig.cs + Designer.cs + Snapshot.cs) - Apply LocalDB Dev + Design Domain entity ApprovalWorkflowLevel +AllowApproverEditBudget (default false). EF config HasDefaultValue(false). DTO AwLevelDto + ApprovalWorkflowOptionsDto + CreateAwLevelInput all extend +AllowApproverEditBudget. PE GET handler populate currentLevelOptions thêm AllowApproverEditBudget từ curLevel slot. Admin Designer GET/POST handler propagate flag. AdjustBudgetCommand handler refactor ChoDuyet branch: - Trước: check actor match level.ApproverUserId (cho phép mặc định) - Sau: check level.AllowApproverEditBudget=true AND actor match ApproverUserId → throw ConflictException nếu slot chưa được cấp quyền Verify: - dotnet build SolutionErp.slnx — 0 err, 2 warn DocxRenderer pre-existing - Mig 30 applied Dev + Design DB Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 40f64c6b32 |
[CLAUDE] PE-Workflow: UAT S22+1 — disable cả 3 button khi không quyền + BE guard
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m29s
User UAT feedback: "Nếu đã không được quyền thao tác thì ko được quyền thao tác hết tất cả các hành động" — trước đây chỉ "Duyệt" disabled, "Trả lại" + "Từ chối" vẫn enabled (design intent S17 cũ). FE 2 app mirror (PeWorkflowPanel.tsx): - `isDisabled = blockedByV2Level` (drop `isForwardApprove &&` qualifier) - Tooltip update "mới thao tác được (Duyệt / Trả lại / Từ chối)" - Comment refresh ghi UAT S22+1 spec + cross-ref BE EnsureCanRejectV2Async BE defense-in-depth (PurchaseEvaluationWorkflowService.cs): - Helper mới `EnsureCanRejectV2Async` mirror FE actorInV2Level logic: Skip silent khi admin/V1/non-ChoDuyet/no actor/no pointer. Throw ForbiddenException khi V2 + ChoDuyet + actor != currentLevel.ApproverUserId. - Invoke ở top Reject branch (cover cả TuChoi + Trả lại sub-branches). - Chặn request forge: non-approver gọi PATCH /transitions direct sẽ 403. Test (test-before §7 — security guard critical algorithm): - ReturnMode tests existing 7/7 vẫn PASS (a2.Id = currentLevel approver, guard accept) - +1 NEW test `Reject_NonApprover_V2_Throws_ForbiddenException` — outsider Drafter role gọi Reject phiếu V2 → throw + Phase không mutate Verify: - dotnet test SolutionErp.slnx — 104/104 PASS (+1 guard regression) Δ: 103 → 104 - npm run build × 2 app — pass (482ms + 583ms) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 036694638e |
[CLAUDE] PE-Workflow: S21 t5 Chunk A — Mig 29 refactor Allow* sang per-NV (per-Level + per-Drafter)
Refactor 6 Allow* options từ workflow-level (Mig 28 S21 t4) sang per-NV scope: - F1 (4 mode Trả lại) + F3 (Edit Section 2) → 5 flag MOVE xuống `ApprovalWorkflowLevels` (per slot Approver, cùng table với ApproverUserId). - F2 (AllowDrafterSkipToFinal) → MOVE xuống `Users` (per-Drafter user, User Mgmt). Mig 29 `RefactorAdvancedOptionsToPerLevelAndDrafterUser` — 4-stage migration (EF auto-generated drop-then-add đã được REORDER manual): 1. ADD 5 column trên `ApprovalWorkflowLevels` (AllowReturnOneLevel/OneStep/ ToAssignee/ToDrafter[default true]/AllowApproverEditDetails) 2. ADD 1 column trên `Users` (AllowDrafterSkipToFinal default false) 3. BACKFILL bulk SQL (preserve admin config Mig 28): - Levels: copy workflow.Allow* → all Levels của workflow (JOIN Steps) - Users: SET TRUE cho user nào từng Drafter PE link workflow Allow=true 4. DROP 6 column workflow-level (Mig 28 cleanup) 3-file rule complete. Apply LocalDB Dev + Design success. Domain entity refactor: - `ApprovalWorkflow.cs` — REMOVE 6 Allow* field (S21 t4 Mig 28 cũ) - `ApprovalWorkflowLevel.cs` — ADD 5 Allow* field (F1 + F3) - `User.cs` — ADD 1 Allow* field (F2 AllowDrafterSkipToFinal) EF config update: - `ApprovalWorkflowConfiguration.cs` — remove 6 HasDefaultValue workflow-level, add 5 HasDefaultValue per-Level (4 false + 1 AllowReturnToDrafter true S17) Service refactor `ApplyReturnModeAsync` (`PurchaseEvaluationWorkflowService.cs`): - Resolve currentLevel slot (CurrentWorkflowStepIndex + CurrentApprovalLevelOrder) - Read 5 Allow* từ `currentLevel.AllowXxx` thay vì workflow.Allow* - Admin bypass per-Level flag check (unchanged behavior) - Drafter mode đặc biệt: check AllowReturnToDrafter của currentLevel (vẫn validate) - V1 legacy (no V2 schema) → fallback Drafter behavior tự động DRAFTER trình refactor (`TransitionAsync` skipToFinal branch): - Permission check moved from workflow-level → `drafterUser.AllowDrafterSkipToFinal` - Use `userManager.FindByIdAsync(actorUserId)` để get current Drafter user entity - Admin bypass user flag check (unchanged) Helper `EnsureEditableForDetailsAsync` refactor: - Read `level.AllowApproverEditDetails` thay vì workflow.AllowApproverEditDetails - Error message rõ "Cấp Approver hiện tại (Bước X / Cấp Y)" thay vì "Workflow" DTO refactor: - `AwLevelDto` ADD 5 Allow* field (admin Designer GET per-Level) - `AwDefinitionDto` REMOVE 6 Allow* (no longer workflow-level) - `CreateAwLevelInput` ADD 5 Allow* param (admin Designer POST per-Level) - `CreateAwDefinitionCommand` REMOVE 6 Allow* (Steps[].Levels[] now has them) - `ApprovalWorkflowOptionsDto` chỉ còn 5 flag (F2 removed — separate field) - `PurchaseEvaluationDetailBundleDto`: - rename `WorkflowOptions` → `CurrentLevelOptions` (clearer semantic per-slot) - ADD `DrafterAllowSkipToFinal bool` (resolve từ DrafterUserId → User entity) GetPurchaseEvaluationQueryHandler populate: - `currentLevelOptions` = 5 Allow* của Cấp hiện tại (null nếu V1 legacy / no pointer) - `drafterAllowSkipToFinal` = User.AllowDrafterSkipToFinal lookup từ DrafterUserId Backward compat verified: - Mig 29 backfill preserve admin config S21 t4 — workflow cũ vẫn chạy đúng sau deploy. User chưa từng làm Drafter F2 phải opt-in lần đầu (no auto-set). - 84 test PASS (58 Domain + 26 Infra unchanged, 3 gotcha #45 guard test backward compat signature). Pending Chunk B/C: FE Admin Designer move 5 checkbox xuống per-Level slot + FE eOffice read currentLevelOptions + drafterAllowSkipToFinal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| c56024ba25 |
[CLAUDE] PE-Workflow: Chunk B — BE Service + handlers + DTOs (F1+F2+F3)
F1 — 4 mode Trả lại (Service.ApplyReturnModeAsync helper):
- WorkflowReturnMode enum (OneLevel / OneStep / Assignee / Drafter)
- OneLevel: lùi 1 Cấp trong cùng Step (peer review). Bước 1 Cấp 1 → fallback Drafter.
- OneStep: lùi sang Bước trước Cấp cuối. Bước 1 → fallback Drafter.
- Assignee: pick runtime → tìm Step+Level match ApproverUserId trong workflow.
- Drafter: Phase=TraLai clear pointer như S17 (backward compat).
- 3 mode đầu giữ Phase=ChoDuyet, reset SLA 7d. Mode Drafter clear SLA.
- Admin bypass workflow.Allow* flag check. Non-admin → throw ConflictException
với message rõ "Workflow không bật mode X".
F2 — Drafter skipToFinal (extend DRAFTER trình branch):
- Workflow.AllowDrafterSkipToFinal=true required (non-admin)
- Set CurrentWorkflowStepIndex = Steps.Count-1 + CurrentApprovalLevelOrder = max Level
- Audit comment append "[Drafter gửi thẳng Cấp cuối]"
F3 — Approver edit Section 2 (Detail + NCC + Báo giá):
- New helper `EnsureEditableForDetailsAsync` (extend pattern PurchaseEvaluationDraftGuard):
- Drafter scope: DangSoanThao OR TraLai (any role, Controller [Authorize] handles)
- F3 Approver scope: ChoDuyet + workflow.AllowApproverEditDetails=true +
actor.Id match CurrentLevel.ApproverUserId. Admin bypass flag check.
- Throw ForbiddenException nếu approver Cấp khác nhau (rõ Bước/Cấp trong message).
- 8 handler switch helper + inject ICurrentUser khi cần:
- Detail: Add (existing ICurrentUser) / Update + Delete (inject new)
- Quote: Upsert + Delete (inject new)
- Supplier: Add (existing) / Update + Delete (inject new + add guard, trước
đây hoàn toàn KHÔNG có phase guard — bonus security fix)
- Audit: thêm changelog Update/Delete handler (trước đây silent). Khi phase=
ChoDuyet append " [Approver edit khi đang duyệt]" cho lịch sử rõ ai sửa.
Extension Service `TransitionAsync` signature (backward compat — 3 optional
param thêm cuối + default null/false):
- WorkflowReturnMode? returnMode = null
- Guid? returnTargetUserId = null
- bool skipToFinal = false
TransitionPurchaseEvaluationCommand DTO + Validator + Handler — mirror signature.
DTO extensions:
- ApprovalWorkflowOptionsDto NEW sub-record (6 Allow* flag) cho FE filter
- PurchaseEvaluationDetailBundleDto + WorkflowOptions field (null nếu V1 legacy)
- GetPe handler populate awOptions từ ApprovalWorkflow entity load (Mig 23 path)
- AwDefinitionDto + 6 Allow* field (admin Designer GET overview)
- CreateAwDefinitionCommand + 6 Allow* param (admin Designer POST new version)
- Handler ToDto + entity new() — propagate Allow* end-to-end
Default backward compat: workflow cũ → AllowReturnToDrafter=true (Mig 28 DB
default), 5 flag còn lại false. Phiếu cũ V2 vẫn Trả lại Drafter như S17 sau
deploy — no breaking change.
Verify:
- dotnet build SolutionErp.slnx → 0 err, 2 warn pre-existing DocxRenderer
- 3 regression test gotcha #45 vẫn PASS (backward compat signature change)
- LocalDB Dev + Design đã apply Mig 28 (Chunk A)
Pending Chunk C: FE Admin Designer mirror 2 app (6 checkbox + DTO types).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 0294693a4a |
[CLAUDE] PE-Workflow: Chunk A — Mig 28 +6 Allow* column ApprovalWorkflow (F1+F2+F3 advanced options)
Domain `ApprovalWorkflow` (Mig 22 — Session 17) thêm 6 boolean cấu hình "Cấu hình nâng cao" cho admin Designer (F1 trả lại modes + F2 skip cấp cuối + F3 approver edit Section 2): - AllowReturnOneLevel (default false) — F1 mode 1 lùi 1 Cấp peer review - AllowReturnOneStep (default false) — F1 mode 2 lùi 1 Bước - AllowReturnToAssignee (default false) — F1 mode 3 pick runtime từ NV đã duyệt - AllowReturnToDrafter (default TRUE) — F1 mode 4 backward compat S17 fallback - AllowDrafterSkipToFinal (default false) — F2 Drafter trình thẳng Cấp cuối - AllowApproverEditDetails (default false) — F3 Approver edit HangMuc/NCC/Báo giá Default backward compat S17: AllowReturnToDrafter=true → mọi workflow cũ chạy đúng "Trả về Drafter" Phase=TraLai. 5 flag còn lại default false → admin opt-in per workflow để audit nghiêm. Mig 28 `AddAdvancedOptionsToApprovalWorkflows`: - AddColumn × 6 bit NOT NULL DEFAULT 0/1 (3-file rule complete + Designer + Snapshot) - Apply LocalDB SolutionErp_Dev (runtime) + SolutionErp_Design (ef tooling) EF config ApprovalWorkflowConfiguration thêm 6 HasDefaultValue match Mig 28 default (backfill rows cũ + ef snapshot consistency). 3 mode Trả lại mới giữ Phase=ChoDuyet, chỉ lùi pointer (peer review chain sequential). Mode Drafter giữ Phase=TraLai + clear pointer như S17. Behavior implement trong Chunk B (Service.TransitionAsync extend branches). Verify: - dotnet ef migrations add success (no compile error) - 3-file rule complete: 28 mig × 2 + Snapshot = 57 file Migrations dir - LocalDB Dev + Design both apply success Pending Chunk B: BE Service branches + handlers + Controller body extend. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| de0088742f |
[CLAUDE] PurchaseEvaluation: Chunk A — BE guard target TraLai/TuChoi BẮT BUỘC decision=Reject + 3 regression test
Defense-in-depth chặn FE inconsistency (gotcha #45 — Session 21 turn 3). Bug pattern: button "← Trả lại" trong PeWorkflowPanel.tsx gửi decision=Approve khi target=TraLai do `isReject` local var thiếu nhánh TraLai → BE skip Reject branch → enter APPROVE STEP → ApproveV2Async UPSERT opinion = "đã duyệt" + advance Cấp. User UAT thấy: "Trả về nhưng hệ thống vẫn duyệt". BE guard: - Service `TransitionAsync` thêm early check sau set isAdmin/isSystem - targetPhase ∈ {TraLai, TuChoi} && decision != Reject → throw ConflictException - Boundary protection cho mọi caller tương lai (API client / mobile / cron) Tests (Infra suite +3): - TransitionAsync_TargetTraLai_WithApproveDecision_Throws_AndDoesNotMutateState - TransitionAsync_TargetTuChoi_WithApproveDecision_Throws_AndDoesNotMutateState - TransitionAsync_TargetTraLai_WithRejectDecision_SetsPhaseTraLai (happy path) + NoOpNotificationService stub reusable cho future PE service tests Verify: - dotnet test SolutionErp.slnx → 84 PASS (58 Domain + 26 Infra = +3 from 81 baseline) - Build pass (0 err, 2 warn CS8602 pre-existing DocxRenderer) Pending Chunk B: FE fix PeWorkflowPanel.tsx isReject + dialog isSendBack mirror 2 app (fe-admin + fe-user) — sync với BE guard rule. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 059bfcbe38 |
[CLAUDE] FE-Admin+Domain: Chunk C — MenuVisibilityPage + menu key + seed
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Session 20 turn 7 Chunk C. FE Admin page quản lý Ẩn/Hiện + Đổi tên menu
cho fe-user (eOffice). Admin sidebar fe-admin LUÔN dùng Tên gốc — page này
KHÔNG đụng admin navigation (user Q2=b).
Domain MenuKeys.cs:
+const MenuVisibility = "MenuVisibility"
All[] thêm MenuVisibility (giữa Permissions + Workflows)
DbInitializer SeedMenuTreeAsync:
+leaf (MenuVisibility, "Menu eOffice", System, 94, "Eye")
Workflows shift Order 94 → 95
Idempotent — chỉ INSERT nếu chưa có trong DB
Manual seed Mig 27 LocalDB Dev: INSERT MenuItems + Permissions cho Admin role
FE Admin:
- types/menu.ts: MenuItem/MenuNode +isVisible bool +displayLabel string|null
- lib/menuKeys.ts: +MenuVisibility const
- components/Layout.tsx resolver +MenuVisibility → /system/menu-visibility
- App.tsx +Route + import MenuVisibilityPage
NEW pages/system/MenuVisibilityPage.tsx (~210 LOC):
- PageHeader + 4 StatCard (Tổng / Hiển thị / Đã ẩn / Đã đổi tên)
- Search input (key | label | displayLabel)
- Table: Key (mono + parentKey ↳) | Tên gốc | Input "Tên hiển thị" inline
(placeholder "Mặc định: ...") | Toggle Hiển thị/Ẩn (emerald/amber) |
Lưu (khi dirty) / Khôi phục (khi đã custom)
- PATCH /menus/{key} body { isVisible, displayLabel } — trim whitespace,
empty string → null
- onSuccess: invalidate ['menus', 'all'] + ['my-menu'] + clear draft entry
- "Khôi phục mặc định" button: PATCH isVisible=true, displayLabel=null
- Footer hint: nhắc admin sidebar luôn dùng Tên gốc, đổi tên áp eOffice
Verify:
- npm run build × fe-admin pass
Pending Chunk D: FE Layout fe-user filter !isVisible + render displayLabel
Pending Chunk E: Docs S20 turn 7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 2ea2d27785 |
[CLAUDE] Infra: Mig 27 — Chunk A MenuItem +IsVisible +DisplayLabel
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Session 20 turn 7: admin có thể Ẩn/Hiện + Đổi tên hiển thị menu cho fe-user (eOffice). Admin sidebar luôn giữ Label gốc (user Q2=b "chỉ của eOffice thôi"). Domain MenuItem: +IsVisible bool=true +DisplayLabel string?(200) EF Configuration: HasDefaultValue(true) + HasMaxLength(200) Migration 27 AddVisibilityAndDisplayLabelToMenuItems — 3-file rule: + AddColumn IsVisible bit NOT NULL DEFAULT 1 + AddColumn DisplayLabel nvarchar(200) NULL Verify: - dotnet build SolutionErp.slnx — 0 warn / 0 err - dotnet ef database update --connection SolutionErp_Dev — applied OK - dotnet ef database update SolutionErp_Design — applied OK Pending: B (BE API) → C (FE admin page) → D (FE user render) → E (Docs) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 90baa8e73c |
[CLAUDE] PurchaseEvaluation: Chunk B Service V2 hook UPSERT opinion + DTO + GET include
Service `ApproveV2Async` sau khi log approval (Decision=Approve) → UPSERT row `PurchaseEvaluationLevelOpinions` cho Cấp hiện tại (auto sync ý kiến từ comment khi duyệt). Reject KHÔNG sync. Match level theo ApproverUserId của actor (multi-NV cùng Cấp OR-of-N). Admin override (actor.Id KHÔNG match) → fallback first level — FE detect SignedByUserId !== Level.ApproverUserId hiển thị "Admin duyệt thay". Empty/whitespace comment → "(duyệt — không ý kiến)" placeholder (Q4 bonus). Helper `ResolveActorFullNameAsync(actorUserId, isSystem, ct)` lookup denorm SignedByFullName từ Users (fallback "(System)" / "(unknown)"). DTO `PurchaseEvaluationLevelOpinionDto` (15 fields): - StepOrder/StepName/StepDepartmentId/StepDepartmentName (Bước Phòng) - LevelOrder/LevelName/ApproverUserId/ApproverFullName (Cấp NV) - Comment/SignedAt/SignedByUserId/SignedByFullName (sign-off) GetPurchaseEvaluationQueryHandler: - Include LevelOpinions - helper BuildLevelOpinionsAsync JOIN ApprovalWorkflows.Steps.Levels + Departments + Users → denorm DTO. Empty list cho phiếu V1 / V2 chưa có cấp nào duyệt → FE fallback message. Verify: dotnet build pass + dotnet test 81 pass (no regression). Chunk C kế tiếp: FE Section 5 dynamic mirror 2 app. |
|||
| 77a30584fc |
[CLAUDE] PurchaseEvaluation: Mig 26 PeLevelOpinions V2 dynamic — Chunk A Domain + EF
Schema mới cho Section 5 "Ý kiến cấp duyệt" V2 dynamic theo ApprovalWorkflowsV2 (Mig 22-25). Thay thế Mig 15 cố định 4 box (V1). Entity `PurchaseEvaluationLevelOpinion : AuditableEntity`: - (PEId, ApprovalWorkflowLevelId) UNIQUE composite - Comment nvarchar(2000) — text ý kiến hoặc "(duyệt — không ý kiến)" placeholder (Q4 bonus) - SignedAt datetime2 (luôn có khi UPSERT từ ApproveV2Async) - SignedByUserId Guid (NV chính chủ HOẶC Admin override) - SignedByFullName nvarchar(200) — denorm tránh user bị xóa/đổi tên EF: FK Cascade Pe + Restrict Level. SignedByUserId KHÔNG nav (denorm only). Migration 26 `AddPeLevelOpinionsForV2`: 1 CREATE TABLE + 2 FK + 2 index. 3-file rule commit đủ (.cs + Designer + Snapshot). Apply LocalDB SolutionErp_Dev OK (Mig 25 + 26 catchup). Verify: dotnet build pass + dotnet test 81 pass (no regression). Chunk B kế tiếp: Service V2 hook UPSERT auto trong ApproveV2Async. |
|||
| 2a53107602 |
[CLAUDE] AwV2: Mig 25 +IsUserSelectable + Designer pin toggle + Workspace filter, bỏ "(clone)"
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Hai yêu cầu UAT 2026-05-08:
1. Bỏ "(clone)" auto-append khi clone version mới — version đã đủ phân biệt.
2. Thêm pin toggle để admin chọn workflows nào cho user pick lúc tạo phiếu.
Migration 25 AddIsUserSelectableToApprovalWorkflows:
- ALTER ApprovalWorkflows ADD IsUserSelectable bit NOT NULL DEFAULT 0
- UPDATE backfill SET IsUserSelectable=1 WHERE IsActive=1 (giữ behavior cũ
cho active versions, archived = false default — admin tự pin nếu cần)
BE:
- Domain ApprovalWorkflow +property IsUserSelectable
- DTO AwDefinitionDto +field
- CreateAwDefinitionCommandHandler set default true cho version mới
- New SetAwUserSelectableCommand + Handler
- API PATCH /api/approval-workflows-v2/{id}/user-selectable (Workflows.Create policy)
- DbInitializer SeedSampleApprovalWorkflowsV2Async set IsUserSelectable=true
FE Designer (fe-admin):
- DefinitionDto +isUserSelectable
- Badge amber "Pin Cho user chọn" khi true (cạnh Đang áp dụng/Archived)
- Button "Pin/PinOff Ghim cho user / Bỏ ghim" trong action group + mutation toggle
- Auto-fill name khi clone: bỏ "(clone)" suffix → giữ nguyên name
FE Workspace (fe-admin + fe-user):
- approvalWorkflows query filter w.isUserSelectable === true
- User dropdown chỉ thấy workflows admin đã pin
Verify: dotnet build pass · 81 test pass · npm build × 2 pass · Mig 25 apply LocalDB OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 937eb2449c |
[CLAUDE] Workflow V2: clone leaf Designer + sample seed cho DuyetNccPhuongAn (B)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m16s
Mở rộng V2 schema cho type B mirror type A đã chốt S17. Phần lớn đã chung qua ApplicableType discriminator — chỉ thêm menu key + sample seed. Changes: - MenuKeys.cs: +const ApprovalWorkflowDuyetNccPhuongAnV2 (AwV2_DuyetNccPhuongAn) + add vào All array - DbInitializer.SeedMenusAsync: +leaf "Duyệt NCC và Giải pháp (Mới)" dưới root ApprovalWorkflowsV2 - DbInitializer +SeedSampleApprovalWorkflowsV2Async: seed QT-DN-PA-V2-001 v01 (1 Bước Phòng CCM × 1 Cấp NV test) Idempotent — skip nếu admin đã tạo bất kỳ workflow B nào hoặc thiếu test user - fe-admin/lib/menuKeys.ts: +AwV2_DuyetNccPhuongAn KHÔNG đụng: - Migration (V2 schema chung qua ApplicableType — Mig 22-24 đã hỗ trợ B) - Service ApproveV2Async (không hardcode type) - Designer page ApprovalWorkflowsV2Page (TYPE_CODE_TO_INT đã có B=2) - Layout/App.tsx (regex AwV2_(.+) match dynamic) - Permission default (admin bypass + role khác không cần Designer access) Verify: dotnet build pass · 81 test pass · npm build fe-admin pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| b41484b702 |
[CLAUDE] PE Workflow: wire Service V2 (Mig 24) — fix bug duyệt phiếu pin schema mới
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m14s
User báo bug eoffice: phiếu tạo mới không duyệt được + không bắt đc quy
trình mới. Root cause: Mig 23 pin ApprovalWorkflowId vào entity nhưng
Service vẫn đọc WorkflowDefinitionId legacy → match approver theo schema
cũ (Dept+PositionLevel/Role/User) thay vì ApproverUserId V2.
BE Domain — Migration 24 `AddCurrentApprovalLevelOrderToPe`:
- PurchaseEvaluation +CurrentApprovalLevelOrder int? (track Cấp 1/2/3
đang chờ duyệt trong Step hiện tại khi pin V2). Null khi terminal/V1.
- RejectedAtStepIndex giữ deprecated DB column cho data cũ.
BE Service PE — branch theo schema pin:
- V2 (`ApprovalWorkflowId` set): ApproveV2Async() — load
ApprovalWorkflows.Steps.Levels Include 3-level. Group Levels by Order
= Cấp (OR-of-N approvers). Match `actor.Id ∈ levelGroup.ApproverUserId`
(KHÔNG match Dept+Level/Role/User như V1). Advance:
Còn cấp tiếp trong Step → levelOrder++
Hết cấp → idx++, levelOrder=1
Hết Step → DaDuyet
- V1 legacy (chỉ `WorkflowDefinitionId` set): ApproveV1LegacyAsync() —
giữ nguyên logic Mig 21 (Dept+PositionLevel match)
- Drafter trình từ Nháp/Trả lại: init CurrentWorkflowStepIndex=0 +
CurrentApprovalLevelOrder=1 (chỉ khi V2 pin)
- Reject (Trả lại): clear CurrentApprovalLevelOrder=null
- Reject (Từ chối): clear all tracking
BE Synthetic Policy V2:
- `PurchaseEvaluationPolicyRegistry.ForV2Schema()` — simple state machine
policy (DangSoanThao/TraLai → ChoDuyet/TuChoi; ChoDuyet → ChoDuyet/
TraLai/TuChoi). Roles="*" cho ChoDuyet branch — Service tự enforce
ApproverUserId, Policy chỉ expose 3 nút FE.
- GetPurchaseEvaluationByIdQuery handler: ưu tiên ForV2Schema() khi pin
V2 (FE đọc workflow.nextPhases để show button).
Verify: 81 test pass · BE 0 error · Mig 24 applied cả 2 LocalDB.
Test thử (Drafter eoffice):
1. Designer V2 tạo quy trình QT-DN-V2-001: Bước 1 (Phòng A), Cấp 1 (NV X)
2. Workspace tạo phiếu mới, Select QT-DN-V2-001 → Lưu phiếu + Gửi duyệt
3. Phiếu Phase=ChoDuyet, idx=0, levelOrder=1. NV X login → thấy phiếu
trong Inbox + duyệt được. Sau approve → idx++, levelOrder reset 1.
4. Cấu hình level mismatch: NV Y khác → thấy ForbiddenException rõ tên.
Logic Contract V2 chưa wire (chỉ PE), defer Session sau khi user UAT PE OK.
|
|||
| 0a40c65421 |
[CLAUDE] PurchaseEvaluation: User chọn quy trình duyệt V2 lúc tạo phiếu (Mig 23)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m11s
User feedback: thay field "Loại quy trình (theo menu — khóa)" disabled
→ Select dropdown cho User pick quy trình ApprovalWorkflowsV2 (Mig 22)
ngay từ workspace tạo mới. Hiển thị "Mã + Tên + Version".
BE Domain:
- PurchaseEvaluation +ApprovalWorkflowId Guid? (nullable, FK Restrict)
- EF Configuration: Index + FK Restrict to ApprovalWorkflows
- Migration 23 `AddApprovalWorkflowIdToPurchaseEvaluation` (1 ALTER +
1 IX + 1 FK), applied cả _Design + _Dev LocalDB
- Field WorkflowDefinitionId (Mig 21 legacy) giữ song song để Service
PE chạy logic cũ tới khi Session sau wire qua schema mới
BE Application:
- CreatePurchaseEvaluationCommand +ApprovalWorkflowId? Guid? optional
param (default null)
- Validate: nếu set, phải tồn tại + ApplicableType khớp PE.Type
(DuyetNcc=1 → ApprovalWorkflowApplicableType.DuyetNcc, etc)
- Handler set entity.ApprovalWorkflowId từ request
- UpdatePurchaseEvaluationDraftCommand mirror — cho User đổi quy trình
khi sửa Nháp/Trả lại (validate same)
- PurchaseEvaluationDetailBundleDto +ApprovalWorkflowId/Code/Name/Version
- GetPurchaseEvaluationByIdQuery handler load workflow info join
- Update Phase guard: cho sửa cả DangSoanThao + TraLai (Trả lại =
editable per Session 17 spec)
FE (cả 2 app mirror):
- types/purchaseEvaluation.ts: PeDetail +approvalWorkflowId/Code/Name/Version
- PeWorkspaceCreateView.tsx:
- Replace field disabled "Loại quy trình" → Select bắt buộc
- useQuery `/api/approval-workflows-v2?applicableType=N` filter theo
defaultType (1=DuyetNcc / 2=DuyetNccPhuongAn)
- Display option: "QT-DN-V2-001 v01 — Quy trình Duyệt NCC (đang áp dụng)"
- List cả version active + archived (UAT cần test compare)
- Empty state hint amber "Chưa có quy trình, vào /system/approval-workflows-v2"
- canSubmit require approvalWorkflowId set
- POST payload include approvalWorkflowId
Verify: dotnet build OK · 81 test pass · npm build × 2 OK · Mig 23 applied
cả 2 LocalDB.
Logic Service PE chưa wire qua ApprovalWorkflowId — vẫn pin
WorkflowDefinitionId Mig 21 legacy chạy. Session sau wire Service iterate
ApprovalWorkflowSteps + match approver theo schema V2 + drop legacy.
|
|||
| ff21120c8c |
[CLAUDE] Workflow: State machine 5 trạng thái — Trả lại = Phase riêng
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m17s
Session 17 spec: chốt 5 trạng thái phiếu PE/HĐ/Budget theo state diagram:
Nháp ─trình──► Đã gửi duyệt ─approve cấp cuối──► Đã duyệt (terminal)
├─ Trả lại ────────► Trả lại
└─ Từ chối ────────► Từ chối (terminal)
Trả lại ──Drafter sửa+gửi lại──► Đã gửi duyệt (chạy LẠI từ đầu)
Khác Mig 21 (Session 16): bỏ smart-reject jump-back. Trả lại = Phase
RIÊNG (TraLai=98), không revert về DangSoanThao + không jump-back step.
Drafter từ TraLai gửi lại như case Nháp — workflow chạy lại từ Cấp 1
Bước 1 (Option A diagram chốt với user).
BE Domain:
- ContractPhase + TraLai = 98
- BudgetPhase + TraLai = 98
- PurchaseEvaluationPhase: TraLai=98 đổi từ [LEGACY deprecated] thành
primary state. Comment update enum docs cho cả 3.
BE Policy (PE/HĐ/Budget):
- Reject transitions trỏ về TraLai (thay DangSoanThao)
- Mirror entry transitions: TraLai → next phase (cho Drafter resubmit)
- ActivePhases thêm TraLai
- FromDefinition mirror: TraLai → step.Phase + reject → TraLai
- DefaultSla cho TraLai = same as DangSoanThao
BE Service (PE + Contract):
- Reject branch: target=TuChoi giữ; else set Phase=TraLai, clear
CurrentWorkflowStepIndex=null
- Bỏ ResumeAfterReject branch + RejectedAtStepIndex/RejectedFromPhase
assignment (DB column giữ deprecated cho data cũ)
- Drafter trình branch: từ DangSoanThao HOẶC TraLai → ChoDuyet, init
CurrentWorkflowStepIndex=0 (cùng entry point, chạy lại từ đầu)
- Notification: TraLai when fromPhase=ChoDuyet → "bị trả lại"
- Budget Handler: simplify reject → TraLai, bỏ smart-reject + isResuming
BE Tests update:
- WorkflowPolicyTests: Standard_RejectFromCCM → TraLai (rename + assert)
+ Standard_TraLai_To_DangGopY_Allowed_For_Drafter (new)
- PurchaseEvaluationPolicyTests: BothPolicies_RejectFromCCM → TraLai
+ BothPolicies_TraLai_To_ChoPurchasing_AllowedForDrafter (new theory)
- BudgetPolicyTests: Default_CostControl_ChoCCM_To_TraLai (rename)
+ ActivePhases All6States (was All5) + NextPhasesFrom_TraLai (new)
+ NextPhasesFrom_ChoCEO_Includes_DaDuyet_And_TraLai (rename)
- 77 → 81 test pass (+4 tests TraLai entry point)
FE rename "Bản nháp" → "Nháp" (cả 2 app + types):
- types/purchaseEvaluation.ts: PurchaseEvaluationPhaseLabel 1=Nháp,
10=Đã gửi duyệt. PeDisplayStatus.BanNhap → Nhap (key + value).
PhaseLabel/Color cho TraLai update active.
- types/contracts.ts: +ChoDuyet=10, +TraLai=98 const + label/color.
Phase 2 'Đang soạn thảo' → 'Nháp'.
- types/budget.ts: +TraLai=98 const + label/color. Phase 1 → 'Nháp'.
- PeListPanel + PurchaseEvaluationsListPage filter dropdown: Nhap +
TraLai map đúng phase value.
BE label maps update consistent:
- ContractExcelExporter PhaseLabel: DangSoanThao → "Nháp" + add ChoDuyet/
TraLai entries.
- PeWorkflowAdminFeatures + WorkflowAdminFeatures PhaseLabels: same.
Verify: dotnet test 81 pass · npm build × 2 app pass · BE 0 error.
Field RejectedAtStepIndex/RejectedFromPhase giữ DB column (nullable,
không set value mới). Cleanup migration sau.
|
|||
| c847dc0b24 |
[CLAUDE] Workflow: Mig 22 schema mới ApprovalWorkflowsV2 (Chunk A)
Session 17 — schema riêng UAT trước khi drop legacy WorkflowDefinition.
Cấu trúc 3 bảng theo yêu cầu user:
Quy trình (Code+Name+ApplicableType)
Bước (Phòng A — DepartmentId hint)
Cấp (NV X — ApproverUserId 1 user cụ thể, KHÔNG OR-of-many)
Khác Mig 21: Levels match 1 NV CHÍNH XÁC qua ApproverUserId, không
match group Dept+PositionLevel/Role/User. Service sau UAT iterate
Steps OrderBy Order → Levels OrderBy Order → ApproverUserId duyệt.
Files:
- Domain/ApprovalWorkflowsV2/ApprovalWorkflow.cs (3 entity + enum
ApplicableType: DuyetNcc/DuyetNccPhuongAn/Contract)
- Infra/Persistence/Configurations/ApprovalWorkflowConfiguration.cs
(FK Cascade Step→Workflow, Level→Step; Restrict Department + User)
- Infra/Persistence/ApplicationDbContext.cs (3 DbSet)
- Infra/Persistence/DbInitializer.cs (2 menu mới: ApprovalWorkflowsV2
root dưới System icon Workflow + AwV2_DuyetNcc leaf icon FileCheck)
- Domain/Identity/MenuKeys.cs (2 const + All array)
- Migration 20260508053749_AddApprovalWorkflowsV2 (3 table CREATE +
2 UNIQUE + 3 index)
Verify:
- Build OK, 77 test pass (54 Domain + 23 Infra) ~3s
- Mig applied cả _Design + _Dev LocalDB
Next chunks:
- B: Application CQRS (Get/Create) + ApprovalWorkflowsV2Controller
- C: FE Designer page /system/approval-workflows-v2/:typeCode
- D: Docs + STATUS update
|
|||
| dbb0089e28 |
[CLAUDE] Drastic refactor: flat workflow Phòng × Cấp + Migration 21 (Chunk A)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m18s
User chốt drastic refactor — bỏ phase enum hoàn toàn, dùng ChoDuyet=10 đơn nhất + currentStepIndex tracking. Workflow flat list (Phòng × Cấp × Approvers). Mỗi PE/HĐ pin WorkflowDefinitionId chạy hết quy trình đó. Schema (Migration 21 `RefactorWorkflowToFlatModel`): - Phase enum +ChoDuyet=10 (PE + Contract). Legacy 2-9 + 98 deprecated. - WorkflowStep + DepartmentId Guid? (FK Restrict) + PositionLevel int? (PE + Contract — mirror). - PE/Contract + CurrentWorkflowStepIndex int? + RejectedAtStepIndex int? - DROP table PurchaseEvaluationWorkflowStepInnerSteps (Mig 18) - DROP table WorkflowStepInnerSteps (Mig 20) - DROP column ContractDeptApproval.InnerStepId (Mig 20) - DROP column PEDeptApproval.InnerStepId (Mig 18) - DROP filtered indexes (Mig 19/20) + restore simple unique (TargetId, Phase, Dept, Stage) non-filtered Service rewrite (PE + Contract WorkflowService.TransitionAsync): - Phase transitions: DangSoanThao → ChoDuyet (Drafter trình, init idx=0) - ChoDuyet → ChoDuyet (advance idx per approve) - ChoDuyet → DaDuyet/DaPhatHanh (idx >= steps.Count → terminal) - ChoDuyet → DangSoanThao (Trả lại — save RejectedAtStepIndex) - ChoDuyet → TuChoi (Từ chối — khoá vĩnh viễn) - DangSoanThao + RejectedAtStepIndex → ChoDuyet jump-back to saved idx - Approver match: actor.Dept == step.Dept AND actor.PositionLevel >= step.PositionLevel (OR-of-many cùng cấp/dept = pass) OR Approvers.Any(Kind=User AND id match) OR Approvers.Any(Kind=Role AND actorRoles contains) - Admin role bypass policy. Last step done → gen mã HĐ (Contract only) App CQRS: - WorkflowStepDto + WorkflowStepInput drop InnerStep, add DepartmentId + PositionLevel fields. PE + Contract mirror. Tests rewrite: - DROP PeNStageApprovalTests.cs (6 test) + ContractNStageApprovalTests.cs (6 test) + PeTwoStageApprovalTests.cs (7 test) — legacy N-stage/2-stage no longer applicable - UPDATE PeWorkflowAdminTests signature to new flat input - 96 → 77 test pass (drop 19 legacy) Reference Domain entities removed: - WorkflowStepInnerStep (Contract) - PurchaseEvaluationWorkflowStepInnerStep (PE) - DTOs WorkflowStepInnerStepDto / CreateWorkflowStepInnerStepInput per module Memory `feedback_drastic_refactor_scope.md` validated: drastic refactor done in dedicated session với context fresh, scope ~5h actual (planned ~8-10h with 2x buffer). Verify: - dotnet build SolutionErp.slnx 0 error - dotnet ef database update Mig 21 LocalDB applied OK - dotnet test 77 pass (54 Domain + 23 Infra) - 3-file rule: Migration .cs + Designer.cs + Snapshot updated Pending Chunk B: FE Designer flat UI (PeWorkflowsPage + WorkflowsPage). Pending Chunk C: FE PeWorkflowPanel + workflow timeline display. Pending Chunk D: Docs + Skill + Memory + session log. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 0d776987e4 |
[CLAUDE] PE workflow 3-button Duyệt/Trả lại/Từ chối (Task 4)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m6s
User chỉ thị thay 2-button hiện tại bằng 3 hành động rõ ràng:
- Duyệt = forward phase tiếp theo
- Trả lại = về DangSoanThao + Drafter sửa → workflow tự jump tới phase
đã reject (smart reject Mig 16 pattern + clear N-stage rows)
- Từ chối = phiếu khoá hoàn toàn (Phase=TuChoi → 17 handler Mig 16 lock
edit). Drafter phải tạo phiếu mới.
Domain (PurchaseEvaluationPolicy.cs):
- NccOnly + NccWithPlan: thêm (X → TuChoi) transition cho mọi phase
trung gian (ChoPurchasing/ChoCCM/ChoCEODuyetNCC/ChoDuAn/ChoCEODuyetPA)
với roles của phase đó. Trước đây chỉ DangSoanThao → TuChoi (Drafter).
- FromDefinition expand: mỗi step (trừ DangSoanThao) thêm
(step.Phase → TuChoi) với roles của step.
Service (PurchaseEvaluationWorkflowService.cs):
- Reject branch tách 2 case:
* target=TuChoi → giữ nguyên (KHÔNG override + KHÔNG set
RejectedFromPhase + KHÔNG clear N-stage rows). Phiếu khoá vĩnh viễn.
* target khác (thường DangSoanThao) → smart reject (set
RejectedFromPhase + force DangSoanThao + clear N-stage rows).
FE (PeWorkflowPanel.tsx, fe-admin + fe-user mirror):
- next.phases render 3 button rõ ràng:
* "✓ Duyệt → <label>" brand (forward)
* "← Trả lại (về Drafter sửa)" red (target=DangSoanThao + isSendBack)
* "✗ Hủy / Từ chối" red (target=TuChoi)
- Decision logic: target=TuChoi || isSendBack → Reject (2), else Approve (1)
- Dialog confirm:
* Title rõ theo loại hành động
* Cancel case: warning red "Phiếu sẽ bị khoá hoàn toàn"
* SendBack case: hint amber "Phiếu sẽ về Đang soạn thảo, Drafter sửa
rồi trình lại — workflow tự jump tới phase này"
Tests update + add 1 test mới:
- Reject_Sets_RejectedFromPhase_And_Forces_DangSoanThao →
Reject_To_DangSoanThao_Sets_RejectedFromPhase_TraLai (rename + change
target từ TuChoi → DangSoanThao để test Trả lại pattern)
- + Reject_To_TuChoi_Locks_Permanently_No_RejectedFromPhase (NEW test
Từ chối — phase=TuChoi + RejectedFromPhase null)
- NStage_Reject_Clears_InnerStep_Rows_At_Phase: target TuChoi →
DangSoanThao (test Trả lại + clear N-stage rows pattern)
Verify:
- dotnet build 0 error
- dotnet test 95 → **96 pass** (+1 test mới Từ chối)
- npm build fe-admin + fe-user pass
Pending Task 2: Sample data seed N-stage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| e247b67681 |
[CLAUDE] Infra: ContractWorkflowService N-stage logic mirror PE (Chunk C)
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Refactor TransitionAsync mirror PurchaseEvaluationWorkflowService Mig 18
N-stage pattern:
- Reject branch: clear N-stage approval rows tại fromPhase (resume sẽ
approve lại từ inner đầu)
- Load definition with InnerSteps eager + assign outer scope
- Department approval block split:
* hasInnerSteps=true → N-stage logic:
- Yêu cầu actor có DeptId + PositionLevel set (else throw 403)
- Match firstPending (Order asc IsRequired) same dept + (exact level
OR canBypass + level≥)
- exact match: upsert 1 row InnerStepId, IsBypassed=false
- bypass: batch upsert NV+PP+TP cùng dept ≤ actor (audit IsBypassed
cho cấp dưới)
- Recheck stillPending → BLOCK + log Approval/Changelog "duyệt cấp X
(còn Y pending)"
- All done → fall through phase transition
* hasInnerSteps=false → Legacy 2-stage Mig 16 (giữ nguyên với
InnerStepId=null filter)
Backward compat 100%: workflow Contract no InnerSteps configured →
service fallback legacy 2-stage NV.Review/TPB.Confirm. Tests 89 pass —
no regression.
Verify: dotnet build 0 error, dotnet test 89 pass.
Pending Chunk D: ContractNStageApprovalTests 6 test mirror PE pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 951ffa3ed8 |
[CLAUDE] Domain+Infra: Migration 20 Contract workflow inner steps mirror PE (Chunk A)
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Mirror PE N-stage Mig 18+19 pattern sang Contract module. Gộp 1 migration (CREATE TABLE + ALTER + filtered unique alter) thay vì tách 2 như PE. Schema: - entity WorkflowStepInnerStep (Domain/Contracts/) — Order, DeptId, PositionLevel, Name, SlaDays, IsRequired - WorkflowStep nav +InnerSteps List - ContractDepartmentApproval +InnerStepId Guid? FK Restrict - CREATE TABLE WorkflowStepInnerSteps (no Contract prefix vì module dùng entity gốc Workflow* từ Mig 8) - DROP UX_ContractDeptApprovals_Contract_Phase_Dept_Stage cũ - RECREATE filtered: WHERE InnerStepId IS NULL (legacy 2-stage Mig 16) + new filtered UNIQUE (ContractId, Phase, InnerStepId) WHERE InnerStepId IS NOT NULL (N-stage) - 3 INDEX: IX_InnerStepId, IX_(StepId, Order), IX_DeptId Backward compat 100%: workflow Contract no InnerSteps configured → service fallback legacy 2-stage. Data legacy InnerStepId=null vẫn enforce unique cũ qua filtered index. Note: Budget defer (chưa có versioned WorkflowDefinition entity — hardcoded BudgetPolicy.Default). Cần migration AddBudgetVersionedWorkflow trước khi mirror N-stage Budget. Verify: - dotnet build SolutionErp.slnx 0 error - dotnet ef database update LocalDB applied OK - dotnet test 89 pass (54 + 35) — no regression Pending Chunk B: WorkflowAdminFeatures.cs DTO/Input/Validator/Handler extend mirror PE Chunk B pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 0c62e241d0 |
[CLAUDE] Infra: PE workflow service N-stage logic + Migration 19 unique filter (Chunk C)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m2s
Migration 19 `AlterPeDeptApprovalsUniqueFilteredForInnerSteps` — fix
UNIQUE constraint conflict khi N-stage có nhiều inner step cùng dept:
- Drop UX_PEDeptApprovals_PE_Phase_Dept_Stage (Mig 16)
- Recreate filtered: UNIQUE (PEId, Phase, Dept, Stage) WHERE InnerStepId IS NULL
(legacy 2-stage rows giữ nguyên invariant)
- New filtered: UNIQUE (PEId, Phase, InnerStepId) WHERE InnerStepId IS NOT NULL
(N-stage 1 row per inner step per phase)
PurchaseEvaluationWorkflowService.TransitionAsync refactor:
- Load definition with InnerSteps eager (.Include ThenInclude)
- Reject branch: clear N-stage approval rows tại fromPhase (resume sẽ
approve lại từ inner step đầu — clean state)
- Department approval block split:
* hasInnerSteps=true → N-stage logic:
- Yêu cầu actor có DepartmentId + PositionLevel set (else throw 403)
- Match firstPending inner step (Order asc, IsRequired only):
same dept AND (exact PositionLevel OR canBypass + level ≥ pending)
- exact match: upsert 1 row (Stage=Confirm, InnerStepId=that)
- bypass: batch upsert NV+PP+TP cùng dept ≤ actor level (audit
IsBypassed=true cho cấp dưới skip)
- Recheck stillPending → BLOCK transition + log Approval/Changelog
"đã duyệt cấp X (còn Y cấp pending)"
- All done → fall through phase transition
* hasInnerSteps=false → Legacy 2-stage (Mig 16) — giữ nguyên logic
NV.Review/TPB.Confirm + InnerStepId=null filter
Backward compat: workflow cũ (no InnerSteps configured) chạy đúng logic
2-stage Mig 16. Data legacy InnerStepId=null vẫn match unique cũ qua
filtered index. Tests 83 pass — no regression.
Verify:
- dotnet build SolutionErp.slnx 0 error
- dotnet ef database update LocalDB applied Mig 19
- dotnet test 83 pass
Pending Chunk D: Tests N-stage workflow (~6-7 test mới: sequential pass /
bypass cùng dept / reject reset / resume jump-back / legacy fallback).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 13ab533fe7 |
[CLAUDE] Domain+Infra: Migration 18 PE workflow inner steps + User.PositionLevel (Chunk A)
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
N-stage workflow approval — mỗi WorkflowStep cha (= 1 phase) cấu hình
được chuỗi InnerSteps con theo Department × PositionLevel với Order
sequential. Phase 9+ feature, mở rộng từ 2-stage Mig 16.
Schema:
- enum PositionLevel { NhanVien=1, PhoPhong=2, TruongPhong=3 } (Domain/Identity)
- ALTER Users + PositionLevel int? NULL (admin/system user vẫn null)
- CREATE TABLE PurchaseEvaluationWorkflowStepInnerSteps:
Id PK, PurchaseEvaluationWorkflowStepId FK Cascade,
Order int, DepartmentId FK Restrict, PositionLevel int,
Name nvarchar(200), SlaDays int?, IsRequired bit
- ALTER PurchaseEvaluationDepartmentApprovals + InnerStepId Guid? FK Restrict
(null cho data legacy 2-stage Review/Confirm Mig 16)
Backward compat: step KHÔNG có InnerSteps → service fallback logic
2-stage Stage=Review|Confirm cũ (Chunk C). Data Mig 16 hiện có giữ
nguyên, InnerStepId=null.
Verify:
- dotnet build SolutionErp.slnx pass (0 error, 2 pre-existing warning DocxRenderer)
- dotnet ef database update LocalDB applied OK
- dotnet test SolutionErp.slnx 83 pass (54 Domain + 29 Infra) — no regression
- 3-file rule: Migration.cs + Designer.cs + Snapshot updated
Pending Chunk B: Application CQRS — extend CreatePeWorkflowDefinitionCommand
với InnerSteps DTO + UpdateUserPositionLevelCommand.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| ecd5f7e9d9 |
[CLAUDE] Domain+Infra: Migration 17 — manual budget fields cho PE + HĐ
Chunk 1/5 — DB schema + Domain layer cho fallback "user nhập số tiền ngân sách
tay khi chưa link Budget entity" (UAT request 2026-05-07). Áp cho cả PE + HĐ
(mirror logic per user Q3 chốt).
Migration 17 `AddManualBudgetFieldsToPeAndContract` — 4 ALTER:
- PurchaseEvaluations.BudgetManualName nvarchar(200) NULL
- PurchaseEvaluations.BudgetManualAmount decimal(18,2) NULL
- Contracts.BudgetManualName nvarchar(200) NULL
- Contracts.BudgetManualAmount decimal(18,2) NULL
Validation Q2: cả 2 cùng null OK (PE/HĐ chưa có ngân sách gì cả). KHÔNG XOR
với BudgetId — tạm thời cho phép cả 2 cùng có (BE prefer BudgetId nếu set vì
có Phase=DaDuyet guarantee, manual chỉ là fallback hiển thị/note).
Files:
~ Domain/PurchaseEvaluations/PurchaseEvaluation.cs — 2 property mới
~ Domain/Contracts/Contract.cs — 2 property mới
~ Infrastructure/Persistence/Configurations/PurchaseEvaluationConfiguration.cs
— HasMaxLength(200) + HasPrecision(18,2)
~ Infrastructure/Persistence/Configurations/ContractConfiguration.cs — same
+ Migration 17 .cs + .Designer.cs (3-file rule per ef-core-migration skill)
~ ApplicationDbContextModelSnapshot.cs (auto-overwrite)
Verify:
- dotnet ef migrations add → 3 file gen sạch (4 AddColumn Up + 4 DropColumn Down)
- dotnet ef database update → applied LocalDB OK
- dotnet test SolutionErp.slnx → 83 pass (54 Domain + 29 Infra) — không regression
Next: Chunk 2 App CQRS Create/Update commands + DTO + AutoMapper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| b6f5a16420 |
[CLAUDE] Infra+App+Api+FE: Chunk E4 — HĐ 2-stage dept approval (mirror PE)
Mở rộng 2-stage logic từ PE sang Contract workflow (Migration 16 đã có schema):
BE Service:
- ContractWorkflowService thêm UserManager<User> DI
- Mirror logic 2-stage từ PurchaseEvaluationWorkflowService.TransitionAsync
Sau policy guard, trước gen mã HĐ:
- User.DepartmentId != null + actor không admin/system + KHÔNG resume
- DeptManager (TPB) → Stage=Confirm trực tiếp
- CanBypassReview=true → Stage=Confirm + IsBypassed=true
- Else (NV) → Stage=Review only, BLOCK transition
- Insert ContractDepartmentApproval row (UPSERT theo UNIQUE)
- Block transition khi chưa có Stage=Confirm:
- Insert ContractApproval (FromPhase=ToPhase=fromPhase, [Review NV] comment)
- Insert ContractChangelog "đã review, chờ TPB confirm"
- Notify TPB cùng dept (UserManager filter DeptManager role)
- Return early — phase KHÔNG đổi
App + Api:
- ContractDepartmentApprovalFeatures.cs (List query mirror PE)
- ContractsController endpoint GET /contracts/{id}/department-approvals
FE (cả fe-admin + fe-user):
- types/contracts.ts thêm ApprovalStage const + ContractDepartmentApproval type
- WorkflowHistoryPanel section "Tiến trình duyệt 2-cấp phòng ban":
- Group by phase × dept, show Review NV + Confirm TPB
- Highlight amber "chờ TPB confirm" khi current phase có Review chưa Confirm
- Badge fuchsia "bypass" khi NV.CanBypassReview=true
- Insert giữa WorkflowSummaryCard và Lịch sử duyệt
- Mirror cả 2 app (rule §3.9)
Use case mirror PE: HĐ ở phase DangGopY (P.CCM) — nv.cao (NV) duyệt thì
phase KHÔNG đổi (Review only), chờ ccm.tran (TPB) confirm mới sang DangXetDuyet.
Build: BE pass + FE pass cả 2 + 77 test pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 3c4931687a |
[CLAUDE] App+Api+Docs: Chunk E1 — List endpoint + Bypass-review + Notify TPB + chốt session 8
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m15s
3 endpoint mới + Notify TPB + Docs update để chốt session 8.
Application:
- PurchaseEvaluationDepartmentApprovalFeatures.cs (NEW):
* ListPeDepartmentApprovalsQuery + DTO PeDepartmentApprovalDto
* Join Departments (lấy Name) + lookup Users.FullName denorm cho FE timeline
- UserFeatures.cs: SetUserBypassReviewCommand + Handler dùng UserManager.UpdateAsync
- IApplicationDbContext: thêm DbSet<User> Users + DbSet<Role> Roles (cần cho lookup)
Api:
- PurchaseEvaluationsController: GET /api/purchase-evaluations/{id}/department-approvals
- UsersController: PATCH /api/users/{id}/bypass-review (Authorize Users.Update)
Infra:
- PurchaseEvaluationWorkflowService: notify TPB cùng dept khi NV review.
Query db.Users.Where(DeptId match + IsActive) → UserManager.GetRolesAsync
filter DeptManager → notifications.NotifyAsync. Best effort fail non-critical.
Docs:
- STATUS.md: Recently Done thêm row session 8 + Phase header update
count 52→55 tables, 15→16 migrations, 128→131 endpoints
- HANDOFF.md: TL;DR session 8 + 8 cảnh báo session 9 (FE chưa làm,
test flow anh Kiệt, smart reject test, lock edit test, ...)
- migration-todos.md: Phase 9 done section đầy đủ 3 ràng buộc + pending Chunk E-bis
- CLAUDE.md: count 52→55 + migration 16 description
- session log: 2026-05-04-1230-chot-session-8-2-stage-dept-approval.md (full report)
Verify final:
- Build pass 0 warning 0 error
- 77 unit test pass (54 Domain + 23 Infra)
- Migration 16 applied LocalDB OK + schema verified
Total session 8 cumulative: 5 commit per-chunk:
- 5fe61cc (A: Migration 16 schema)
-
|
|||
| a532ba6fc3 |
[CLAUDE] Infra: Chunk D — PE 2-stage dept approval (đóng bug anh Kiệt báo)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m22s
Ràng buộc 3 (Phase 9) scope tối giản: chỉ PE workflow trước. Đóng bug
"NV duyệt được hết phase" anh Kiệt báo trong chat FDC.
Logic 2-stage trong PurchaseEvaluationWorkflowService.TransitionAsync,
chèn sau policy guard, trước phase transition:
1. Detect approving phase với role thuộc phòng ban:
- decision == Approve
- target != DangSoanThao && != TuChoi
- Không reject + không resume + không admin/system
- actorUserId != null + actor.DepartmentId != null
2. Stage detection:
- DeptManager (TPB) → Stage=Confirm trực tiếp (TPB tự confirm được)
- User.CanBypassReview=true → Stage=Confirm + IsBypassed=true (NV bypass)
- Else (NV thường) → Stage=Review only
3. Upsert PurchaseEvaluationDepartmentApproval row:
- UNIQUE (PEId, PhaseAtApproval, DepartmentId, Stage) đảm bảo 1 row
- UPDATE in-place khi user click Duyệt lần 2 (đổi comment)
- ApproverRoleSnapshot: "TPB" / "NV(bypass)" / "NV" denorm cho audit
4. Check Stage=Confirm tồn tại cho (PEId, fromPhase, deptId):
- hasConfirm = vừa insert Stage=Confirm OR đã có sẵn
- !hasConfirm → BLOCK phase transition:
* Insert PEApproval row (FromPhase=ToPhase=fromPhase, Decision=Approve,
Comment="[Review NV] ...") để track audit
* Insert Changelog "NV X đã review phase Y, chờ TPB confirm"
* Return early — Phase KHÔNG đổi
- hasConfirm → tiếp tục normal phase transition logic
5. Skip 2-stage hoàn toàn khi:
- Decision=Reject (smart reject Chunk C đã handle)
- Resume after reject (target đã pinned)
- Admin role hoặc System (auto-approve)
- actorUserId == null hoặc actor.DepartmentId == null
Bug fix verified theo flow anh Kiệt:
- User long.chau (NV.PRO, role=Procurement, DepartmentId=PRO) tạo phiếu
- long.chau click Duyệt phase ChoPurchasing → ChoCCM:
- actor.DepartmentId=PRO → 2-stage logic active
- role=Procurement, không có DeptManager → Stage=Review
- hasConfirm=false → BLOCK transition
- Insert PEDeptApproval(PE, ChoPurchasing, PRO, Review)
- Phase giữ nguyên ChoPurchasing
- TPB.PRO (tra.bui có role DeptManager + DeptId=PRO) click Duyệt:
- role=DeptManager → Stage=Confirm
- hasConfirm=true (vừa insert) → ALLOW transition
- Phase chuyển ChoPurchasing → ChoCCM
- NV CCM lặp pattern tương tự ở phase ChoCCM
- Cuối cùng CEO/AuthSigner duyệt ChoCEODuyetNCC → DaDuyet (CEO không thuộc
dept cụ thể nên bypass 2-stage)
Pending Chunk E:
- TODO notify TPB cùng dept khi NV review (best effort, chưa implement)
- List endpoint GET /api/purchase-evaluations/{id}/department-approvals
cho FE hiển thị progress 2-stage
- UserManager API PATCH /api/users/{id}/bypass-review
- FE Workflow Panel update + UserManager toggle
HĐ + Budget 2-stage scope sẽ làm sau khi PE verify UAT (per default chốt
trước đó).
Verify:
- Build pass (2 warning DocxRenderer cũ)
- 77 unit test pass — Domain policy chưa đụng
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 9747f8cbf5 |
[CLAUDE] Infra+App: Chunk C — Smart reject + Resume after reject (3 module)
Ràng buộc 2 (Phase 9): khi reject, trả về Drafter (DangSoanThao) + lưu phase nguồn. Drafter sửa lại + trình lại → quay về phase đã reject (skip phase trung gian). Logic flow: 1. Reject (Decision=Reject): - entity.RejectedFromPhase = currentPhase // snapshot phase đang reject - targetPhase override = DangSoanThao // force về Drafter - Approval row: FromPhase=X, ToPhase=DangSoanThao, Decision=Reject - Notification cho Drafter 2. Resume after reject (Decision=Approve, fromPhase=DangSoanThao, RejectedFromPhase != null): - targetPhase override = entity.RejectedFromPhase!.Value - entity.RejectedFromPhase = null // clear field - Skip policy guard (Drafter có quyền trình lại sau khi sửa) - Approval row: FromPhase=DangSoanThao, ToPhase=ResumePhase, Decision=Approve 3. Normal transition (chưa reject hoặc đã clear): - Logic cũ giữ nguyên — policy guard check + transition Pattern unified cho 3 module: - ContractWorkflowService.TransitionAsync: 2 case detect + override - PurchaseEvaluationWorkflowService.TransitionAsync: tương tự - TransitionBudgetCommandHandler.Handle: tương tự (Budget không có service riêng, logic ở handler) Files: - src/Backend/SolutionErp.Infrastructure/Services/ContractWorkflowService.cs - src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs - src/Backend/SolutionErp.Application/Budgets/BudgetFeatures.cs Verify: - Build pass (2 warning DocxRenderer cũ, không liên quan) - 77 unit test pass — Domain policy không đổi, tests giữ nguyên Note: Approval history giờ track đầy đủ cycle reject→sửa→resume: Approval 1: DangGopY → DangSoanThao, Decision=Reject (CCM reject) Approval 2: DangSoanThao → DangGopY, Decision=Approve (Drafter resume) UI có thể detect "đã từng reject" qua RejectedFromPhase != null hoặc qua Approval history (Decision=Reject row gần nhất). Hiển thị banner đỏ "Phiếu đã bị reject từ phase X, lý do: Y" cho Drafter. Smart reject hoàn tất Ràng buộc 2. Còn Ràng buộc 3 (2-stage dept approval) ở Chunk D. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 5c200978cb |
[CLAUDE] Domain+Infra: Migration 16 — 2-stage dept approval + smart reject schema
Chunk A của feature 2-stage department approval (Phase 9). 3 ràng buộc gộp 1 migration để rollback atomic. Schema changes: 1. Smart reject (3 ALTER): - Contracts.RejectedFromPhase int NULL - PurchaseEvaluations.RejectedFromPhase int NULL - Budgets.RejectedFromPhase int NULL Lưu phase nguồn khi reject để Drafter trình lại quay về đúng phase (skip phase trung gian đã duyệt) thay vì đi tuần tự từ DangSoanThao. 2. Bypass per-user (1 ALTER): - Users.CanBypassReview bit NOT NULL DEFAULT 0 Khi true → NV được duyệt thay TPB (skip Stage Review, đẩy thẳng Stage Confirm). Audit qua DepartmentApproval.IsBypassed=true. 3. 2-stage dept approval (3 CREATE TABLE): - ContractDepartmentApprovals - PurchaseEvaluationDepartmentApprovals - BudgetDepartmentApprovals Schema (chung): - Id, *Id FK Cascade, PhaseAtApproval int, DepartmentId FK - Stage enum (1=Review NV, 2=Confirm TPB) - ApproverUserId, ApproverRoleSnapshot, Comment, ApprovedAt - IsBypassed bit (mark NV bypass) - AuditableEntity (CreatedAt/By/UpdatedAt/By/IsDeleted/...) Indexes: - UNIQUE (TargetId, PhaseAtApproval, DepartmentId, Stage) - Single: TargetId, DepartmentId, ApproverUserId Files: - Domain: 4 entity update (Contract/PE/Budget/User add field) + 1 enum mới ApprovalStage + 3 entity DepartmentApproval mới - Infrastructure: 3 EntityConfiguration update + 1 file mới DepartmentApprovalsConfiguration với 3 config classes - IApplicationDbContext: thêm 3 DbSet - ApplicationDbContext: thêm 3 DbSet - Migration 16: 3 file (.cs + Designer.cs + Snapshot.cs) — 3-file rule Verify: - Build pass (2 warning DocxRenderer cũ, không liên quan) - 77 unit test pass (54 Domain + 23 Infra) — Domain policy chưa update, test pass nguyên không regression Note: KHÔNG khác PurchaseEvaluationDepartmentOpinion (Migration 15) — Opinion là sign-off block "Ý kiến 4 phòng ban" trên header phiếu. DepartmentApproval mới là 2-stage approval workflow per phase. Tổng sau Migration 16: 55 bảng (52+3), 16 migration. Chunk B-E sẽ implement Application + FE + Tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |