Files
solution-erp/docs/changelog/sessions/2026-07-13-S116-pe-negative-quote.md
pqhuy1987 73f2939d8b [CLAUDE] Docs: S116 closeout — PE negative-quote (STATUS/HANDOFF/session-log + harvest)
- STATUS/HANDOFF prepend S116 (test 486->495 · bundle Dd55jBpj/DVDr1UQ4) + fix
  D1 Tests-row (486->495, 441->450 Infra) + D2 bundle-live-pointer (tooling-auditor
  caught 2 derived-summary lags below the mega-line).
- Session log 2026-07-13-S116-pe-negative-quote.md.
- Harvest: investigator-codebase on-behalf S116 (invest lanes RETURN-only) +
  reviewer/impl-fe/impl-be/test/cicd self-written · monitor diaries (harvest-curator
  self-compact moved-not-cut + tooling-auditor archive-move).
- §5.0 squash 2 wal-trailing (dfa1c8c/0edbc58, post-feature Stop-hook flush) folded here.
- Feature already shipped 88368fd (Run 386 PASS 5m28s); this commit = docs/memory/run-trace
  only -> CI skip (paths-ignore #41).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 17:40:27 +07:00

47 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

# S116 — PE cho nhập giá ÂM ô "Số tiền báo giá cho hạng mục" (2026-07-13)
**Commit:** `88368fd` · **CI:** Run 386 PASS 5m28s · **State:** Mig 65 · 89 bảng · **495 test** (486→495) · gotcha 81 · bundle admin `Dd55jBpj` / user `DVDr1UQ4`
**Lead:** Opus 4.8 (owner-choice; Fable UP — xác nhận qua fable-real `claude-fable-5`) · HMW-ON · em-main-led
## Đề bài (anh Kiệt FDC ⇄ Duy Nguyen, Zalo)
Ô **"Số tiền báo giá cho hạng mục"** (`form.thanhTien``Quote.ThanhTien`) trong dialog "Thêm NCC vào phiếu" PE **không nhập được số âm**. Yêu cầu cho nhập âm — số âm = **phát-sinh-giảm / hoàn-tiền** của NCC = giá **THẬT** (không phải lỗi). Rule: **ngân sách GIỮ NGUYÊN**; "có giá là được" (≥1 giá thật âm/dương → gửi được; chỉ chưa-nhập-gì mới chặn).
## Pipeline (anh chỉ định: /fable-clone invest-review → /fable-real → hmw Opus MAX → commit+deploy)
| Stage | Workflow | Kết quả |
|---|---|---|
| invest | `wf_eaf52972-d18` 4× investigator-codebase | **FE-ONLY** — BE/DB nhận âm sẵn (0 validator `UpsertQuoteCommand`, `decimal(18,2)` no CHECK); ngân sách safe by architecture (`PeWorkItemBudgets` role-gated, quote-path không đụng) |
| gate | AskUser ×2 (4 decision) | R1 · R2 · R2-revised · R-CEO (dưới) |
| review | `wf_4824f158-b66` 3× reviewer | Lane1 PASS_WITH_FIXES (sign-plumbing) · Lane2/3 PASS |
| deep-pass | `wf_9648d207-21b` 1× reviewer (Fable) | **#53 garble → em-main-solo gate GO-WITH-FIXES** |
| implement | `wf_48834aca-c41` FE‖(BE→test) Opus MAX | 0-garble, 486→495 |
| verify | em-main độc-lập | dotnet test 495 · mirror SHA · BE build |
| deploy | cicd-monitor | Run 386 PASS, byte-level ship-proof |
## Owner-decisions (anh Kiệt chốt)
- **R1** — Contract.GiaTri âm: **CHO PHÉP** (no clamp; `CreateContractFromEvaluationFeatures.cs:88-100` giữ âm; validator tạo-tay `ContractFeatures.cs:46` GiaTri≥0 = đường khác, giữ).
- **R2 (revised)** — submit-guard `Sum()<=0`**`All(x=>x==0)`**. Anh **hỏi lại mid-pipeline**: "1 NCC / 1 hạng mục âm thì có chặn không?" → phát hiện guard cũ **mâu-thuẫn R1** (cho HĐ âm nhưng chặn phiếu tạo ra nó). Guard là **proxy sai** cho ý-định "chưa báo giá" → sửa về đúng ý-định.
- **R-CEO** — ngưỡng CEO: **giữ NET** (`winnerQuoteTotal` net, dòng âm có thể kéo phiếu dưới ngưỡng → CCM finalize bỏ CEO; owner chấp nhận "duyệt theo net cam-kết").
- **R3** — BgVat/ChuaVat giữ nguyên.
## Thay đổi code (FE + 1 BE, KHÔNG migration)
**FE** `PeDetailTabs.tsx` (fe-user + fe-admin byte-identical SHA `3e16dba5`, 112 dòng/app):
- Nút **± toggle** 2 input quote (AddSupplierDialog `:2365` + QuoteDialog `:2917`), ghi giá-**CÓ-DẤU** thẳng vào `form.thanhTien` (2 dialog POST commit trực tiếp, không nút Lưu riêng → nếu chỉ flip cờ `neg` = mất-dấu thầm lặng — review Lane-1 bắt).
- Submit gate `:2226` + toast `>0``!==0`. Ô ngân sách `:2830` giữ ≥0 (không toggle).
- Helper mới `hasRealSelectedQuote` thay 2 FE submit-mirror (`:201` missingForApproval + `:235` submitChecklist) — SUM `computeGiaChaoThau` không phân-biệt all-zero vs net-zero → iterate `ev.details[].quotes` (KHÔNG `suppliers[].quotes` — FE-agent tự sửa spec-shape-error).
**BE** `PurchaseEvaluationWorkflowService.cs:206`: `selectedThanhTien.Sum() <= 0``.All(x => x == 0)` (1 điều-kiện) + stale-comment fix. `Count==0` guard giữ riêng.
**Tests** +9 (486→495): T1 ngân-sách-bất-biến (`PeWorkItemBudgetTests`) · T2 winnerQuoteTotal signed-sum (`PeMultiWinnerTests`) · T3 submit-guard all-zero/pure-neg/net-zero (`PeSubmitGuardAndBypassTests` + helper ext) · T4 CCM-net-threshold (`PeCcmThresholdFinalizeTests`).
## Lessons
- **Owner bắt mâu-thuẫn giữa 2 decision** (R1⇄R2) mà invest+review đều chưa nối — cho dữ-liệu-âm ở đầu-ra nhưng chặn con-đường tạo ra nó. → engine phải nối các quyết-định, không xét độc-lập.
- **Guard là proxy sai:** `Sum()<=0` ≠ ý-định "chưa báo giá" (= `All(==0)`). Sửa về đúng ý-định giải mâu-thuẫn + đúng ngữ-nghĩa.
- **#53-garble ×19 (fable-real, reasoning-lane):** garble ≠ mất-việc. Recover **transcript-forensics** (thấy nó verify CEO-threshold, chưa kịp verdict) → **em-main-solo gate** vẫn có RĂNG (bắt thêm FE mirror `:235`). `parallel()` + schema-nhỏ → 4/5 workflow 0-garble.
- **verify-deployed-byte-level (#77):** cicd curl bundle prod grep feature-string PRESENT (bundle cũ=0) = ship-proof thật, không cache. + bắt bẫy snapshot-while-CI-running → poll-success trước re-snapshot.
- **FE agent verify-on-disk** sửa spec-shape-error (quotes ở `details[]` không `suppliers[]`) — lý do worker phải verify, không tin spec mù.
## Carry NEXT
- Anh UAT incognito (7 điểm → HANDOFF).
- run-trace + agent-memory `.claude/`-gitignored → force-add @closeout.
- PE "lỗi phân quyền vào phiếu" (chờ số-phiếu+user) · BVAAU (`AUTH_ENFORCE_CRM_GATE`+User_ID) · monthly re-tier 2026-08-01.