wal: flush 20260729T1932

This commit is contained in:
pqhuy1987
2026-07-29 19:32:44 +07:00
parent 22bd8997d4
commit 66f79579dc
8 changed files with 239 additions and 5 deletions

View File

@ -9,7 +9,7 @@
| # | Stage | Vai / cách | Trạng thái |
|---|---|---|---|
| 1 | Đợt-1a BE: Domain 7 entity + 3 enum + ApplicableType=10 + EF Config ×7 + DbSets + **Mig 69** 3-file (apply 2 DB) + MenuKeys All+= + DbInitializer UPGRADE-if-exists (13/13 role) + CodeGen KHKK + 3 site AWV2AdminFeatures | hmw `implementer-backend` | [!] lane-1 `wf_7d6fb81b-640` chết #53 SAU Domain-layer (8 file entity ĐỦ RUỘT + ApplicableType=10 + 4/7 config — lead verify đĩa; 0 sub-md) → **RELAUNCH-CẮT-GỌT `wf_f8c806d2-5a3`** (3 config + DbSets + Mig 69 + MenuKeys + seeder + CodeGen + 3-site + build) ĐANG CHẠY |
| 1 | Đợt-1a BE: Domain 7 entity + 3 enum + ApplicableType=10 + EF Config ×7 + DbSets + **Mig 69** 3-file (apply 2 DB) + MenuKeys All+= + DbInitializer UPGRADE-if-exists (13/13 role) + CodeGen KHKK + 3 site AWV2AdminFeatures | hmw `implementer-backend` | [x] 3 chặng: lane-1 `wf_7d6fb81b-640` chết #53 sau Domain (8 entity + type-10 + 4/7 config) → lane-2 `wf_f8c806d2-5a3` cũng chết #53 NHƯNG sub-md 6.2KB ghi-trong-lúc-làm cứu trọn (audit 0-lệch + 3 config + DbSets + MenuKeys:179 + **Mig 69 `20260729122015` 3-file, apply 2 DB, sys.tables=96**, grep-proof trong sub-md) → **lead solo đuôi @S161**: seeder `SeedKeHoachKyKetAccessAsync` UPGRADE-if-exists+CanDelete (call sau revoke) + `ContractSigningPlanCodeGenerator` KHKK/{YYYY}/{Seq:D3} + DI + 3 site AWV2Admin (count/retained/orphan mirror VehicleBooking). **`dotnet build` 0W/0E 47s** |
| 2 | Đợt-1b FE: Designer type-10 `ApprovalWorkflowsV2Page.tsx` (TYPE_CODE_TO_INT + option + ẩn 2 checkbox early-finalize khi type=10) | hmw `implementer-frontend` | [x] `wf_7d6fb81b-640` lane-2 CLEAN — +67/9, containment 2-LỚP render+payload; **lead verify build tươi PASS** (bundle `index-iNZq9job`; diagnostics giữa-agent = stale #68); harvest diary xong. Cờ đỏ: type-10 chưa có đường sidebar (allowlist `Layout.tsx:162-168` — cả type-3 xưa nay cũng vậy, đúng spec "menu KHÔNG đổi") |
| 3 | Đợt-2a: schema test + codegen format test (`ContractSigningPlanSchemaTests` + `SigningPlanCodeGenerator_Format_KhkkYearSeqD3`, N ≥ 2) | hmw `test-specialist` | [ ] |
| 4 | Đợt-2b: design-review Mig 69 TRƯỚC deploy (reversible Down · snapshot drift · FK/index) | hmw `database-agent` | [ ] |

View File

@ -0,0 +1,75 @@
# sub-database-agent-1 — Design-review Mig 69 `AddContractSigningPlans` (S161 W1, READ-ONLY advisory)
> Ghi TỪNG MỤC trong lúc làm (chống #53). Không sửa file production. Không commit.
> Nguồn đo: file migration + Designer + ModelSnapshot + 7 EF Configuration + sqlcmd LocalDB Dev/Design.
## VERDICT TỔNG (điền cuối, xem §END)
---
## 0. Vật liệu đã đọc (evidence anchors)
| Vật | Đường dẫn | Ghi chú |
|---|---|---|
| Migration | `src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260729122015_AddContractSigningPlans.cs` | 373 dòng |
| Designer | `...20260729122015_AddContractSigningPlans.Designer.cs` | 264.515 B |
| Snapshot | `...Migrations/ApplicationDbContextModelSnapshot.cs` | 264.399 B, git diff +550/-0 |
| Config ×7 | `src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/ContractSigningPlan*.cs` | 7 file |
| Spec W1 | `.claude/workflows/runs/2026-07-29-S160-khkk-dryrun-plan/spec-wave-w1-schema-designer-29-07-2026.md:16-20` | §②-2/4/6 |
| Spec gốc maxLength | `.claude/workflows/runs/2026-07-28-S157-ke-hoach-ky-ket-hd/spec-ke-hoach-ky-ket-hd-28-07-2026.md:134-137` | danh sách 11 cột chuỗi |
Đếm operation trong migration (grep + uniq):
- `CreateTable` ×7 · `CreateIndex` ×20 · `DropTable` ×7 (Down)
- `AddColumn` / `AlterColumn` / `DropColumn` / `RenameColumn` / `Sql(` = **0** (không match nào)
---
## 3. Đối chứng sqlcmd LocalDB (DB1/DB10 — schema THẬT, không trí nhớ)
`sqlcmd -S "(localdb)\MSSQLLocalDB" -d SolutionErp_Dev -E` :
```
TABLECOUNT|96
CSPTABLE|ContractSigningPlanApprovals
CSPTABLE|ContractSigningPlanAttachments
CSPTABLE|ContractSigningPlanChangelogs
CSPTABLE|ContractSigningPlanDossierItems
CSPTABLE|ContractSigningPlanLevelOpinions
CSPTABLE|ContractSigningPlanLines
CSPTABLE|ContractSigningPlans
MIGTOP|20260729122015_AddContractSigningPlans
MIGCOUNT|69
```
`-d SolutionErp_Design` :
```
DESIGN_TABLECOUNT|96 · DESIGN_CSP|7 · DESIGN_MIGTOP|20260729122015_AddContractSigningPlans · DESIGN_IXCOUNT|27
```
**2 DB parity TUYỆT ĐỐI** (96/96 bảng · 7/7 CSP · cùng mig-top · 27 index = 20 IX + 7 PK).
**KHÔNG có committed-but-unapplied-local drift** (khác bẫy S53).
→ Acceptance §③-B "sys.tables 89 → 96" = **ĐẠT** (đo được 96; delta +7 khớp 7 CreateTable).
### Index thật trong DB (27 dòng, khớp 1:1 file mig)
Nhóm filtered (soi gotcha #57):
```
ContractSigningPlanLines | IX_..._ContractSigningPlanId_SupplierId | unique=1 | ([IsDeleted]=(0)) | ContractSigningPlanId,SupplierId
ContractSigningPlans | IX_ContractSigningPlans_MaKeHoach | unique=1 | ([MaKeHoach] IS NOT NULL) | MaKeHoach
```
Các IX còn lại filter = `-` (không filter), unique=0 trừ `IX_ContractSigningPlanLevelOpinions_ContractSigningPlanId_ApprovalWorkflowLevelId` (unique=1, **không** filter — bàn ở §2e/§4).
### FK thật (8 FK, cột `delete_referential_action_desc`)
```
Approvals → ContractSigningPlans CASCADE
Attachments → ContractSigningPlans CASCADE
Changelogs → ContractSigningPlans CASCADE
DossierItems → ContractSigningPlans CASCADE
LevelOpinions→ ContractSigningPlans CASCADE
Lines → ContractSigningPlans CASCADE
LevelOpinions→ ApprovalWorkflowLevels NO_ACTION (= Restrict)
ContractSigningPlans → ApprovalWorkflows NO_ACTION (= Restrict)
```
→ 6 con Cascade 1-hop + 2 Restrict, **đúng y spec §②-2/4**. Zero FK vật lý sang PE/Project/Supplier/User/Contract (loose-Guid, convention Mig 49) — xác nhận bằng chính danh sách 8 FK trên (không có dòng nào trỏ `PurchaseEvaluations`/`Suppliers`/`Projects`).
---

View File

@ -0,0 +1,84 @@
# sub-implementer-backend-0 — W1 KHKK lane BE (RELAUNCH-CẮT-GỌT, tiếp lane chết #53)
> Ghi TỪNG MỤC ngay sau khi xong mục đó (lane trước chết trước khi kịp ghi).
> Run: `2026-07-29-S161-khkk-w1-schema` · spec `spec-wave-w1-schema-designer-29-07-2026.md` §② 2→9.
## §0 — AUDIT mục "✅ ĐÃ XONG" (trước khi làm tiếp)
| Vật | Đo | Verdict |
|---|---|---|
| `Domain/ContractSigningPlans/` | 8 file (7 entity + `ContractSigningPlanPhase.cs`) | PASS |
| `ContractSigningPlanPhase` | `:10-14` = DangSoanThao=1 / ChoDuyet=2 / DaDuyet=3 / TraLai=98 / TuChoi=99 | PASS khớp spec `:16` |
| `DossierItemKind` | `ContractSigningPlanDossierItem.cs:7-12` = MauVatLieu=1/Shopdrawing=2/Khac=99 | PASS |
| `DossierItemStatus` | `ContractSigningPlanDossierItem.cs:17-23` = ChuaNop=1..TvgsBac=4 (KHÔNG 0) | PASS — vá C2 |
| `ApprovalWorkflowApplicableType.ContractSigningPlan = 10` | `ApprovalWorkflow.cs:66` (append-only, sau `TravelRequest = 9`) | PASS |
| Header +`DepartmentId Guid?` (C5) | `ContractSigningPlan.cs:21` | PASS |
| Lines +`ContractId Guid?` (C6) | `ContractSigningPlanLine.cs:25` | PASS |
| Changelog ĐỦ 4 cột (C1) | `ContractSigningPlanChangelog.cs:29,30,34,36` EntityType/EntityId/UserName/FieldChangesJson | PASS |
| Configuration đã có | `ContractSigningPlanConfiguration` + `...Line` + `...DossierItem` + `...LevelOpinion` = 4/7 | đúng như brief |
| UNIQUE filtered Lines | `ContractSigningPlanLineConfiguration.cs:22-24` `(PlanId,SupplierId)` + `HasFilter("[IsDeleted] = 0")` | PASS gotcha #57 |
| UNIQUE LevelOpinions | `ContractSigningPlanLevelOpinionConfiguration.cs:36` `(PlanId,LevelId)` IsUnique KHÔNG filter | PASS (parity 7 twin, UPSERT) |
→ 0 lệch spec ⇒ KHÔNG sửa gì phần đã xong.
## MỤC 1 — 3 Configuration còn lại ✅
3 file MỚI (khuôn = 4 config đã có + twin PE `PurchaseEvaluationConfiguration.cs:156-210`):
1. `Configurations/ContractSigningPlanAttachmentConfiguration.cs`
- FileName 500 IsRequired · StoragePath 1000 IsRequired · ContentType 200 IsRequired (spec `:134-137`;
RỘNG hơn PE 255/500/100 — khuôn `ProposalAttachmentConfiguration.cs:15-17`) · Note 1000 · Purpose `HasConversion<int>()`
- FK **Cascade** Plan; `ContractSigningPlanDossierItemId` chỉ `HasIndex` (KHÔNG FK — tránh MULTIPLE CASCADE PATHS)
- `HasQueryFilter(!IsDeleted)` — AuditableEntity
2. `Configurations/ContractSigningPlanChangelogConfiguration.cs`
- Summary **1000** (spec, PE để 500) · ContextNote 2000 · UserName 200 · FieldChangesJson `nvarchar(max)`
- IX `(PlanId, CreatedAt)` + `(PlanId, EntityType)` (index twin C1)
- BaseEntity ⇒ **KHÔNG** HasQueryFilter (parity PE Changelog)
3. `Configurations/ContractSigningPlanApprovalConfiguration.cs`
- Comment 1000 · From/ToPhase/Decision `HasConversion<int>()`
- IX `(PlanId, ApprovedAt)` (twin PE `:168`) + IX `ApprovalWorkflowLevelId`
- `ApprovalWorkflowLevelId` loose-Guid KHÔNG FK (nếu Restrict → admin xoá Level nổ 500, vì 3 site dọn dẹp
chỉ biết họ `*LevelOpinions`)
- BaseEntity ⇒ KHÔNG HasQueryFilter
⚠️ Cả 7 bảng con đều FK Cascade 1-hop từ Plan ⇒ 0 multiple-cascade-path.
## MỤC 2 — DbSets ×7 ✅
- `Application/Common/Interfaces/IApplicationDbContext.cs:3` +`using SolutionErp.Domain.ContractSigningPlans;`
· `:144-153` block 7 DbSet + chú thích "mã KHKK qua `WorkflowAppCodeSequences` (:136) DÙNG CHUNG, KHÔNG bảng sequence riêng".
- `Infrastructure/Persistence/ApplicationDbContext.cs:5` +using · `:129-136` 7 `public DbSet<...> => Set<...>()`.
- Config tự nạp qua `ApplyConfigurationsFromAssembly` (`ApplicationDbContext.cs` cuối `OnModelCreating`) — 0 dòng đăng ký tay.
## MỤC 4 (làm sớm, trước build) — MenuKeys ✅
`Domain/Identity/MenuKeys.cs:179` — thêm ĐÚNG 1 dòng `KeHoachKyKet,` vào `All` (const sẵn `:41`, KHÔNG key mới).
`Program.cs` foreach `All × Actions` tự sinh 4 policy `KeHoachKyKet.{Read,Create,Update,Delete}`.
## Build sau mục 1+2+4
`dotnet build SolutionErp.slnx`**Build succeeded. 0 Warning(s) / 0 Error(s)** (56.77s, 6 project).
## MỤC 3 — Mig 69 `AddContractSigningPlans` ✅
Lệnh: `dotnet ef migrations add AddContractSigningPlans --project src/Backend/SolutionErp.Infrastructure --startup-project src/Backend/SolutionErp.Api --output-dir Persistence/Migrations`
**3-file rule (gotcha #17) — `git status` chứng:**
- `?? 20260729122015_AddContractSigningPlans.cs` (22.059 B)
- `?? 20260729122015_AddContractSigningPlans.Designer.cs` (264.515 B)
- ` M ApplicationDbContextModelSnapshot.cs`
**Nội dung đo (grep, KHÔNG đọc-mắt):**
- `grep -c CreateTable` = **7** · Down = **7 DropTable** (`:350-368`) · **0** `AddColumn/DropColumn/AlterColumn/Sql/RenameColumn` ⇒ 0 ALTER bảng cũ.
- UNIQUE filtered Lines `:301-306`: `(ContractSigningPlanId, SupplierId)` `unique: true, filter: "[IsDeleted] = 0"` ✅ gotcha #57
- UNIQUE LevelOpinions `:287-291`: `(ContractSigningPlanId, ApprovalWorkflowLevelId)` `unique: true` (KHÔNG filter — đúng parity twin)
- UNIQUE MaKeHoach `:325-330`: filter `[MaKeHoach] IS NOT NULL`
- FK: Plan→ApprovalWorkflows **Restrict** `:47` · LevelOpinions→ApprovalWorkflowLevels **Restrict** `:201` · 6 con→Plan **Cascade** (`:76,108,139,171,207,238`)
- maxLength khớp spec `:134-137` 100%: MaKeHoach 50 · GhiChu/ContextNote 2000 · Name 500 · Note 1000 · TvgsName 200 · Summary 1000 · SignedByFullName 200 NOT NULL · FileName 500 / StoragePath 1000 / ContentType 200 · Comment(LevelOpinion) 2000 / Comment(Approval) 1000 · HoSoLink 1000.
Chỉ `FieldChangesJson` = `nvarchar(max)` `:124` (cố ý, JSON tự do).
**Apply 2 DB:**
- `migrations list --connection …SolutionErp_Dev` TRƯỚC apply: chỉ **1 Pending** = Mig 69 ⇒ **0 drift** (khác S133 phải catch-up).
- Dev: `Applying migration '20260729122015_AddContractSigningPlans'. Done.`
- Design (default factory): `Applying migration '20260729122015_AddContractSigningPlans'. Done.`
- `sqlcmd` Dev: `sys.tables WHERE is_ms_shipped=0` = **96** (89→96, +7) · `LIKE 'ContractSigningPlan%'` = **7**.

View File

@ -0,0 +1,65 @@
# sub-test-specialist-0 — W1 KHKK Mig 69: schema-invariant test + codegen format test
> Ghi TỪNG MỤC trong lúc làm (chống #53). Run: `2026-07-29-S161-khkk-w1-schema`.
> Vai: test-specialist (WRITE độc quyền `tests/**`). KHÔNG sửa production, KHÔNG commit.
---
## §0 — Tiền đề đọc từ ĐĨA (trước khi viết 1 dòng test)
Spec đọc: `.claude/workflows/runs/2026-07-29-S160-khkk-dryrun-plan/spec-wave-w1-schema-designer-29-07-2026.md`
- §③-B dòng 35: test PIN tên `ContractSigningPlanSchemaTests.BayBang_MoiBang_CoAuditFields_VaQueryFilter`, sàn `N ≥ 2`, số-sau ≥ số-trước + 2.
- §③-B dòng 36: test PIN tên `SigningPlanCodeGenerator_Format_KhkkYearSeqD3` — regex `^KHKK/\d{4}/\d{3}$` + 2 lần gọi seq tăng.
### 7 entity — base class ĐO THẬT trên đĩa (KHÔNG đoán)
| # | Entity | File | Base | Query filter (config) |
|---|---|---|---|---|
| 1 | `ContractSigningPlan` | `Domain/ContractSigningPlans/ContractSigningPlan.cs:16` | `AuditableEntity` | ✅ `ContractSigningPlanConfiguration.cs:47` |
| 2 | `ContractSigningPlanLine` | `ContractSigningPlanLine.cs:14` | `AuditableEntity` | ✅ `ContractSigningPlanLineConfiguration.cs:36` |
| 3 | `ContractSigningPlanDossierItem` | `ContractSigningPlanDossierItem.cs:31` | `AuditableEntity` | ✅ `...DossierItemConfiguration.cs:29` |
| 4 | `ContractSigningPlanAttachment` | `ContractSigningPlanAttachment.cs:24` | `AuditableEntity` | ✅ `...AttachmentConfiguration.cs:39` |
| 5 | `ContractSigningPlanLevelOpinion` | `ContractSigningPlanLevelOpinion.cs:24` | `AuditableEntity` | ❌ **CỐ Ý KHÔNG có** |
| 6 | `ContractSigningPlanChangelog` | `ContractSigningPlanChangelog.cs:26` | `BaseEntity` | ❌ (đúng — no `IsDeleted`) |
| 7 | `ContractSigningPlanApproval` | `ContractSigningPlanApproval.cs:19` | `BaseEntity` | ❌ (đúng — no `IsDeleted`) |
🔴 **LỆCH so mô-tả trong TASK-prompt** (prompt liệt `LevelOpinion = AuditableEntity có IsDeleted` ⇒ ngụ ý phải có query filter). ĐĨA nói khác: `ContractSigningPlanLevelOpinion``AuditableEntity` **nhưng CỐ Ý KHÔNG HasQueryFilter**, có lý do viết sẵn 2 chỗ:
- entity `ContractSigningPlanLevelOpinion.cs:20-23`: *"CỐ Ý KHÔNG có HasQueryFilter (parity 7 twin còn lại): site 'dọn chữ ký MỒ CÔI' (ApprovalWorkflowV2AdminFeatures ExecuteDeleteAsync) phải THẤY được row đã soft-delete, nếu bị filter che thì row mồ côi sống sót và FK Restrict lại nổ 500."*
- config `ContractSigningPlanLevelOpinionConfiguration.cs:12-16`: cùng nội dung.
⇒ Áp luật MEMORY "spec drift detection BEFORE write (S34)": **test theo CODE**, không theo prompt. Nhưng KHÔNG bỏ qua — assert LevelOpinion **KHÔNG có filter** như một invariant CÓ CHỦ Ý (nếu ai đó thêm filter sau này ⇒ test ĐỎ ⇒ buộc đọc lại lý do). Đây là assert có RĂNG, không phải né.
### Codegen
`src/Backend/SolutionErp.Infrastructure/Services/ContractSigningPlanCodeGenerator.cs`
- ctor `(IApplicationDbContext db, IDateTime dateTime)``:12`
- `GenerateAsync(CancellationToken ct = default)` — KHÔNG nhận entity (khác PE nhận `PurchaseEvaluation`)
- prefix `$"KHKK/{year}"`, return `$"{prefix}/{seq.LastSeq:D3}"``:20`, `:45`
- `BeginTransactionAsync(IsolationLevel.Serializable, ct)``:24-25`
- sequence sống ở `db.WorkflowAppCodeSequences` (bảng DÙNG CHUNG Office), PK `Prefix`
### Tiền lệ SQLite + Serializable — ĐÃ CÓ, không cần chế mới
`tests/SolutionErp.Infrastructure.Tests/Common/SqliteDbFixture.cs:36-42` ghi rõ:
> *"SQLite hỗ trợ transactions thật (BEGIN/COMMIT/ROLLBACK), tuy nhiên IsolationLevel enum value bị provider mapping gracefully (no exception, just default behavior)"*
`PurchaseEvaluationCodeGeneratorTests.cs:17` gọi thẳng `new PurchaseEvaluationCodeGenerator(fix.Db, dt)` — generator PE cũng `Serializable`, test cũ chạy XANH nhiều session ⇒ **dùng y khuôn, KHÔNG đụng production**.
`FixedDateTime` stub: `SqliteDbFixture.cs:71`.
---
## §1 — Test đã viết
File MỚI: `tests/SolutionErp.Infrastructure.Tests/Application/ContractSigningPlanSchemaTests.cs`
(đặt cạnh `EmployeeSatelliteTests.cs` — cùng lớp "EF-model assertion", khuôn `EmployeeSatelliteTests.cs:248-261` `db.Model.FindEntityType(...)`).
(chi tiết điền sau khi chạy — xem §2/§3)
---
## §2 — Số đo
(điền sau)
## §3 — Verdict
(điền sau)