You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor(budget-plan): give editability one home and delete through the policy
Review feedback on PR #329: BudgetPlan::isEditable() sat between business logic
and authorization, with the rule re-derived at each call site.
The state now owns it — BudgetPlanState::isEditable() is false by default and
overridden to true in Draft and Resolved, beside isAdvancement() and
isCascadingArc(). BudgetPlanPolicy::update() and delete() delegate to it, and
BudgetPlan::isEditable() is a thin forwarder that folds in an amendment's
stricter Draft-only rule so ⚡amendment-edit stops keeping its own copy.
Plan deletion goes through the policy rather than around it. deletePlan() had
authorized 'admin' and then repeated the state check with its own
abort_unless(); it now authorizes 'delete' once, so the server-side rule and the
modal's checklist cannot drift apart. Deleting a plan consequently takes the
budget officer role (ref-finanzen-hv) instead of admin — admins still pass via
UserPolicy::before(). Nothing released changes, since the whole delete flow is
new in 4.5.0.
Two call sites needed care while narrowing 'update':
- ⚡plan-edit::mount() now runs its state check before authorize(), so a
budget officer opening a frozen plan still gets the redirect to the
read-only view rather than a bare 403.
- The "change state" menu item had been using @can('update') as a stand-in for
"is budget officer". Left alone it would have vanished on every Approved,
Active and Completed plan — exactly where transitioning matters — so it now
checks the role directly, matching what transitionTo() enforces.
Coverage for the new rule: a budget officer may delete, a plain user may not and
never sees the modal, admins still may, and the state gate still holds. The
existing "forbids a non-admin from deleting the plan" case asserted the rule
this commit deliberately replaces and now covers a user without the role.
Refs: OP#581
0 commit comments