[CLAUDE] FE-PE: S22+5 Chunk B — Designer checkbox +AllowApproverEditBudget per slot + Section read flag (mirror 2 app)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m23s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m23s
FE Admin Designer (ApprovalWorkflowsV2Page.tsx): - LevelDto + EditLevelEntry +allowApproverEditBudget - copyFromDefinition + makeDefaultLevelEntry propagate default false - POST body include allowApproverEditBudget cho mỗi Level slot - NEW checkbox UI per Level inline panel: "Cho phép chỉnh sửa Section ngân sách lúc đang duyệt" (col-span-2, mirror pattern allowApproverEditDetails Mig 29) FE Types mirror 2 app: - fe-admin + fe-user `ApprovalWorkflowOptions` +allowApproverEditBudget FE BudgetAdjustSection refactor (mirror 2 app): - Trước: isApproverChoDuyet = phase ChoDuyet + actor in approvers - Sau: isApproverChoDuyet = phase ChoDuyet + actor in approvers + currentLevelOptions.allowApproverEditBudget=true (per slot opt-in) - Drafter scope Nháp/Trả lại unchanged - Admin bypass unchanged UX impact: - Admin Designer phải tick checkbox cho NV slot mới được edit ngân sách lúc duyệt - Nếu KHÔNG tick → button "Điều chỉnh" trong Section 5 KHÔNG hiện cho approver - Drafter vẫn edit bình thường khi phiếu Nháp/Trả lại Verify: - npm run build fe-admin — 569ms pass - npm run build fe-user — 528ms pass Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -974,9 +974,13 @@ function BudgetAdjustSection({ ev, readOnly }: { ev: PeDetailBundle; readOnly: b
|
||||
const isDrafter = currentUser?.id != null && ev.drafterUserId === currentUser.id
|
||||
const isDrafterPhase = ev.phase === PurchaseEvaluationPhase.DangSoanThao
|
||||
|| ev.phase === PurchaseEvaluationPhase.TraLai
|
||||
// Approver currentLevel match — phase ChoDuyet + actor in current approval level
|
||||
// F4 Approver scope (Mig 30): phase ChoDuyet + actor in currentApproval.approvers
|
||||
// + currentLevel có flag AllowApproverEditBudget=true (admin Designer tick per slot).
|
||||
const actorInCurrentLevel = ev.currentApproval?.approvers?.some(a => a.userId === currentUser?.id) ?? false
|
||||
const isApproverChoDuyet = ev.phase === PurchaseEvaluationPhase.ChoDuyet && actorInCurrentLevel
|
||||
const approverEditBudgetAllowed = ev.currentLevelOptions?.allowApproverEditBudget ?? false
|
||||
const isApproverChoDuyet = ev.phase === PurchaseEvaluationPhase.ChoDuyet
|
||||
&& actorInCurrentLevel
|
||||
&& approverEditBudgetAllowed
|
||||
|
||||
const canAdjust = !readOnly && (isAdmin || (isDrafter && isDrafterPhase) || isApproverChoDuyet)
|
||||
|
||||
|
||||
@ -42,11 +42,13 @@ type LevelDto = {
|
||||
approverUserName: string | null
|
||||
approverEmail: string | null
|
||||
// Mig 29 (S21 t5) — 5 Allow* options per slot Approver
|
||||
// Mig 30 (S22+5) — +AllowApproverEditBudget cho Section ngân sách
|
||||
allowReturnOneLevel: boolean
|
||||
allowReturnOneStep: boolean
|
||||
allowReturnToAssignee: boolean
|
||||
allowReturnToDrafter: boolean
|
||||
allowApproverEditDetails: boolean
|
||||
allowApproverEditBudget: boolean
|
||||
}
|
||||
type StepDto = {
|
||||
id: string
|
||||
@ -86,11 +88,13 @@ type EditLevelEntry = {
|
||||
approverUserId: string
|
||||
// Mig 29 (S21 t5) — 5 Allow* per slot (default backward compat S17: chỉ
|
||||
// AllowReturnToDrafter=true, 4 còn lại false).
|
||||
// Mig 30 (S22+5) — +AllowApproverEditBudget cho Section ngân sách (default false).
|
||||
allowReturnOneLevel: boolean
|
||||
allowReturnOneStep: boolean
|
||||
allowReturnToAssignee: boolean
|
||||
allowReturnToDrafter: boolean
|
||||
allowApproverEditDetails: boolean
|
||||
allowApproverEditBudget: boolean
|
||||
}
|
||||
type EditStep = { name: string; departmentId: string | null; levelEntries: EditLevelEntry[] }
|
||||
|
||||
@ -137,6 +141,7 @@ function copyFromDefinition(d: DefinitionDto): EditStep[] {
|
||||
allowReturnToAssignee: l.allowReturnToAssignee ?? false,
|
||||
allowReturnToDrafter: l.allowReturnToDrafter ?? true,
|
||||
allowApproverEditDetails: l.allowApproverEditDetails ?? false,
|
||||
allowApproverEditBudget: l.allowApproverEditBudget ?? false,
|
||||
})),
|
||||
}))
|
||||
}
|
||||
@ -152,6 +157,7 @@ function makeDefaultLevelEntry(order: LevelOrder, approverUserId: string): EditL
|
||||
allowReturnToAssignee: false,
|
||||
allowReturnToDrafter: true,
|
||||
allowApproverEditDetails: false,
|
||||
allowApproverEditBudget: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -553,6 +559,7 @@ function Designer({
|
||||
allowReturnToAssignee: e.allowReturnToAssignee,
|
||||
allowReturnToDrafter: e.allowReturnToDrafter,
|
||||
allowApproverEditDetails: e.allowApproverEditDetails,
|
||||
allowApproverEditBudget: e.allowApproverEditBudget,
|
||||
})),
|
||||
})),
|
||||
})
|
||||
@ -911,6 +918,15 @@ function Designer({
|
||||
/>
|
||||
<span>Cho phép chỉnh sửa Section 2 (Hạng mục/NCC/Báo giá) lúc đang duyệt</span>
|
||||
</label>
|
||||
<label className="col-span-2 flex items-center gap-1 text-[11px] text-slate-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="h-3 w-3"
|
||||
checked={entry.allowApproverEditBudget}
|
||||
onChange={e => updateField('allowApproverEditBudget', e.target.checked)}
|
||||
/>
|
||||
<span>Cho phép chỉnh sửa Section ngân sách lúc đang duyệt</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -355,6 +355,7 @@ export type ApprovalWorkflowOptions = {
|
||||
allowReturnToAssignee: boolean
|
||||
allowReturnToDrafter: boolean
|
||||
allowApproverEditDetails: boolean
|
||||
allowApproverEditBudget: boolean // Mig 30 (S22+5) — F4 Section ngân sách
|
||||
}
|
||||
|
||||
// Mig 28 (S21 t4) — F1 mode Trả lại payload gửi BE
|
||||
|
||||
@ -981,8 +981,12 @@ function BudgetAdjustSection({ ev, readOnly }: { ev: PeDetailBundle; readOnly: b
|
||||
const isDrafter = currentUser?.id != null && ev.drafterUserId === currentUser.id
|
||||
const isDrafterPhase = ev.phase === PurchaseEvaluationPhase.DangSoanThao
|
||||
|| ev.phase === PurchaseEvaluationPhase.TraLai
|
||||
// F4 Approver scope (Mig 30): ChoDuyet + actor in approvers + flag tick.
|
||||
const actorInCurrentLevel = ev.currentApproval?.approvers?.some(a => a.userId === currentUser?.id) ?? false
|
||||
const isApproverChoDuyet = ev.phase === PurchaseEvaluationPhase.ChoDuyet && actorInCurrentLevel
|
||||
const approverEditBudgetAllowed = ev.currentLevelOptions?.allowApproverEditBudget ?? false
|
||||
const isApproverChoDuyet = ev.phase === PurchaseEvaluationPhase.ChoDuyet
|
||||
&& actorInCurrentLevel
|
||||
&& approverEditBudgetAllowed
|
||||
|
||||
const canAdjust = !readOnly && (isAdmin || (isDrafter && isDrafterPhase) || isApproverChoDuyet)
|
||||
|
||||
|
||||
@ -353,6 +353,7 @@ export type ApprovalWorkflowOptions = {
|
||||
allowReturnToAssignee: boolean
|
||||
allowReturnToDrafter: boolean
|
||||
allowApproverEditDetails: boolean
|
||||
allowApproverEditBudget: boolean // Mig 30 (S22+5) — F4 Section ngân sách
|
||||
}
|
||||
|
||||
// Mig 28 (S21 t4) — F1 mode Trả lại payload gửi BE
|
||||
|
||||
Reference in New Issue
Block a user