wal: flush 20260714T1100

This commit is contained in:
pqhuy1987
2026-07-14 11:00:09 +07:00
parent 940db93309
commit 625169dce8
2 changed files with 41 additions and 0 deletions

View File

@ -101,6 +101,9 @@ Bearer từ `POST api.solutions.com.vn/api/auth/login` → status matrix expecte
---
## S118 — PE giá-chốt RANGE Min+Max invest (fable-real single, 07-14)
- ApprovedPrice WRITE = 1 site duy nhất `ApplyApprovedPriceOnFinalize` (`PurchaseEvaluationWorkflowService.cs:951-966`, allow-list :950) gọi tại 3 nhánh DaDuyet :858 level-finalize / :896 ccm-delegation / :928 all-steps-done (skipToFinal :820 chỉ dời pointer, KHÔNG finalize-site thứ 4). READ prod = detail-DTO ONLY (`PurchaseEvaluationFeatures.cs:1230``PurchaseEvaluationDtos.cs:285`) + FE display `PeDetailTabs.tsx:1712/:1850-1863`. ⭐ **HĐ-gen KHÔNG đọc ApprovedPrice** — GiaTri=SUM Quote.IsSelected per-winner (`CreateContractFromEvaluationFeatures.cs:88-100` LOCKED A1/D4) → range 0-impact contract/lũy-kế/list. Rec model A: +1 col nullable `ApprovedPriceMaxAmount` + source token `ProMinMax` = radio thứ-5 (chỉ khi cả Min+Max present), no backfill, phiếu cũ nguyên. PRO-entry đã enforce Min≤Max (`PeSuggestedPriceFeatures.cs:38-40`). Panel+DetailTabs SHA-mirror 2-app NHƯNG `types/purchaseEvaluation.ts` KHÔNG mirror (SHA differ) — edit riêng từng app. Latent edge: winnerQuoteTotal subset-selected có thể ÂM (S116) vs validator `ApprovedPriceAmount>=0` `:501` → transition reject khó hiểu. WIP S117 uncommitted CÙNG file PurchaseEvaluationFeatures.cs (endsBeforeCeo pointer-aware :642/:768/:1157) — vùng khác, merge-safe. `[s118, pe-approved-price-range, prominmax]`
## S117 — PE lũy-kế "không bắt sau duyệt" RCA (fable-real single deep-pass)
- **2026-07-14** VERDICT: phiếu nguồn A/018 KHÔNG hề DaDuyet — Chương (Bước2 Cấp2 `AllowApproverFinalize`, badge "Duyệt thay CEO" = CONFIG-display `PeWorkflowPanel.tsx:364`) duyệt KHÔNG tick ô OPT-IN "Cấp này KẾT THÚC" (S97 Part1 đảo default UNCHECKED `PeWorkflowPanel.tsx:57` useState(false); FE gửi tường minh :203) → advance thường lên Bước3 CEO, Phase=10 ChoDuyet. Prod ground-truth: StepIdx=2 LvlOrd=1 EndedFin=0; changelog 13/07 12:23Z "Hoàn tất Bước 2/3, sang Bước 3 (Cấp 1)" no-finalize-marker; IsWinner=1 SelQ=1 (749.564M) → RC-2 refuted. Lũy-kế Row2 filter `Phase==DaDuyet` (`PeFeatures.cs:906`) ĐÚNG nguyên tắc Kenny "duyệt rồi mới bắc"; tính LIVE :886-918 no-store → xóa+tạo-lại phiếu-XEM vô ích. ⭐ Residual = DISPLAY-LIE nửa-còn-lại gotcha #78: non-DaDuyet `endsBeforeCeo`=config TRUE bất chấp pointer (detail `:1152-1154` literal `: true`; list `:647-651` subquery `.Any()`) → Bước3 mờ "Kết thúc tại Cấp 2" + chip list "⚑ Kết thúc·CCM" NGAY KHI phiếu nằm inbox CEO → user tưởng xong. 3 cơ chế chồng phân biệt: AllowApproverFinalize (Mig58 + opt-in S97) ≠ finalizeByCcmDelegation (Mig53/54 threshold — v04 CeoThresh=NULL không dùng) ≠ skipToFinal (Mig31 pointer-jump giữ ChoDuyet). Fix rec: vận-hành CEO-duyệt hoặc trả-lại→Chương tick; code = pointer-aware endsBeforeCeo (detail in-memory trivial, list EF-subquery kềnh), KHÔNG nới filter lũy kế. `[engine: fable-real-single · Fable-S117]`

View File

@ -0,0 +1,38 @@
# Spec — PE duyệt cấp cuối: chọn CẢ Min và Max (duyệt theo khoảng)
> `/fable-real investigator-codebase` engine-đắt deep-pass — run 2026-07-14 (S117 task 2). Model lane: fable (0-garble, clean verdict). Lead verify: HĐ-gen linchpin CONFIRMED độc-lập (`CreateContractFromEvaluationFeatures.cs:88-90` GiaTri=SUM selected NCC quotes, grep ApprovedPrice=0-match). Owner: "làm theo hướng dẫn của nó luôn".
## ① Tính-năng / đề-bài
anh Kiệt FDC: ô "CHỌN GIÁ CHỐT (DUYỆT THEO GIÁ ĐỀ XUẤT)" khi duyệt cấp cuối hiện radio Ncc/ProMin/ProMax/Ccm — chọn ĐÚNG 1. Yêu cầu: **khi PRO có cả Min & Max → cho chọn CẢ HAI (duyệt theo khoảng [Min,Max])**. "Chọn cả 2 min và max."
## ② Cách implement — MODEL A (investigator khuyến nghị, verify OK)
Thêm nullable `ApprovedPriceMaxAmount` + source token `"ProMinMax"` làm **radio thứ 5** (chỉ hiện khi PRO có CẢ Min+Max). Bất-biến: `ApprovedPriceMaxAmount != null ⟺ ApprovedPriceSource == "ProMinMax"` (Amount=Min, Max=Max). Source đơn giữ nguyên (Max=null). **1 migration AddColumn nullable, no new table, NO backfill** (phiếu cũ nguyên vẹn). **HĐ.GiaTri KHÔNG đổi** (không đọc giá chốt) — range thuần record-of-decision hiển thị.
**BE:**
- Entity `PurchaseEvaluation.cs:97-98` +`decimal? ApprovedPriceMaxAmount`.
- Config `PurchaseEvaluationConfiguration.cs` +`HasPrecision(18,2)`.
- Migration `AddPeApprovedPriceRange` (Mig 66) 3-file.
- Service `PurchaseEvaluationWorkflowService.cs`: `ValidApprovedPriceSources:950` +`"ProMinMax"`; `ApplyApprovedPriceOnFinalize:951-966` +param `decimal? maxAmount` (ProMinMax⇒require maxAmount≠null && amount≤maxAmount; source-đơn+maxAmount≠null⇒Conflict wire-bug; isSystem+null giữ); 3 call-site :858/:896/:928 + `ApproveV2Async:650-661` sig + `TransitionAsync:47-50` sig + pass-through :291.
- Interface `IPurchaseEvaluationWorkflowService.cs:33-34` +`approvedPriceMaxAmount=null`.
- `PurchaseEvaluationFeatures.cs`: Command +field; Validator (+ProMinMax vào Must; +Max≥0; +cross-rule Max⟺ProMinMax; Min≤Max); Handler pass; projection GetDetail +`e.ApprovedPriceMaxAmount`.
- Controller `PurchaseEvaluationsController.cs`: `TransitionPeBody` +field; Send +field.
- DTO `PurchaseEvaluationDtos.cs:285` +`ApprovedPriceMaxAmount`.
**FE (2-app):**
- `types/purchaseEvaluation.ts` (⚠️ 2 app KHÔNG identical — sửa riêng) +`approvedPriceMaxAmount`.
- `PeWorkflowPanel.tsx:134-141`: +candidate `{source:'ProMinMax', label:'PRO — cả Min và Max (duyệt theo khoảng)', amount:min, amountMax:max}` chỉ khi cả 2≠null; GIỮ radio; +`selectedPriceAmountMax`; submit +`approvedPriceMaxAmount`; render "min max".
- `PeDetailTabs.tsx:1661-1666` +`ProMinMax:'PRO MinMax'`; :1850-1863 khi Max≠null render `min max`.
## ③ Checklist (financial-critical → test-before merge, rules §7)
- [ ] Entity + config + migration 3-file → ef update Dev.
- [ ] Service allow-list + helper +param + 3 call-site + 2 sig + interface.
- [ ] Command/Validator/Handler + Controller body + DTO + projection.
- [ ] Tests (`PeApprovedPriceFinalizeTests` mirror): (a) ProMinMax happy set 2 field; (b) thiếu maxAmount→Conflict not-finalized; (c) min>max→Conflict; (d) source-đơn+maxAmount→Conflict; (e) 4 source đơn regression pass. `dotnet test`.
- [ ] FE types ×2 → Panel ×2 mirror → DetailTabs ×2 mirror → npm build ×2 + SHA-check Panel/Tabs.
- [ ] Smoke: duyệt cuối chọn range → detail "min max"; phiếu cũ nguyên.
## Product-notes (resolved theo owner "làm theo hướng dẫn")
- HĐ.GiaTri khi range = GIỮ NGUYÊN (SUM NCC quotes, không đọc giá chốt). Cảnh-báo-mềm GiaTri ngoài [Min,Max] = **out-of-scope** (feature riêng, flag cho anh Kiệt).
- Range dùng chung 3 finalize path (level-finalize/ccm-delegation/all-steps) — tự có vì chung helper, OK.
- UI = 1 radio "cả Min và Max" (khớp "chọn cả 2 min và max"), KHÔNG checkbox combo.
- Latent (pre-existing, ghi nhận không sửa): winnerQuoteTotal âm S116 → validator ApprovedPriceAmount≥0 reject — ngoài scope.