[CLAUDE] PurchaseEvaluation Tests: Chunk N1+N2 — HOTFIX per-NV lookup site discrimination Allow* flag (BE bug 2 ngày prod)
Bro UAT 2026-05-15 screenshot phát hiện: Admin Designer tick TRUE 7 flag cho
NV Test (UAT V2) slot Bước 2 Cấp 1 (4 NV cùng Cấp, OR-of-N Mig 29). Actor
login → dialog ✓ Duyệt KHÔNG có checkbox F2 skipToFinal + dialog ← Trả lại
CHỈ 1 radio Drafter + KHÔNG có F3+F4 Edit options.
Investigator audit confirm Hypothesis B: BE handler
`PurchaseEvaluationFeatures.cs:765` `FirstOrDefault(l => l.Order ==
curLevelOrder)` THIẾU discriminator `ApproverUserId == currentUser.UserId`.
Schema Mig 29 (S21 t5 2026-05-13) refactor: 1 row per ApproverUserId, OR-of-N
cùng Order → handler luôn lấy row đầu DB (Lê Văn Bính / Trần Xuân Lưu —
chỉ Drafter flag), bỏ qua admin tick per-NV của actor thật.
Bug PRESENT từ Mig 29 deploy 2026-05-13 (2 NGÀY PROD) nhưng chỉ bộc lộ khi
lần đầu admin tick selectively per-NV. Trước đây tất cả slot FALSE → mọi
actor đều thấy "không có options", behavior giống nhau, không lộ.
Cumulative gap analysis: Mig 29 + Mig 30 + Mig 31 wire 8 surface points đúng
nhưng MISS point 9 lookup discrimination → 3× refactor cùng bug. Point 9
mới được catch Plan N S23 t4 (em main + Reviewer + Implementer all MISS
xuyên 3 plan).
N1 BE fix (5 LOC line 765-779):
```csharp
var curLevel = curStep?.Levels.FirstOrDefault(l =>
l.Order == curLevelOrder && l.ApproverUserId == currentUser.UserId)
?? curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder); // admin/non-approver fallback
```
N2 Regression test (new file `GetPurchaseEvaluationCurrentLevelOptionsTests.cs`):
- `GetPe_PerNvLookup_ActorMatchesSlot_ReturnsActorSpecificFlags`:
Seed 4 Level cùng Order=1 (mỗi Level distinct flag profile) × 4 actor →
assert mỗi actor nhận flag riêng (KHÔNG profile khác). Critical assertion:
Actor C → AllowApproverSkipToFinal=true (bug bro UAT regression).
- `GetPe_PerNvLookup_AdminNonApprover_FallsBackToFirstRow`:
Admin actor (NON-match) → fallback FirstOrDefault EF SQLite non-deterministic
→ weak assert NOT null + match exactly 1 of 4 distinct profile.
Pattern reusable saved memory `feedback_per_nv_permission_scope.md` CRITICAL
HOTFIX S23 t4 section:
- Wire checklist 9 surface points (NOT 8 — thêm point 9 lookup discrimination)
- Audit cho future flag F5+: grep `FirstOrDefault.*Order ==` enumerate all
lookup sites, verify discriminator role-context
Verify:
- dotnet build src/Backend/SolutionErp.Application clean (0 warning, 0 error)
- dotnet test SolutionErp.slnx **108/108 PASS** (+2 từ 106: 58 Domain + 50 Infra)
- N2 2 test individual PASS
Pending Chunk N4: docs + memory update commit + push remote.
Pending CICD Monitor post-deploy verify (spawn sau push).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -757,12 +757,21 @@ public class GetPurchaseEvaluationQueryHandler(
|
||||
// Mig 29 (S21 t5) + Mig 30 (S22+5) + Mig 31 (S23 t1) — Resolve
|
||||
// Cấp hiện tại + populate 7 Allow* flag của slot Approver đang
|
||||
// duyệt. Null nếu pointer chưa init.
|
||||
//
|
||||
// Plan N S23 t4 (2026-05-15) — Per-NV lookup site MUST discriminate
|
||||
// theo ApproverUserId. Schema Mig 29: OR-of-N approvers cùng Cấp =
|
||||
// N rows cùng Order, mỗi row có Allow* riêng. Lookup KHÔNG
|
||||
// discriminate → luôn lấy row đầu DB, bỏ qua admin tick per-NV.
|
||||
if (e.CurrentWorkflowStepIndex is int curStepIdx
|
||||
&& curStepIdx >= 0 && curStepIdx < aw.Steps.Count
|
||||
&& e.CurrentApprovalLevelOrder is int curLevelOrder)
|
||||
{
|
||||
var curStep = aw.Steps.OrderBy(s => s.Order).Skip(curStepIdx).FirstOrDefault();
|
||||
var curLevel = curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder);
|
||||
// Match actor.UserId trong slot (per-NV admin opt-in flag).
|
||||
// Admin / non-approver fallback row đầu (giữ behavior view detail).
|
||||
var curLevel = curStep?.Levels.FirstOrDefault(l =>
|
||||
l.Order == curLevelOrder && l.ApproverUserId == currentUser.UserId)
|
||||
?? curStep?.Levels.FirstOrDefault(l => l.Order == curLevelOrder);
|
||||
if (curLevel is not null)
|
||||
{
|
||||
currentLevelOptions = new ApprovalWorkflowOptionsDto(
|
||||
|
||||
Reference in New Issue
Block a user