[CLAUDE] PurchaseEvaluation: drafter tự gỡ cờ GẤP + ngân sách kỳ này tự nhảy từ ban-hành (anh Kiệt FDC UAT R2)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m46s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m46s
D2 — Người KHAI phiếu tự GỠ cờ GẤP của mình (nới luật S77 "GỠ=chỉ TP"). BE handler restructure SET/UNSET: GẮN giữ role-only (PRO/CCM/Admin); GỠ +drafter (DrafterUserId==actor) → drafter/Admin clear CẢ 2 cờ, TP-vai clear cờ vai mình. FE canToggle +isDrafter. D3 — "Ngân sách kỳ này" (row3) tự nhảy từ "Ngân sách Ban hành lần đầu" (PeWorkItemBudget.ProInitialAmount) khi chưa nhập tay. Submit-guard BE + FE missingForApproval dùng effective = budgetPeriodAmount ?? proInitialAmount (mirror 2 tầng, chống bypass gotcha #44). BE 2 file (Urgent handler + Workflow submit-guard). FE PeDetailTabs ×2 app SHA-identical. Test +5 PeUrgentToggleAuthzTests (drafter-self-unset authz matrix) → 371 PASS. BE slnx + 2 FE build PASS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -130,11 +130,13 @@ export function PeDetailTabs({
|
||||
// GỠ = chỉ Trưởng phòng (DeptManager)/Admin (tránh NV khác lỡ tay gỡ). Nút phụ thuộc
|
||||
// trạng thái hiện tại: đã gấp → cần quyền GỠ; chưa gấp → cần quyền GẮN.
|
||||
const isDeptManager = currentUser?.roles?.includes('DeptManager') ?? false
|
||||
// [S85 anh Kiệt] Người KHAI phiếu tự GỠ cờ gấp của mình (nới GỠ +drafter; GẮN giữ role-only).
|
||||
const isDrafter = currentUser?.id != null && evaluation.drafterUserId === currentUser.id
|
||||
const canToggleProUrgent = evaluation.isUrgentByPro
|
||||
? (isAdmin || (isPro && isDeptManager))
|
||||
? (isAdmin || (isPro && isDeptManager) || isDrafter)
|
||||
: (isAdmin || isPro)
|
||||
const canToggleCcmUrgent = evaluation.isUrgentByCcm
|
||||
? (isAdmin || (isCcm && isDeptManager))
|
||||
? (isAdmin || (isCcm && isDeptManager) || isDrafter)
|
||||
: (isAdmin || isCcm)
|
||||
const v2Approvers = evaluation.currentApproval?.approvers ?? []
|
||||
const actorMatchesLevel = isAdmin
|
||||
@ -203,7 +205,10 @@ export function PeDetailTabs({
|
||||
}
|
||||
// 3. Chưa nhập Ngân sách kỳ này (S61 — row 3 bảng tổng hợp, drafter nhập).
|
||||
// Predicate MIRROR BE guard: BudgetPeriodAmount is null || <= 0.
|
||||
if (evaluation.budgetPeriodAmount == null || evaluation.budgetPeriodAmount <= 0) {
|
||||
// [S85 D3 anh Kiệt] Effective = budgetPeriodAmount ?? proInitialAmount (Ban hành lần đầu) —
|
||||
// mirror BE submit-guard fallback (row3 "tự nhảy" từ ban-hành → vẫn gửi-duyệt được).
|
||||
const effBudget = evaluation.budgetPeriodAmount ?? evaluation.budgetSummary?.proInitialAmount ?? null
|
||||
if (effBudget == null || effBudget <= 0) {
|
||||
missing.push("Chưa nhập Ngân sách kỳ này")
|
||||
}
|
||||
// 4. Chưa đính kèm Bảng so sánh (attachment supplier-row null — chuẩn Section 3).
|
||||
@ -1245,9 +1250,11 @@ function PeBudgetSummaryTable({ ev, readOnly }: { ev: PeDetailBundle; readOnly:
|
||||
// [C4a anh Kiệt FDC] Live-recompute: giữ giá trị ĐANG GÕ của ô 3 (NS kỳ này) + ô 8 (giá
|
||||
// trị TH dự kiến còn lại) ở state cục bộ → dòng 5/6/7/9 + So sánh + % nhảy NGAY khi gõ
|
||||
// (chưa cần bấm Lưu). Sync lại từ server (ev.*) sau mỗi save/refetch.
|
||||
const [draftRow3, setDraftRow3] = useState<number | null>(ev.budgetPeriodAmount)
|
||||
// [S85 D3 anh Kiệt] draftRow3 default ← "Ngân sách Ban hành lần đầu" (bs.proInitialAmount)
|
||||
// khi chưa nhập tay budgetPeriodAmount → row3 "tự nhảy". bs có thể null (guard !bs dưới) → optional.
|
||||
const [draftRow3, setDraftRow3] = useState<number | null>(ev.budgetPeriodAmount ?? bs?.proInitialAmount ?? null)
|
||||
const [draftRow8, setDraftRow8] = useState<number | null>(ev.expectedRemainingAmount)
|
||||
useEffect(() => { setDraftRow3(ev.budgetPeriodAmount) }, [ev.budgetPeriodAmount])
|
||||
useEffect(() => { setDraftRow3(ev.budgetPeriodAmount ?? bs?.proInitialAmount ?? null) }, [ev.budgetPeriodAmount, bs?.proInitialAmount])
|
||||
useEffect(() => { setDraftRow8(ev.expectedRemainingAmount) }, [ev.expectedRemainingAmount])
|
||||
|
||||
// Phiếu cũ chưa gắn Hạng mục công việc → budgetSummary null.
|
||||
@ -1434,7 +1441,7 @@ function PeBudgetSummaryTable({ ev, readOnly }: { ev: PeDetailBundle; readOnly:
|
||||
value={
|
||||
drafterEditable ? (
|
||||
<VndInlineEdit
|
||||
initial={ev.budgetPeriodAmount}
|
||||
initial={ev.budgetPeriodAmount ?? bs.proInitialAmount}
|
||||
saving={adjustMut.isPending}
|
||||
label="Ngân sách kỳ này"
|
||||
onLiveChange={setDraftRow3}
|
||||
|
||||
Reference in New Issue
Block a user