Files
solution-erp/.claude/workflows/runs/2026-07-31-S164-4gd-khkk-fanout/sub-implbe-k1.md
2026-07-31 14:41:04 +07:00

10 KiB
Raw Blame History

impl-be K1

Tiến độ

  • B1 Đọc spec 4 nguồn: sub-invest-fable-b2-cum1.md §K1 (:9-75) · spec-cum1-chi-tiet-31-07-2026.md §② vá-1 (:8) · danh-muc-sp002-transcribe.md (86 row) · spec-4gd-khkk-tong-quat §③ K1 DoD (:24)
  • B2 Soi khuôn: Domain/Master/Catalogs/WorkItem.cs (entity) · Configurations/CatalogsConfiguration.cs (filtered-unique #57) · Api/Controllers/CatalogsController.cs:12-14 (authz mẫu) · Application/Master/Catalogs/CatalogsFeatures.cs (CQRS mẫu) · DbInitializer.cs:2901 (seed khuôn) / :1766-1769 (menu leaf) / :2058 (grant Admin loop)
  • B3 Entity ContractCatalogEntry + Config + DbSet ×2 (ApplicationDbContext + IApplicationDbContext)
  • B4 Mig 70
  • B5 Seeder 86 row
  • B6 MenuKeys + MenuItem row
  • B7 CQRS + controller
  • B8 build + test

Lệch spec (nếu có)

  1. MenuKeys VÀO All (lead) vs NGOÀI All (invest-spec K1.d:57) → theo lead, khai drift bên dưới.
  2. Controller riêng ContractCatalogController (lead) vs extend CatalogsController (invest-spec K1.c:51) → theo lead về FILE, giữ PATH api/catalogs/contract-catalog của spec để FE khỏi đổi.
  3. TenVi B2-07..B2-14 bỏ chú-thích (ảnh: …) — chú-thích của người chép, không phải tên hạng mục; chuyển thành comment cuối dòng. Row vẫn đủ 14.
  4. Test K1 k=5 (invest-spec K1.e) KHÔNG viết — lead chỉ định "KHÔNG viết test mới, chỉ KHÔNG VỠ"; test là việc test-specialist.

Kết quả build/test

  • dotnet build SolutionErp.slnx --nologo -v minimalBuild succeeded · 0 Warning(s) · 0 Error(s) · Time Elapsed 00:00:15.33
  • dotnet test SolutionErp.slnx --nologo -v minimal
    • Passed! - Failed: 0, Passed: 45, Skipped: 0, Total: 45 (Domain, 332 ms)
    • Passed! - Failed: 0, Passed: 545, Skipped: 0, Total: 545 (Infrastructure, 1 m 25 s)
    • Tổng 590 PASS / 0 FAIL = ĐÚNG baseline 590 (STATUS.md:472) → 0 test vỡ, 0 test mới (đúng lệnh).

FILE tạo/sửa (PHẦN 2 — lượt này)

MỚI (2):

  • src/Backend/SolutionErp.Application/Master/Catalogs/ContractCatalogFeatures.cs — 5 handler + 2 validator + DTO
  • src/Backend/SolutionErp.Api/Controllers/ContractCatalogController.cs — 5 endpoint, [Authorize] + 3× [Authorize(Roles="Admin")]

SỬA (2):

  • src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs +186 dòngSeedContractCatalogAsync (86 row) + call-site InitializeAsync + 1 menu row leaf
  • src/Backend/SolutionErp.Domain/Identity/MenuKeys.cs +5 dòng — const ContractCatalog + vào All

CÓ SẴN từ lượt 1 (KHÔNG đụng, liệt để lead commit đủ): Domain/Master/Catalogs/ContractCatalogEntry.cs · Persistence/Configurations/ContractCatalogEntryConfiguration.cs · Mig 3-file 20260731072441_AddContractCatalogEntries.cs + .Designer.cs + ApplicationDbContextModelSnapshot.cs · IApplicationDbContext.cs (+2) · ApplicationDbContext.cs (+2). → Tổng scope K1-BE = 10 file (4 lượt-này + 6 lượt-1). git status -- src/Backend xác nhận 0 file FE, 0 file tests/ bị chạm.

Drift DELTA (số canonical docs/STATUS.md — em main vá doc, tôi KHÔNG đụng)

Row Trước Sau Cách đo
Menu keys 55 56 đếm TỪ MÃ MenuKeys.All (sed vùng array → tách , → đếm identifier) = 56
Policies 220 224 DERIVED |All| × |Actions| = 56 × 4
Mig 69 70 20260731072441_AddContractCatalogEntries (lượt 1)
Bảng 96 97 +1 ContractCatalogEntries (lượt 1)
Tests 590 590 không đổi (0 test mới, 0 vỡ)

🔴 Bàn giao / còn hở (lead quyết)

  1. Mig 70 CHƯA áp Dev-DBsqlcmd (localdb)\MSSQLLocalDB -d SolutionErp_Dev → top __EFMigrationsHistory = 20260729122015_AddContractSigningPlans (Mig 69). Tôi KHÔNG tự áp (ngoài 7 bước lệnh + lệnh cấm sửa schema). Tự lành khi chạy API: DbInitializer:64 MigrateAsync() áp Mig 70 rồi seed 86 row ngay cùng lần boot. Hoặc tay: dotnet ef database update --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api (+ --connection "…Database=SolutionErp_Dev…" cho Dev).
  2. Chưa chạy verify RUNTIME (restart API ×2 → COUNT=86 idempotent, K1.f:70-71) vì chưa áp DB — checklist đó còn TRỐNG, đừng tick.
  3. Leaf menu hiện chỉ Admin thấy — 3 seeder grant khác dùng list-key-cứng (chi tiết P2-B2).
  4. [OG-6] chưa gật — data 86 row đã land trong code; nếu owner soát ra sai ô nào thì sửa tuple + restart (per-Code idempotent nên row đã seed KHÔNG tự cập nhật, cần UPDATE tay hoặc thêm nhánh upgrade-if-exists).
  5. Test K1 k=5 để test-specialist.

END impl-be-k1 — 4 file lượt này (10 file scope K1-BE) · build PASS 0W/0E · test 590 (45D+545I) 0 fail

PHẦN 2 (relaunch)

Lượt 1 chết sau schema-layer. Schema ĐÃ TRÊN ĐĨA (verify @relaunch):

  • src/Backend/SolutionErp.Domain/Master/Catalogs/ContractCatalogEntry.cs (10 cột, AuditableEntity)
  • Mig 20260731072441_AddContractCatalogEntries (3-file) — KHÔNG tạo lại, KHÔNG sửa.

Việc PHẦN 2: seeder 86-row · MenuKeys + MenuItem · CQRS/controller · build · test.

P2-B0 Đọc nguồn (xong)

  • sub-invest-fable-b2-cum1.md §K1.b/.c (:33-51) — seed UNGATED + IgnoreQueryFilters + mapping field; CQRS mega-file + extend CatalogsController
  • danh-muc-sp002-transcribe.md TRỌN — 86 row (A1 5 · A2 1 · A3 9 · A4 5 · B1 23 · B2 14 · B3 15 · C1 6 · C2 3 · C3 4 · CEO 1 = 86); bảng 8 nhóm (SignerRole + InitialsChain)
  • DbInitializer.cs:2889-2986 khuôn tuple-array + per-Code idempotent
  • MenuKeys.cs:164-186 vùng All (5 leaf catalog :168)

⚠️ LỆCH SPEC đã khai (lead override, làm theo lead)

  • invest-spec §K1.d (:57) ghi MenuKeys.ContractCatalog NGOÀI All (0 policy mới).
  • LỆNH lead PHẦN 2 mục 2 ghi VÀO All + drift DELTA (+1 key / +4 policy). → Làm theo LỆNH lead (mới hơn + có khai drift). Hệ quả: docs/STATUS.md row Menu keys +1 và row Policies +4 (DERIVED |All|×|Actions|) — em main cập nhật STATUS, tôi KHÔNG đụng doc.

P2-B1 Seeder 86 row — XONG

  • DbInitializer.cs +SeedContractCatalogAsync (đặt ngay TRƯỚC SeedContractTemplatesAsync) + call-site trong InitializeAsync ngay SAU SeedRealMasterDataAsync (khớp K1.b "cạnh SeedRealMasterDataAsync, trước sample workflows").
  • UNGATED (ngoài if (!demoSeedDisabled)) — gotcha #51.
  • Idempotent per-Code + IgnoreQueryFilters() khi dò existing → né #75/#76 resurrect (làm ĐÚNG HƠN khuôn :2970, ghi comment lý do trong code).
  • NduqNote = null [OG-3] · IsActive = true · SortOrder = STT trong section.
  • SignerRole/InitialsChain: lookup signByGroup 8 nhóm → ghi denorm vào TỪNG row (đủ 8 field/row như lệnh; không chép tay chuỗi 86 lần để khỏi lệch ký tự). Ô "N/A" đã bỏ đúng spec (N3 còn 3 ô, N4/N8 còn 2 ô, N7 còn 3 ô).
  • ĐO tại chỗ (grep count trên file vừa ghi): tổng 86 row; theo section A1=5 A2=1 A3=9 A4=5 B1=23 B2=14 B3=15 C1=6 C2=3 C3=4 CEO=1; theo ApprovalGroup = 5/10/5/23/14/15/13/1 → khớp ĐÚNG K1.b:41.

P2-B2 MenuKeys + MenuItem — XONG

  • MenuKeys.cs +const ContractCatalog = "ContractCatalog" (cạnh 4 const catalog, :28-32) + VÀO All ngay sau dòng 5 leaf catalog.
  • DbInitializer.cs SeedMenuTreeAsync +row (MenuKeys.ContractCatalog, "Danh mục Hợp đồng", MenuKeys.Catalogs, 5, "FileSpreadsheet") — leaf thứ 5 dưới "Danh mục chi tiết"; icon FileSpreadsheet đã dùng sẵn (MenuKeys.Forms :1788) nên FE có map.
  • Grant Admin: TỰ ĐỘNG qua loop SeedAdminPermissionsAsync MenuKeys.All (:2058) — 0 code thêm.
  • ⚠️ Flag cho lead (KHÔNG tự làm — ngoài lệnh): 3 seeder grant khác dùng DANH SÁCH KEY CỨNG chứ không prefix-match → key mới KHÔNG tự vào: SeedCatalogManagerPermissionsAsync (:2626-2629), SeedAllRolesReviewReadPermissionsAsync (:2181-2182), SeedProcurementMasterAccessAsync (:2360-2363). ⇒ hiện chỉ Admin thấy leaf "Danh mục Hợp đồng"; muốn PRO/CatalogManager thấy thì thêm key vào 3 list đó (mỗi chỗ 1 dòng).

P2-B3+B4 CQRS + Controller — XONG

  • MỚI SolutionErp.Application/Master/Catalogs/ContractCatalogFeatures.cs: ListContractCatalogQuery(GroupCode?, ApprovalGroup?, IsActive?, Q?) (sort GroupCode→SortOrder→Code, KHÔNG paging) · GetContractCatalogEntryQuery · Create/Update/Delete + 2 validator.
  • Validator MaxLength lấy từ EF config ContractCatalogEntryConfiguration.cs:18-24 (Code 20 · TenVi/TenEn/NduqNote 500 · GroupCode 10 · SignerRole 100 · InitialsChain 300) — không trust spec; ApprovalGroup InclusiveBetween(1,8).
  • Dup-Code: AnyAsync(x => x.Code == req.Code) chạy trên ACTIVE-set (query filter !IsDeleted) → khớp UNIQUE filtered [IsDeleted]=0, Code của row xoá-mềm tái dùng được. Delete = Remove → soft qua AuditingInterceptor.
  • MỚI SolutionErp.Api/Controllers/ContractCatalogController.cs[Route("api/catalogs/contract-catalog")] + class [Authorize], 3 verb ghi [Authorize(Roles = "Admin")]. 0 policy key mới.
    • Ghi chú hòa 2 lệnh: lead nói "controller ContractCatalogController", invest-spec K1.c:51 nói "extend CatalogsController" với path api/catalogs/contract-catalog → tôi lấy file controller riêng (theo lead) + giữ nguyên path (theo spec) ⇒ FE KIND_CONFIG contract-catalog chạy được không cần đổi.

P2-B5 Build — PASS

dotnet build SolutionErp.slnx --nologo -v minimalBuild succeeded · 0 Warning · 0 Error (15.3s, 6 project) — PASS ngay lần đầu, 0 vòng sửa compile.

  • ⚠️ 1 quyết-định data khai rõ: transcribe B2-07..B2-14 có chú-thích truy-vết (ảnh: số 7 thứ nhất), (ảnh: 9)… — đó là ghi-chú của người chép, KHÔNG phải tên hạng mục. Tôi seed TenVi sạch ("Công tác kim loại", "Cảnh quan"…) và chuyển chú-thích thành comment cuối dòng code. Số dòng giữ nguyên 14 (AS-IS về row). Nếu owner muốn giữ nguyên văn trong tên → sửa 8 chuỗi, 1 phút.