Files
solution-erp/docs/changelog/sessions/2026-06-12-S60-S62-pe-budget-workitem-softwarning.md
pqhuy1987 5e6dcc1479 [CLAUDE] Docs: S63 closeout S60-62 — re-tier STATUS/HANDOFF + count-flush (Mig 50, 88 bang, 263 test, 64 gotcha) + reconcile stray reviewer + gotcha #63/#64
Viet bu docs cho S60/S61/S62 (ship code prod-verified nhung chua closeout — drift bat o /session-start S63 qua git log).

- Reconcile stray reviewer cwd-misland: MOVE 2 file con fe-admin/.claude -> canonical + pointer (no overwrite 31KB) + xoa stray

- Commit harvest S61/S62: cicd-monitor MEMORY (Run #286) + gotcha #63 (EF RenameColumn sai-semantics) + #64 (Design-DB vs Dev-DB data-migrate)

- Count-flush 4 file: Mig 49->50, tables 93->88, test 240->263 (45D+218I), gotcha 62->64, menu 57->53, Budget module REMOVED->PeWorkItemBudgets

- Session-log bu 2026-06-12-S60-S62-pe-budget-workitem-softwarning.md. Docs-only -> CI skip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 20:41:47 +07:00

69 lines
10 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# S60S62 (2026-06-12 → 06-13) — PE workflow polish + ngân sách per-gói-thầu (Mig 50, XÓA module Budget) + soft-warning vượt ngân sách
> **Closeout muộn (viết bù S63 2026-06-15):** 3 session product S60/S61/S62 ship CODE prod-verified nhưng KHÔNG closeout docs cùng lúc (UAT realtime deadline anh Kiệt FDC — đúng pattern "code committed, docs mù" P1). Drift lộ ở `/session-start` S63 khi `git log` cho thấy docs dừng S59 (`6bf28bf`) còn HEAD = `7926c21` (S62) + Mig 50. Nguồn log: 4 commit `37122f0`→`7926c21` + cicd-monitor MEMORY (Run #286) + reviewer stray reconcile.
>
> **Driver xuyên suốt:** anh Kiệt (FDC) UAT realtime trên eoffice prod — chuỗi chỉnh PE module theo phản hồi thực tế. (PE module "CÒN CHỈNH NHIỀU" như CLAUDE.md cảnh báo.)
**Net state sau S62:** Mig **50** · **88** bảng (was 93 — Mig 50 XÓA module Budget) · **263** test (45 Domain + 218 Infra) · **64** gotcha (#63/#64) · menu **53** (was 57 — gỡ 4 Bg_*) · bundle admin **`0xKYGhhf`** / user **`C81ZdG9G`** (Run #286).
---
## S60 (2026-06-12) — 2 commit: ràng buộc gửi duyệt + gỡ "Từ chối"
### `37122f0` (11:53) — Ràng buộc đủ 4 thông tin mục 3 + bypass người soạn trong chuỗi duyệt
- **Rename mục 3** "Chọn NCC / TP thắng thầu" → **"Đơn vị NCC/TP được chọn"** (anh Kiệt chốt chữ) ×2 app + wording phụ nhất quán.
- **Guard gửi duyệt đủ CẢ 4** (anh chốt): đơn vị được chọn + giá chào thầu >0 + ngân sách (Budget link HOẶC nhập tay) + bảng so sánh đính kèm.
- BE `ConflictException` gộp mọi mục thiếu 1 lần, áp **cả Admin** (`TransitionAsync` submit branch).
- FE pre-check `missingForApproval` cùng predicate → disable nút + tooltip liệt kê đủ (`computeGiaChaoThau` extract single-source).
- **Bypass drafter-in-chain** (luật GENERIC theo cấp, anh chốt): V2-only, **BƯỚC ĐẦU only** — người soạn là approver cấp k → auto qua Cấp 1..k khi gửi.
- Audit 3 tầng: Approval row `AutoApprove` per cấp + LevelOpinion CHỈ slot chính chủ (KHÔNG gắn chữ ký NV bị skip) + Changelog.
- Pointer: k<max Cấp k+1; hết bước Bước 2 Cấp 1; workflow 1 bước terminal DaDuyet. TraLai resubmit áp lại idempotent (opinion UPSERT).
- **Test:** +14 `PeSubmitGuardAndBypassTests` (240 **254** PASS). Reviewer die mid-run (gotcha #53 class) em main self-gate evidence-checklist PASS 0 blocker.
### `6db195d` (14:30) — Gỡ hành động "Từ chối" (chỉ còn Duyệt / Trả lại)
- **Domain policy:** xóa MỌI transition `TuChoi` cả 4 policy (NccOnly + NccWithPlan + ForV2Schema + FromDefinition) `NextPhases` hết trả TuChoi, nút FE tự biến mất.
- **Service guard S60:** chặn `targetPhase=TuChoi` mọi caller kể cả Admin (đứng TRƯỚC mọi branch spec bỏ hẳn, không escape hatch); message hướng dẫn dùng Trả lại / Xóa nháp.
- **FE ×2 app:** filter `next.filter(p != TuChoi)` PeWorkflowPanel (SHA256 identical); dialog/isCancel giữ dead-safe để flip lại dễ.
- Enum TuChoi + phiếu TuChoi + tab filter "Từ chối" **GIỮ display** (data render bình thường). SlaExpiryJob chỉ Contract PE không auto-TuChoi, không ảnh hưởng.
- **Test 254 256** (59 Domain + 197 Infra). Spec-change tests cùng commit (Domain flip `BothPolicies_TuChoi_Removed...` + NEW V2SchemaPolicy fact; Infra NEW `TargetTuChoi_WithRejectDecision_Throws_TuChoiRemoved_S60`; guard #45 test giữ PASS).
---
## S61 (2026-06-13 01:07) — `79ef8da` Mig 50: ngân sách gói thầu theo Excel anh Kiệt + XÓA module Budget cũ
- **Mig 50 `ReplaceBudgetModuleWithPeWorkItemBudgets`:** bảng mới `PeWorkItemBudgets` (1 record/cặp Dự án × Hạng mục, UNIQUE filtered `[IsDeleted]=0`) + **drop module Budget cũ** + PE/Contracts **drop `BudgetId`** + **backfill `BudgetManualAmount→BudgetPeriodAmount` TRƯỚC DropColumn** (phiếu UAT giữ số) + DELETE menu/permission `Bg_*` IN-list children-first.
- **BE:** `PUT {id}/budget/pro` (role Procurement) + `{id}/budget/ccm` (role CostControl, Adjustment cho phép ÂM) **fail-closed Forbidden-TRƯỚC-side-effect** + `EnsureTrackedAsync` race-safe (catch unique re-fetch winner, lỗi khác rethrow) + auto-create record khi tạo phiếu + `budgetSummary` DTO (lũy kế trình-trước/chọn-thầu-trước/đề-xuất-kỳ-này + full fallback dự-trù-PRO + canEdit flags) + submit-guard (3) đổi predicate `BudgetPeriodAmount` "chưa nhập Ngân sách kỳ này" + PATCH budget-adjust absolute-set 2 field mới + Contract GIỮ `BudgetManual*` ( nhập tay không đổi) + kế thừa map `BudgetPeriodAmount`.
- **FE ×2 app SHA256 identical:** bảng "TỔNG HỢP NGÂN SÁCH TRÌNH " block A (full đầm + ban hành + V0 hiệu chỉnh + dự trù PRO + ghi chú, editable theo `canEditPro`/`canEditCcm`) + block B 9 dòng công thức Excel (5=1+3, 6=2+4, 7=full5, 8 tự nhập default 7, 9=4+8) + màu vượt ngân sách `#C00000` / âm đỏ / red-soft row8>row7 + "Chưa chọn" khi count=0 + banner phiếu chưa gắn Hạng mục + ô "Ngân sách kỳ này" ở create/header + **XÓA pages/components/types budgets + routes + menuKeys + Layout staticMap (4-place)**.
- **Test:** +22 `PeWorkItemBudgetTests` (auto-create ×3, ensure/race ×2, authz matrix PRO ×5 + CCM ×3, budgetSummary aggregates ×5, adjust ×4) 14 `BudgetPolicyTests` (xóa theo module) 1 test via-BudgetId → **263 PASS** (45 Domain + 218 Infra, 0 fail).
- **database-agent advise adopted:** không FK vật lý PE/Contracts→Budgets (DropColumn không cần DropForeignKey) + DropIndex TRƯỚC DropColumn (SQL 5074) + IN-list thay LIKE `Bg_%` (underscore wildcard + miss root) + không Serializable wrap (nested-tx conflict codegen).
- **Reviewer PASS-with-minor 0 blocker** (verdict-first survived); 2 minor đã sửa trước commit (comment adjustMut absolute-set + dead key budgetId). Note: F4 approver-edit-budget UI entry tạm drafter-only, BE vẫn cho approver scope — chờ UAT anh Kiệt.
- **⚠️ Scaffold-bug caught → 2 gotcha NEW:**
- **#63** EF tự sinh `RenameColumn(BudgetManualAmount→ExpectedRemainingAmount)` SAI semantics (drop+add cùng type → EF heuristic đoán rename) → thay bằng `AddColumn` + `Sql(UPDATE backfill)` + `DropColumn`. SQLite test (`EnsureCreated` từ model) KHÔNG bắt được.
- **#64** `dotnet ef database update` áp **Design DB** (`DesignTimeDbContextFactory`, 0 rows) ≠ runtime Dev DB → `Sql()` backfill CHƯA TỪNG chạy trên data thật trước prod. Guard: cicd brief BẮT BUỘC mục DATA-PRESERVE spot-check sau deploy.
---
## S62 (2026-06-13 11:13) — `7926c21`: vượt ngân sách = cảnh báo mềm (cho lưu số âm row 8)
- **Root cause:** ô "Giá trị thực hiện dự kiến còn lại" (row 8 bảng Tổng hợp) khi giá trị NCC vượt ngân sách → số dư còn lại ra ÂM; BE validator `ExpectedRemainingAmount>=0` + FE `VndInlineEdit` không bật `allowNegative` → chặn cứng "âm không lưu được" (testing báo qua anh Kiệt).
- **BE:** `AdjustPurchaseEvaluationBudgetCommandValidator` GỠ rule `ExpectedRemainingAmount.GreaterThanOrEqualTo(0)` → cho lưu số âm (mirror tiền lệ LeaveBalance `AllowsNegativeRemaining`). GIỮ `BudgetPeriodAmount>0` + submit-guard "đã nhập NS kỳ này" không đổi. (`PurchaseEvaluationFeatures.cs:317` validator.)
- **FE ×2 app SHA256 identical:** (a) `allowNegative` cho VndInlineEdit row 8; (b) banner amber "Vượt ngân sách — vẫn lưu & gửi duyệt được" trong `PeBudgetSummaryTable` khi `cmpPeriod<0 || cmpFull<0`. Tô màu đỏ cũ GIỮ NGUYÊN.
- **Spec change:** flip test `AdjustBudget_Validator_ExpectedRemainingNegative_FailsValidation``_PassesValidation` (âm giờ hợp lệ); test `BudgetPeriodZero_FailsValidation` GIỮ (budget>0 vẫn enforced).
- **Build FE ×2 PASS + test 263 PASS** (45 Domain + 218 Infra, 0 fail/skip). **Reviewer PASS 0 issue** (row8 âm an toàn arithmetic additive-only — row9=row4+row8, cmpFull=fullrow9, no division/sqrt/unsigned-cast; submit guard nguyên; mirror byte-identical; no scope creep).
- **cicd Run #286** sha `7926c21` PASS ~4m41s — bundle ROTATE admin `DsGZlNzT→0xKYGhhf` + user `DTL_bjzQ→C81ZdG9G` (FE×2 changed, đúng). DATA-PRESERVE spot-check 8/8 phiếu UAT giữ số (gồm phiếu 1.243.820.600 đ anh Kiệt).
---
## Quality + lessons
- **3 session deadline-driven KHÔNG closeout docs** = drift S59→S62 (Mig/table/test/gotcha/menu/bundle đều lệch). Bắt được ở S63 `/session-start` qua `git log` (session-log disk + MEMORY count đều lag). **Lesson H1: đầu session luôn `git log` trước, đừng tin MEMORY count.**
- **Reviewer cwd-relative mis-land (S62):** reviewer cd `fe-admin/` → Write MEMORY relative-path → 3 file rơi `fe-admin/.claude/agent-memory/reviewer/` (pattern `feedback_agent_cwd_relative_memory_misland` S54). Reconcile S63: MOVE 2 file con vào canonical + MERGE pointer (KHÔNG overwrite 31KB) + xóa stray.
- **EF migration data-migrate (gotcha #63/#64):** test xanh + "applied-local OK" ≠ migration đúng — SQLite test dựng từ model (không replay migration), `ef database update` áp Design-DB 0-rows. Guard duy nhất = đọc file migration sau scaffold + cicd DATA-PRESERVE spot-check sau deploy.
## Carry / NEXT (cho session sau)
- **test-after guard** (deadline trade-off): `PeWorkItemBudgetTests` đã có; cân nhắc thêm guard `LockDemoSampleUsersAsync` (S58) + suppliers asymmetric authz (S59) — vẫn pending.
- **F4 approver-edit-budget UI** tạm drafter-only (BE cho approver scope) — chờ anh Kiệt UAT chốt mở UI cho approver.
- **schema-diagram §16+** Mig 32-50 ERD debt (giờ +Mig 50 Budget-drop) — monthly audit 2026-07-01.
- **cicd-monitor L1 MEMORY 63.6KB** over-cap lần 5 → curate L2 gấp (H2 flag).
- **Bundle/Run** S60/S61 run numbers (#283-#285) chưa truy verbatim — nếu cần, đọc cicd-monitor MEMORY recent entries.