[CLAUDE] PurchaseEvaluation: ô "Cấp này KẾT THÚC quy trình" → opt-out bỏ-tick-được (anh Kiệt FDC)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m12s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m12s
anh Kiệt UAT "cái này option thôi chứ e" — cấp cấu hình AllowApproverFinalize (Mig 58, S89 auto-finalize im lặng) NAY là ô-tích BỎ được: - Default checked = giữ hành vi cũ (duyệt xong KẾT THÚC, không lên CEO) — backward-compat. - BỎ tick (applyLevelFinalize=false) → KHÔNG finalize, trình tiếp CEO/cấp sau; giá chốt ẩn (CEO chọn sau). BE: +param applyLevelFinalize (default true) xuyên interface → service (finalize guard `&& applyLevelFinalize`) → command → handler → controller body. FE: ô xanh static → checkbox 2 app (fe-admin+fe-user SHA-mirror) + hint động + sendPrice/shouldPickPrice gate theo cờ. Test: +test #6 opt-out→advance-to-CEO (spec change; #1-5 backward-compat giữ). Build slnx + 2 FE PASS 0-err; test 434→435. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -12,8 +12,11 @@ using SolutionErp.Infrastructure.Tests.Common;
|
||||
namespace SolutionErp.Infrastructure.Tests.Services;
|
||||
|
||||
// ===== item 4 (Mig 58 2026-06-24 — CCM/anh Kiệt FDC) — PER-LEVEL finalize "Duyệt là
|
||||
// KẾT THÚC, không trình CEO" — AUTO theo cấu hình quy trình (anh Kiệt chốt 2026-06-24
|
||||
// "cho bật trong quy trình hay hơn") — test theo CODE (S34 rule, KHÔNG touch production).
|
||||
// KẾT THÚC, không trình CEO" — test theo CODE (S34 rule, KHÔNG touch production).
|
||||
// ⚠️ [S96 2026-07-01 SPEC CHANGE — anh Kiệt FDC "cái này option thôi"] Cấp cấu hình
|
||||
// AllowApproverFinalize NAY là OPT-OUT: mặc-định vẫn auto-finalize (applyLevelFinalize=true,
|
||||
// backward-compat test #1-5) NHƯNG người duyệt BỎ tick (applyLevelFinalize=false) → KHÔNG
|
||||
// finalize, trình tiếp CEO (test #6). Đảo "AUTO im lặng 2026-06-24" → opt-out có thể hủy.
|
||||
//
|
||||
// SPEC item 4 (AUTO, KHÔNG ô-tích runtime): ApprovalWorkflowLevel +AllowApproverFinalize
|
||||
// (bool, Mig 58). Trong ApproveV2Async, SAU opinion UPSERT + skipToFinal, TRƯỚC
|
||||
@ -105,12 +108,14 @@ public class PeApproverFinalizeTests
|
||||
return wf;
|
||||
}
|
||||
|
||||
// AUTO model: KHÔNG còn finalizeByApprover param — finalize tự chạy theo cờ cấp.
|
||||
// [S96 opt-out] Cấp cấu hình KẾT THÚC NAY người duyệt BỎ tick được: applyLevelFinalize=false
|
||||
// → KHÔNG finalize, trình tiếp CEO. Default true = giữ auto cũ (test #1-5 backward-compat).
|
||||
private static Task ApproveAsync(
|
||||
PurchaseEvaluationWorkflowService svc, PurchaseEvaluation pe, Guid actorUserId,
|
||||
string[] roles,
|
||||
decimal? approvedPriceAmount = null,
|
||||
string? approvedPriceSource = null) =>
|
||||
string? approvedPriceSource = null,
|
||||
bool applyLevelFinalize = true) =>
|
||||
svc.TransitionAsync(
|
||||
evaluation: pe,
|
||||
targetPhase: PurchaseEvaluationPhase.ChoDuyet, // approve-in-place
|
||||
@ -120,6 +125,7 @@ public class PeApproverFinalizeTests
|
||||
comment: null,
|
||||
approvedPriceAmount: approvedPriceAmount,
|
||||
approvedPriceSource: approvedPriceSource,
|
||||
applyLevelFinalize: applyLevelFinalize,
|
||||
ct: CancellationToken.None);
|
||||
|
||||
// =====================================================================
|
||||
@ -316,4 +322,48 @@ public class PeApproverFinalizeTests
|
||||
approvals.Should().HaveCount(1, "1 lần duyệt = 1 Approval row, không double");
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// 6. ⭐ OPT-OUT (S96 2026-07-01 — anh Kiệt FDC "cái này option thôi") — slot
|
||||
// AllowApproverFinalize=TRUE NHƯNG người duyệt BỎ tick (applyLevelFinalize=FALSE)
|
||||
// → KHÔNG finalize, advance bình thường lên CEO (Step 2). Đảo AUTO (test #1): cùng
|
||||
// cấu hình cấp kết-thúc, chỉ khác cờ opt-out → KHÔNG DaDuyet, KHÔNG cần giá chốt.
|
||||
// =====================================================================
|
||||
[Fact]
|
||||
public async Task ApproveV2_LevelAllowsFinalize_ApproverUnchecks_AdvancesToCeo_NotFinalized()
|
||||
{
|
||||
var (svc, fix, db, clock) = CreateService();
|
||||
using (fix)
|
||||
{
|
||||
// Bước 1 Cấp 1 = approver (AllowApproverFinalize=TRUE) — nhưng người duyệt BỎ tick.
|
||||
// Bước 2 Cấp 1 = CEO (LẦN NÀY phải tới được vì bỏ tick kết-thúc).
|
||||
var approver = (await fix.CreateUserAsync("appr6@af.test", "Approver", null, Array.Empty<string>())).Id;
|
||||
var ceo = (await fix.CreateUserAsync("ceo6@af.test", "CEO", null, new[] { AppRoles.Director })).Id;
|
||||
|
||||
var wf = await SeedWorkflowAsync(db, new[]
|
||||
{
|
||||
new[] { (approver, true) }, // slot CẤU HÌNH kết thúc — nhưng opt-out
|
||||
new[] { (ceo, false) }, // CEO Step 2 — phải tới được
|
||||
});
|
||||
|
||||
var pe = BuildPeAtApprovalSlot(wf.Id, stepIdx: 0, levelOrder: 1, code: "PE-AF-006");
|
||||
db.PurchaseEvaluations.Add(pe);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
|
||||
// ⭐ BỎ tick "Cấp này KẾT THÚC" → applyLevelFinalize=false. KHÔNG cần giá chốt (chưa terminal).
|
||||
await ApproveAsync(svc, pe, approver, Array.Empty<string>(), applyLevelFinalize: false);
|
||||
|
||||
pe.Phase.Should().Be(PurchaseEvaluationPhase.ChoDuyet,
|
||||
"bỏ tick KẾT THÚC → KHÔNG finalize, advance bình thường lên CEO");
|
||||
pe.CurrentWorkflowStepIndex.Should().Be(1, "advance sang Bước 2 (CEO) vì opt-out");
|
||||
pe.CurrentApprovalLevelOrder.Should().Be(1);
|
||||
pe.SlaDeadline.Should().Be(clock.UtcNow.AddDays(7), "SLA reset cho CEO");
|
||||
pe.ApprovedPriceAmount.Should().BeNull("opt-out → chưa terminal → chưa bind giá chốt");
|
||||
|
||||
// approver VẪN ghi opinion (đã duyệt cấp mình), CEO chưa.
|
||||
var opinions = await db.PurchaseEvaluationLevelOpinions
|
||||
.Where(o => o.PurchaseEvaluationId == pe.Id).ToListAsync();
|
||||
opinions.Should().ContainSingle("approver ký cấp mình rồi mới đẩy lên CEO");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user