From 424131d0b1237980924771418ad005a359e797d7 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Fri, 19 Jun 2026 15:22:44 +0700 Subject: [PATCH] [CLAUDE] PurchaseEvaluation: chuong bao nguoi duyet khi phieu vao cap cua ho (submit + moi cap) Tra Sol (Zalo): 'khong thay chuong bao - viec co ho so can duyet'. PE workflow truoc chi notify DRAFTER luc terminal; nguoi DUYET khong duoc bao khi phieu toi cap cua ho. Fix: block trong LogTransitionAsync (sau drafter-notify) - khi toPhase==ChoDuyet, resolve approver cap hien tai (workflow.Steps[CurrentWorkflowStepIndex].Levels Order==CurrentApprovalLevelOrder -> ApproverUserId OR-of-N, exclude actor) -> NotifyManyAsync Generic 'Phieu can ban duyet'. Fire moi luc phieu vao cap ChoDuyet: submit (cap 1) + moi approve-advance (cap ke) - 5 call-site advance deu log toPhase=ChoDuyet sau khi set pointer (verified). Best-effort try/catch (notify fail KHONG rollback transition). V2-only guard. Bell render Generic san (urgent->CEO da chung minh). BE-only, no-mig. Build slnx 0/0, test 354 PASS (khong vo transition cu). Notify = test-after (UAT). Co-Authored-By: Claude Opus 4.8 --- .../PurchaseEvaluationWorkflowService.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs b/src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs index a8aebc0..96236ba 100644 --- a/src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs +++ b/src/Backend/SolutionErp.Infrastructure/Services/PurchaseEvaluationWorkflowService.cs @@ -1055,6 +1055,54 @@ public class PurchaseEvaluationWorkflowService( refId: evaluation.Id, ct: ct); } + + // ===== Notify approver(s) cấp hiện tại khi phiếu vào ChoDuyet ===== + // Tra Sol (Zalo): approver KHÔNG nhận chuông "có hồ sơ cần duyệt" — chỉ + // drafter được báo (block ↑). Bổ sung: mỗi lần phiếu ENTER hoặc ADVANCE + // tới 1 Cấp duyệt (toPhase==ChoDuyet) → báo NV Cấp đang chờ. + // Resolve mirror ĐÚNG canonical (ApplyDrafterBypassOnSubmit / EnsureActor + // line ~301): load ApprovalWorkflow → Step tại CurrentWorkflowStepIndex → + // Levels.Where(Order == CurrentApprovalLevelOrder) → distinct ApproverUserId. + // V2-only (ApprovalWorkflowId + pointer set); phiếu V1/no-workflow skip. + // Best-effort (try/catch) — phiếu đã SaveChanges, notify fail KHÔNG fail transition. + if (toPhase == PurchaseEvaluationPhase.ChoDuyet + && evaluation.ApprovalWorkflowId is Guid notifyAwId + && evaluation.CurrentWorkflowStepIndex is int notifyCsi + && evaluation.CurrentApprovalLevelOrder is int notifyLvl) + { + try + { + var workflow = await db.ApprovalWorkflows.AsNoTracking() + .Include(w => w.Steps).ThenInclude(s => s.Levels) + .FirstOrDefaultAsync(w => w.Id == notifyAwId, ct); + var stepsOrdered = workflow?.Steps.OrderBy(s => s.Order).ToList(); + if (stepsOrdered is not null && notifyCsi >= 0 && notifyCsi < stepsOrdered.Count) + { + var approverIds = stepsOrdered[notifyCsi].Levels + .Where(l => l.Order == notifyLvl + && l.ApproverUserId != Guid.Empty + && l.ApproverUserId != actorUserId) + .Select(l => l.ApproverUserId) + .Distinct() + .ToList(); + if (approverIds.Count > 0) + { + await notifications.NotifyManyAsync( + approverIds, + NotificationType.Generic, + $"Phiếu cần bạn duyệt: {evaluation.MaPhieu ?? evaluation.TenGoiThau}", + "Có phiếu Duyệt NCC đang chờ bạn duyệt.", + $"/purchase-evaluations/{evaluation.Id}", + evaluation.Id, + ct); + } + } + } + catch + { + // best-effort — nuốt lỗi notify để KHÔNG rollback transition đã lưu. + } + } } // Mig 26 (Session 19) — helper resolve FullName cho denorm `SignedByFullName`.