Files
solution-erp/.claude/workflows/runs/2026-08-05-S175-duan-budget-column/spec-hang-muc-nhieu-dong-va-cot-du-an-05-08-2026.md
pqhuy1987 b811e1125d
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m32s
[CLAUDE] Contract: KHKK them N hang muc (gia+ghi chu) + gop muc 3-4 file dinh kem (0-mig)
Nguon: /fable-real investigator-codebase (wf_a574cff1-6b9, 1 lane, 169K tok).
Spec 3-muc: runs/2026-08-05-S175-duan-budget-column/spec-hang-muc-nhieu-dong-va-cot-du-an-05-08-2026.md
Anh chot: "Chi them hang muc -> nhieu hang muc -> gia va ghi chu, vay thoi" + "file 3+4 ->
Hop thanh file dinh kem nhu cua Duyet NCC nhe".

=== 1. THEM N HANG MUC / NCC (0 MIGRATION) ===
🔴 Cau hoi TIEN da co dap an: KHONG co luat chia — NGUOI NHAP TAY tung dong.
Truoc do lead dung lai vi hoi "tach N hang muc thi chia tien NCC the nao"; anh tra loi khong
theo tien, nhap tay. Het chan.

BE — 0 migration (unique 3 cot Plan x Supplier x CatalogEntry co tu Mig 71; may DA rong,
lan nay chi bung UI — dung khuon "may rong, UI hep" anh chot S164):
· UpdateContractSigningPlanLineCommand +ProposedAmount? +Note?, CatalogEntryId -> NULLABLE.
  Null-safe khuon UpdateDraftCommand:559-563 => call-site FE cu (gui dung 1 field) KHONG phai
  sua dong nao.
· AddContractSigningPlanLineCommand (MOI) + route POST {id}/lines.
  🔴 SupplierId PHAI thuoc phieu — NCC den tu winner phieu Duyet NCC; de NCC moi o day = ky
    voi nha thau chua ai chon. Khong thuoc => 409.
  🔴 CatalogEntryId BAT BUOC: unique index CO Y khong loc "IS NOT NULL"
    (ContractSigningPlanLineConfiguration.cs:35-38) => them dong NULL se dung dong goc.
  🔴 PeReferenceAmount = 0, KHONG copy so tham chieu cua NCC. Tham chieu la tong bao gia DUOC
    CHON cua CA NCC, da nam tron o dong goc (Features.cs:512-515). Lap lai o dong them tay =
    DEM DOI tien tham chieu du an. No cung la DAU phan biet dong-goc/dong-them-tay.
· DeleteContractSigningPlanLineCommand (MOI) + route DELETE {id}/lines/{lineId}.
  🔴 CHI xoa duoc dong THEM TAY (PeReferenceAmount == 0). Dong goc mang snapshot tham chieu —
    xoa la mat so goc + mat dau vet NCC tung trung thau.
· Bat TRUNG cap (Plan, Supplier, CatalogEntry) TRUOC SaveChanges => 409 doc duoc, thay vi 500
  unique-violation. Ap cho CA duong them LAN duong doi hang muc (NCC nay co N dong => doi
  trung hang muc dong khac cung NCC la co that).
· Rao ContractId nay che CA TIEN, khong chi hang muc: ProposedAmount la so di vao HD.
· Changelog ghi cap CU->MOI cho tien ("gia de xuat 1.000 -> 2.000 d") — day la tien, nguoi
  duyet phai truy duoc ai doi tu bao nhieu sang bao nhieu, khong chi biet "co sua".
· 3 route deu policy KeHoachKyKet.Update (gotcha #85 — dung key cua endpoint; "tao" o module
  nay nghia la tao PHIEU nen POST line van la Update).

FE: cot GIA DE XUAT + GHI CHU nhap duoc inline (chi Nhap/Tra lai, chi dong chua co HD), luu
onBlur va CHI khi so doi that. Nut "+ Them hang muc" liet ke dung NCC da co tren phieu. Dong
them tay hien "—" o cot tham chieu (khong phai "0 d" — de khong doc nham la tham chieu bang 0).

=== 2. GOP MUC 3 + 4 -> 1 KHOI "FILE DINH KEM" ===
Khuon dong file be tu PE (PeDetailTabs.tsx:3382-3418): 1 danh sach PHANG, moi dong co badge
cho biet file thuoc can cu nao (hoac "Ho so phieu" neu o muc phieu), + eye xem truoc + download.
🔴 Bang can cu 5 cot GO khoi man nhung MAY GIU NGUYEN: entity KhkkDossierItem + 3 endpoint +
changelog con nguyen; kind/status/TVGS/ghi-chu van sua qua nut ✎ (mo dung dialog cu, du 5
truong) va van hien duoi dang badge. Go luon entity de "cho gon giao dien" = mat du-lieu BE
dang ghi va nguoi duyet dang doc.
Can cu DA KHAI ma chua co file van hien 1 dong (dashed) — khong thi khai xong no bien mat va
nguoi dung tuong bam hut.

=== 3. ENGINE BAC 2 GIA-THUYET CUA LEAD (ghi lai vi la SAI CUA LEAD) ===
(a) Lead: "cot DU AN la DI-CHUNG module Budget cu bi Mig 50 rut ruot" — SAI. Cot sinh o
    e33481e 19-06 (Mig 56), SAU Mig 50 (13-06) 6 ngay, va CUNG COMMIT voi comment y-dinh
    PeWorkItemBudget.cs:19 "DU AN: hien thi FE-only (chua wire BE — sau moi co NGUOI DU AN
    NHAP)" => CHO-CHUA-SAN co chu dich, khong phai xac chet.
(b) Lead do "≥3 cho hardcode —" — THIEU. That = 11 o/app (Block A 3 + Block B 8), mirror 2 app.
(c) 🔑 PHAT HIEN: Project.BudgetTotal DA TON TAI tu Mig 2 (day-1), wired tron 2 dau (BE CRUD
    ProjectFeatures.cs + FE cot "Ngan sach" ProjectsPage.tsx:117 + form :205). Chi chua noi vao
    ma tran PE, va DU-LIEU RONG: Dev 61/69 du an NULL (8 cai co so deu la demo-seed gated-OFF
    prod). Rat co the CHINH LA thu anh noi "ben muc ngan sach co muc du an chua dien vao" —
    lop "co-che dung, du-lieu khong co" lan thu 7.

=== CON NO ===
🔴 Cot DU AN (11 o) CHUA LAND — cho anh chot 3 cau trong spec §③: (1) Sigma KHKK do vao O NAO
trong 11 o; (2) phieu DaDuyet thi cot DU AN live hay dong bang; (3) sua thang ProposedAmount
(delta tu-audit = Proposed − PeReference) du chua hay can field "dieu chinh" rieng.
🔴 CHUA soi mat prod 375/1366/1440 (5 lane NO-SCREENSHOT, gotcha #3).
🔸 Test-before cho lop luy-ke la BAT BUOC khi land cot DU AN (financial-critical, rules §7) —
  lan nay chua cham luy-ke nen chua no test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 20:54:15 +07:00

7.2 KiB
Raw Blame History

SPEC — Thêm N hạng mục (giá + ghi chú) & cột DỰ ÁN trong ma trận ngân sách (05-08-2026)

Nguồn: /fable-real investigator-codebase single deep-pass (wf_a574cff1-6b9, 1 lane, 169K tok, 38 tool-use, ~12). Engine propose-only; file này LEAD ghi SAU khi verify (H21 honest-note (c)).


① TÍNH-NĂNG / ĐỀ-BÀI

Anh chốt (verbatim, tin nhắn cuối): "Chỉ thêm hạng mục → nhiều hạng mục → giá và ghi chú, vậy thôi" (kèm ảnh khoanh đỏ mục 2. Giá đề xuất theo NCC trúng thầu, mũi tên chỉ cột HẠNG MỤC).

Phạm vi lượt này = A. Mục B ghi lại để không rơi, chưa land.

A. Dòng hạng mục (LÀM NGAY): 1 NCC được N dòng hạng mục, mỗi dòng nhập tay giá + ghi chú. 🔑 Câu này mở khoá bế tắc trước đó: lead từng dừng vì hỏi "tách N hạng mục thì CHIA tiền NCC thế nào". Câu trả lời là không có luật chia — người nhập tay từng dòng. Hết chặn.

B. Cột DỰ ÁN (CHƯA LÀM — chờ anh chốt 3 câu ở §③): 11 ô trong ma trận ngân sách của phiếu Duyệt NCC, nuôi bằng Σ các dòng hạng mục bên KHKK.

🔴 3 đính-chính engine bắt được — lead ghi lại vì 2 cái đầu là SAI CỦA LEAD

# Lead nói Sự thật đo được Nguồn
a "cột DỰ ÁN là DI-CHỨNG module Budget cũ bị Mig 50 rút ruột" SAI. Cột sinh ở e33481e 19-06 (S76/Mig 56) — SAU Mig 50 (13-06) 6 ngày, và cùng commit với comment ý-định "DỰ ÁN: hiển thị FE-only (chưa wire BE — sau mới có NGƯỜI DỰ ÁN NHẬP)"CHỖ-CHỪA-SẴN có chủ đích, không phải xác chết PeWorkItemBudget.cs:19 + git e33481e
b "cột DỰ ÁN hardcode ở ≥3 chỗ" THIẾU. Thật = 11 ô/app: Block A PeDetailTabs.tsx:1415·1426·1447 + Block B :1535·1589·1599·1619·1629·1639·1664·1677; fe-user mirror y hệt pattern-count 19/19 cả 2 app
c (lead không biết) 🔑 Tầng NS cấp dự-án ĐÃ CÓ MỘT NỬA: Project.BudgetTotal sinh từ Mig 2 — day-1, wired TRỌN 2 đầu (BE CRUD ProjectFeatures.cs:18·82·107·129·154 + FE cột "Ngân sách" ProjectsPage.tsx:117 + form :205). Chỉ chưa nối vào ma trận PE, và DỮ-LIỆU RỖNG: 61/69 dự án NULL, 8 cái có số đều là demo-seed gated-OFF-prod Domain/Master/Project.cs:12 · DbInitializer.cs:3212-3219

🔴 (c) rất có thể CHÍNH LÀ thứ anh nói"bên mục ngân sách có mục dự án chưa điền vào". Ô đó có thật, nhập được, và đang trống ở 61/69 dự án. Đây là lớp "cơ-chế đúng, dữ-liệu không có" lần thứ 7.

🔸 Kết-luận hệ-quả của lead ("phải dựng một tầng, không phải vá 1 cột FE") vẫn đứng — chỉ tiền-đề đổ.


② CÁCH IMPLEMENT

A — Dòng hạng mục N-dòng + giá + ghi chú (lượt này)

BE — 0 migration (unique (PlanId, SupplierId, CatalogEntryId) lọc IsDeleted=0 đã có từ Mig 71):

# Việc Ghi chú
A1 UpdateContractSigningPlanLineCommand +ProposedAmount? +Note?, đổi CatalogEntryIdnullable Null-safe khuôn UpdateDraftCommand:559-563: null = không gửi ⇒ giữ nguyên. Nullable ⇒ call-site FE cũ không phải sửa dòng nào
A2 AddContractSigningPlanLineCommand(PlanId, SupplierId, CatalogEntryId, ProposedAmount, Note) 🔴 SupplierId PHẢI ∈ NCC đã có trên phiếu — NCC đến từ winner của PE, không được đẻ NCC mới ở đây. 🔴 CatalogEntryId BẮT BUỘC: unique index cố ý không lọc NULL ⇒ thêm dòng NULL sẽ đụng dòng gốc
A3 DeleteContractSigningPlanLineCommand(PlanId, LineId) 🔴 CHỈ xoá được dòng THÊM TAY (PeReferenceAmount == 0). Dòng gốc sinh từ PE mang snapshot tham chiếu — xoá là mất số gốc
A4 3 route: POST {id}/lines · DELETE {id}/lines/{lineId} · mở rộng body PUT {id}/lines/{lineId} policy KeHoachKyKet.Update (gotcha #85: đúng key endpoint)

🔴 PeReferenceAmount của dòng THÊM TAY = 0, KHÔNG copy số NCC. Tham chiếu là Σ báo giá được chọn của cả NCC, đã nằm trọn ở dòng gốc; lặp lại là đếm đôi tiền dự án.

FE (mirror 2 app): mục 2 thêm nút + Thêm hạng mục theo NCC · ô nhập giá đề xuất + ghi chú inline (chỉ phase Nháp/Trả lại) · nút xoá cho dòng thêm tay.

B — Cột DỰ ÁN (CHƯA LÀM)

Phương án engine chốt = live-derive, 0 migration, 0 endpoint mới: JOIN Lines → Plan WHERE Plan.ProjectId = pe.ProjectId, 2 tầng mirror S134 (CHÍNH-XÁC = Σ ApprovedAmount plan DaDuyet ⟂ TẠM-TÍNH = Σ ProposedAmount plan đang mở). Đã BÁC: ledger-table riêng (+1 mig, số derive được lại đi lưu) · cột DuAn* trên PeWorkItemBudgets (sai trục khoá — bảng per-WorkItemId mà chứa số per-Project; và CatalogEntryId SP-002 không map được WorkItemId).

🔴 Rào tiền phải xử khi land B: nhánh frozen PurchaseEvaluationFeatures.cs:946-959 dựng DTO bằng tay — quên field mới thì phiếu DaDuyet hiện DỰ ÁN = 0 câm lặng (đúng lớp "vắng mặt trông giống ổn").


③ CHECKLIST — acceptance ĐO ĐƯỢC

# Deliverable Acceptance
1 A1 land grep -c 'ProposedAmount' ContractSigningPlanFeatures.cs tăng; call-site FE cũ (catalogEntryId đơn lẻ) vẫn build
2 A2 rào NCC POST với supplierId KHÔNG thuộc phiếu → 409, message nêu rõ
3 A2 rào trùng POST trùng cặp (Plan, Supplier, CatalogEntry) → 409 chứ không phải 500 unique-violation
4 A3 rào dòng gốc DELETE dòng có PeReferenceAmount != 0409
5 Tiền không đếm đôi Dòng thêm tay có PeReferenceAmount == 0 — verify bằng đọc DB/DTO sau khi tạo
6 Mirror sha256sum từng cặp file FE 2 app — khớp 100%
7 Build npm run build ×2 app + dotnet build — 0 error. 🔴 Đo NỘI DUNG bundle, đừng tin thời-gian build
8 Deploy bundle prod 2 vế: chuỗi mới ∧ chuỗi cũ vắng, kèm control-dương. Dùng grep -o | wc -l, KHÔNG grep -c (bundle 1 mega-dòng)
9 🔴 Mắt anh Soi thật 375 / 1366 / 1440 trên prod — mọi kết luận giao diện tới giờ là ĐỌC MÃ

🔴 3 câu treo — cần anh chốt TRƯỚC khi land mục B

  1. Σ KHKK đổ vào Ô NÀO trong 11 ô? (engine đề xuất: hàng 5 lũy kế Block B, 2 tầng chính-xác + tạm-tính khuôn amber S134; còn hàng NS full Block A đọc Project.BudgetTotal)
  2. Phiếu đã duyệt: cột DỰ ÁN live hay đóng băng theo snapshot?
  3. Sửa thẳng ProposedAmount (delta tự-audit = ProposedAmount PeReferenceAmount) là đủ, hay cần field "điều chỉnh" riêng?