10 KiB
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ó)
- MenuKeys VÀO
All(lead) vs NGOÀIAll(invest-spec K1.d:57) → theo lead, khai drift bên dưới. - Controller riêng
ContractCatalogController(lead) vs extendCatalogsController(invest-spec K1.c:51) → theo lead về FILE, giữ PATHapi/catalogs/contract-catalogcủa spec để FE khỏi đổi. - 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. - 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 minimal→ Build succeeded · 0 Warning(s) · 0 Error(s) · Time Elapsed 00:00:15.33dotnet 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 + DTOsrc/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òng —SeedContractCatalogAsync(86 row) + call-siteInitializeAsync+ 1 menu row leafsrc/Backend/SolutionErp.Domain/Identity/MenuKeys.cs+5 dòng — constContractCatalog+ vàoAll
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)
- Mig 70 CHƯA áp Dev-DB —
sqlcmd (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). - 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.
- Leaf menu hiện chỉ Admin thấy — 3 seeder grant khác dùng list-key-cứng (chi tiết P2-B2).
- [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).
- 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 CatalogsControllerdanh-muc-sp002-transcribe.mdTRỌ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-2986khuôn tuple-array + per-Code idempotentMenuKeys.cs:164-186vùngAll(5 leaf catalog :168)
⚠️ LỆCH SPEC đã khai (lead override, làm theo lead)
- invest-spec §K1.d (:57) ghi
MenuKeys.ContractCatalogNGOÀIAll(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.mdrowMenu keys+1 và rowPolicies+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ƯỚCSeedContractTemplatesAsync) + call-site trongInitializeAsyncngay SAUSeedRealMasterDataAsync(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
signByGroup8 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; theoApprovalGroup= 5/10/5/23/14/15/13/1 → khớp ĐÚNG K1.b:41.
P2-B2 MenuKeys + MenuItem — XONG
MenuKeys.cs+constContractCatalog = "ContractCatalog"(cạnh 4 const catalog,:28-32) + VÀOAllngay sau dòng 5 leaf catalog.DbInitializer.csSeedMenuTreeAsync+row(MenuKeys.ContractCatalog, "Danh mục Hợp đồng", MenuKeys.Catalogs, 5, "FileSpreadsheet")— leaf thứ 5 dưới "Danh mục chi tiết"; iconFileSpreadsheetđã dùng sẵn (MenuKeys.Forms:1788) nên FE có map.- Grant Admin: TỰ ĐỘNG qua loop
SeedAdminPermissionsAsyncMenuKeys.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;ApprovalGroupInclusiveBetween(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_CONFIGcontract-catalogchạy được không cần đổ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
P2-B5 Build — PASS
dotnet build SolutionErp.slnx --nologo -v minimal → Build 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.