[CLAUDE] PurchaseEvaluation: ngan sach goi thau theo Excel anh Kiet - bang tong hop 2 block + nhap theo role PRO/CCM + xoa module Budget cu (Mig 50)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m31s

- Mig 50 ReplaceBudgetModuleWithPeWorkItemBudgets: bang moi PeWorkItemBudgets (1 record/cap Du an x Hang muc, UNIQUE filtered [IsDeleted]=0) + drop 5 bang Budget cu + PE/Contracts drop BudgetId + backfill BudgetManualAmount->BudgetPeriodAmount TRUOC DropColumn (phieu UAT giu so) + DELETE menu/permission Bg_* IN-list children-first
- BE: PUT {id}/budget/pro (role Procurement) + {id}/budget/ccm (role CostControl, Adjustment cho phep AM) fail-closed Forbidden-truoc-side-effect + EnsureTrackedAsync race-safe (catch unique -> re-fetch winner, loi khac rethrow) + auto-create record khi tao phieu + budgetSummary DTO (luy ke trinh-truoc/chon-thau-truoc/de-xuat-ky-nay + full fallback du-tru-PRO + canEdit flags) + submit-guard (3) doi predicate BudgetPeriodAmount -> "chua nhap Ngan sach ky nay" + PATCH budget-adjust absolute-set 2 field moi + Contract GIU BudgetManual* (HD nhap tay khong doi) + ke thua HD map BudgetPeriodAmount
- FE x2 app SHA256 identical: bang "TONG HOP NGAN SACH TRINH KY" block A (full dam + ban hanh + V0 hieu chinh + du tru PRO + ghi chu, editable theo canEditPro/canEditCcm) + block B 9 dong cong thuc Excel (5=1+3, 6=2+4, 7=full-5, 8 tu nhap default 7, 9=4+8) + to mau vuot ngan sach #C00000 / am do / red-soft row8>row7 + "Chua chon" khi count=0 + banner phieu chua gan Hang muc + o "Ngan sach ky nay" o create/header + XOA pages/components/types budgets + routes + menuKeys + Layout staticMap 4-place
- Tests: +22 PeWorkItemBudgetTests (auto-create x3, ensure/race x2, authz matrix PRO x5 + CCM x3, budgetSummary aggregates x5, adjust x4) - 14 BudgetPolicyTests xoa theo module - 1 test via-BudgetId -> 263 PASS (45 Domain + 218 Infra, 0 fail)
- database-agent advise adopted: khong FK vat ly PE/Contracts->Budgets (DropColumn khong can DropForeignKey) + DropIndex truoc DropColumn (SQL 5074) + IN-list thay LIKE Bg_% (underscore wildcard + miss root) + khong Serializable wrap (nested-tx conflict codegen)
- Reviewer PASS-with-minor 0 blocker (verdict-first survived); 2 minor da sua truoc commit (comment adjustMut absolute-set + dead key budgetId); note: F4 approver-edit-budget UI entry tam drafter-only, BE van cho approver scope - cho UAT anh Kiet
- Scaffold-bug caught: EF tu sinh RenameColumn BudgetManualAmount->ExpectedRemainingAmount (SAI semantics) -> thay bang Add+UPDATE+Drop

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-06-13 01:07:27 +07:00
parent 6db195dd42
commit 79ef8da9f4
70 changed files with 9052 additions and 5956 deletions

View File

@ -0,0 +1,31 @@
using SolutionErp.Domain.Common;
namespace SolutionErp.Domain.PurchaseEvaluations;
// [S61 2026-06-12] Ngân sách gói thầu theo cặp (Dự án, Hạng mục công việc) —
// thay module Budget cũ (5 bảng, xóa cùng Mig 50). 1 record DUY NHẤT per cặp
// (UNIQUE composite filtered [IsDeleted] = 0 — gotcha #57), mọi phiếu PE cùng
// cặp dùng chung. Auto-create khi tạo phiếu PE đầu tiên của cặp.
//
// Loose-Guid convention PE (giống PE.ProjectId/WorkItemId/SelectedSupplierId):
// KHÔNG FK vật lý, KHÔNG navigation property.
//
// Quyền nhập theo ROLE (anh Kiệt chốt S61):
// - PRO (Procurement): ProEstimateAmount (dự trù lần đầu) + ProNote.
// - CCM (CostControl): InitialAmount (Ban hành lần đầu) + AdjustmentAmount
// (NS V0 hiệu chỉnh tăng/giảm — cho phép ÂM).
//
// "Ngân sách full gói thầu" KHÔNG lưu cột — BE compute:
// full = (InitialAmount ?? 0) + (AdjustmentAmount ?? 0);
// cả Initial + Adjustment đều null → fallback ProEstimateAmount ?? 0
// với cờ fullIsEstimate=true (FE badge "dự trù PRO").
public class PeWorkItemBudget : AuditableEntity
{
public Guid ProjectId { get; set; } // loose-Guid Projects.Id
public Guid WorkItemId { get; set; } // loose-Guid WorkItems.Id
public decimal? ProEstimateAmount { get; set; } // PRO dự trù lần đầu (đ)
public string? ProNote { get; set; } // "Ghi chú từ PRO"
public decimal? InitialAmount { get; set; } // CCM "Ngân sách Ban hành lần đầu" (đ)
public decimal? AdjustmentAmount { get; set; } // CCM "NS V0/hiệu chỉnh tăng giảm" (đ, cho phép ÂM)
}

View File

@ -28,15 +28,16 @@ public class PurchaseEvaluation : AuditableEntity
public string? PaymentTerms { get; set; } // JSON {tamUng, thanhToanTam, quyetToan, baoHanh, hanMucCongNo, danhGia}
public Guid? ContractId { get; set; } // FK Contracts — set khi user gen HĐ từ phiếu
public Guid? BudgetId { get; set; } // FK Budget (Phase 7) — đối chiếu báo giá vs ngân sách gói thầu
// Manual budget fields (Phase 9 — Migration 17): user nhập tay khi KHÔNG link
// Budget entity (vd ngân sách chưa approve hoặc gói nhỏ ko cần workflow ngân
// sách riêng). Cả 2 cùng null OK (PE chưa có ngân sách gì cả). KHÔNG validate
// XOR với BudgetId — tạm thời cho phép cả 2 cùng có (BE prefer BudgetId nếu
// set vì có Phase=DaDuyet guarantee, manual chỉ là fallback hiển thị).
public string? BudgetManualName { get; set; } // Tên tham chiếu vd "Tạm tính dự toán T11/2025"
public decimal? BudgetManualAmount { get; set; } // Tổng số tiền nhập tay (đ)
// [S61 Mig 50] Ngân sách PE theo Excel anh Kiệt — thay BudgetId/BudgetManual*
// cũ (drop cùng Mig 50, data BudgetManualAmount migrate → BudgetPeriodAmount).
// - BudgetPeriodAmount: "Ngân sách - kỳ này" (row 3) — NGƯỜI SOẠN nhập khi
// soạn phiếu. Submit guard yêu cầu > 0.
// - ExpectedRemainingAmount: "Giá trị thực hiện dự kiến còn lại" (row 8) —
// tự nhập; null = FE default theo NS còn lại (row 7).
// Ngân sách FULL gói thầu nằm ở PeWorkItemBudget (per cặp Project × WorkItem).
public decimal? BudgetPeriodAmount { get; set; }
public decimal? ExpectedRemainingAmount { get; set; }
// Smart reject (Phase 9 — Migration 16): Phase nguồn khi reject. Drafter
// sửa lại + trình lại → quay về RejectedFromPhase thay vì đi tuần tự.