[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
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:
@ -52,8 +52,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
// random (test guard không cần drafter trong chuỗi). V2 nếu awId set.
|
||||
private static PurchaseEvaluation BuildPeNhap(
|
||||
Guid? selectedSupplierId = null,
|
||||
Guid? budgetId = null,
|
||||
decimal? budgetManualAmount = null,
|
||||
decimal? budgetPeriodAmount = null,
|
||||
Guid? approvalWorkflowId = null,
|
||||
Guid? drafterUserId = null,
|
||||
string code = "PE-S60-001")
|
||||
@ -67,8 +66,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
ProjectId = Guid.NewGuid(),
|
||||
DrafterUserId = drafterUserId ?? Guid.NewGuid(),
|
||||
SelectedSupplierId = selectedSupplierId,
|
||||
BudgetId = budgetId,
|
||||
BudgetManualAmount = budgetManualAmount,
|
||||
BudgetPeriodAmount = budgetPeriodAmount, // [S61 Mig 50] thay BudgetId/BudgetManualAmount
|
||||
ApprovalWorkflowId = approvalWorkflowId,
|
||||
};
|
||||
}
|
||||
@ -215,7 +213,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
var (svc, fix, db, _) = CreateService();
|
||||
using (fix)
|
||||
{
|
||||
var pe = BuildPeNhap(budgetManualAmount: 500_000m);
|
||||
var pe = BuildPeNhap(budgetPeriodAmount: 500_000m);
|
||||
db.PurchaseEvaluations.Add(pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
SeedComparisonAttachment(db, pe);
|
||||
@ -237,7 +235,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
var (svc, fix, db, _) = CreateService();
|
||||
using (fix)
|
||||
{
|
||||
var pe = BuildPeNhap(budgetManualAmount: 500_000m);
|
||||
var pe = BuildPeNhap(budgetPeriodAmount: 500_000m);
|
||||
db.PurchaseEvaluations.Add(pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
var supplierId = await SeedWinnerWithQuoteAsync(db, pe, quoteThanhTien: 0m); // quote = 0
|
||||
@ -261,7 +259,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
var (svc, fix, db, _) = CreateService();
|
||||
using (fix)
|
||||
{
|
||||
var pe = BuildPeNhap(budgetId: null, budgetManualAmount: 0m);
|
||||
var pe = BuildPeNhap(budgetPeriodAmount: 0m);
|
||||
db.PurchaseEvaluations.Add(pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
var supplierId = await SeedWinnerWithQuoteAsync(db, pe, quoteThanhTien: 1_000_000m);
|
||||
@ -286,7 +284,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
var (svc, fix, db, _) = CreateService();
|
||||
using (fix)
|
||||
{
|
||||
var pe = BuildPeNhap(budgetManualAmount: 500_000m);
|
||||
var pe = BuildPeNhap(budgetPeriodAmount: 500_000m);
|
||||
db.PurchaseEvaluations.Add(pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
var supplierId = await SeedWinnerWithQuoteAsync(db, pe, quoteThanhTien: 1_000_000m);
|
||||
@ -309,7 +307,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
var (svc, fix, db, _) = CreateService();
|
||||
using (fix)
|
||||
{
|
||||
var pe = BuildPeNhap(budgetManualAmount: 500_000m);
|
||||
var pe = BuildPeNhap(budgetPeriodAmount: 500_000m);
|
||||
db.PurchaseEvaluations.Add(pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
var supplierId = await SeedWinnerWithQuoteAsync(db, pe, quoteThanhTien: 1_000_000m);
|
||||
@ -347,7 +345,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
var (svc, fix, db, clock) = CreateService();
|
||||
using (fix)
|
||||
{
|
||||
var pe = BuildPeNhap(budgetManualAmount: 750_000m);
|
||||
var pe = BuildPeNhap(budgetPeriodAmount: 750_000m);
|
||||
db.PurchaseEvaluations.Add(pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
var supplierId = await SeedWinnerWithQuoteAsync(db, pe, quoteThanhTien: 1_000_000m);
|
||||
@ -364,27 +362,9 @@ public class PeSubmitGuardAndBypassTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Submit_AllFourMet_ViaBudgetId_ManualNull_SetsChoDuyet()
|
||||
{
|
||||
// (8) Đủ 4 qua BudgetId (manual null) → OK. Cover nhánh budget thoả qua FK
|
||||
// Budget thay vì manual amount.
|
||||
var (svc, fix, db, _) = CreateService();
|
||||
using (fix)
|
||||
{
|
||||
var pe = BuildPeNhap(budgetId: Guid.NewGuid(), budgetManualAmount: null);
|
||||
db.PurchaseEvaluations.Add(pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
var supplierId = await SeedWinnerWithQuoteAsync(db, pe, quoteThanhTien: 2_000_000m);
|
||||
pe.SelectedSupplierId = supplierId;
|
||||
SeedComparisonAttachment(db, pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
|
||||
await SubmitAsync(svc, pe, Guid.NewGuid());
|
||||
|
||||
pe.Phase.Should().Be(PurchaseEvaluationPhase.ChoDuyet);
|
||||
}
|
||||
}
|
||||
// [S61 Mig 50] Test (8) "đủ 4 qua BudgetId" XÓA — nhánh BudgetId không còn
|
||||
// tồn tại (module Budget cũ drop, predicate (3) chỉ còn BudgetPeriodAmount).
|
||||
// Nhánh thoả-mãn duy nhất đã cover bởi test (7) budgetPeriodAmount > 0.
|
||||
|
||||
// =====================================================================
|
||||
// FEATURE 2 — Drafter-in-chain bypass khi submit (V2-only)
|
||||
@ -397,7 +377,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
TestApplicationDbContext db, Guid workflowId, Guid drafterUserId, string code)
|
||||
{
|
||||
var pe = BuildPeNhap(
|
||||
budgetManualAmount: 1_000_000m,
|
||||
budgetPeriodAmount: 1_000_000m,
|
||||
approvalWorkflowId: workflowId,
|
||||
drafterUserId: drafterUserId,
|
||||
code: code);
|
||||
@ -569,7 +549,7 @@ public class PeSubmitGuardAndBypassTests
|
||||
using (fix)
|
||||
{
|
||||
var drafter = (await fix.CreateUserAsync("v1d@s60.test", "V1 Drafter", null, new[] { AppRoles.Drafter })).Id;
|
||||
var pe = BuildPeNhap(budgetManualAmount: 1_000_000m, approvalWorkflowId: null, drafterUserId: drafter, code: "PE-S60-013");
|
||||
var pe = BuildPeNhap(budgetPeriodAmount: 1_000_000m, approvalWorkflowId: null, drafterUserId: drafter, code: "PE-S60-013");
|
||||
db.PurchaseEvaluations.Add(pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
var supplierId = await SeedWinnerWithQuoteAsync(db, pe, quoteThanhTien: 1_000_000m);
|
||||
|
||||
Reference in New Issue
Block a user