feat(permissions): carve out dedicated payments resource
Payments (deposit / balance / refund records on an interest) used to
share `invoices.record_payment`, which forces a port that doesn't
issue invoices at all to still navigate the invoicing permission
group to grant its sales reps payment-recording rights. Splitting
the resource lets admins gate the two surfaces independently.
The new resource has three actions:
- view — gates the UI affordance (API reads still go through
`interests.view`)
- record — POST / PATCH a payment
- delete — DELETE a payment record
Seed maps updated for all six system roles; existing role rows +
per-user permission overrides are backfilled by migration 0064 so
upgrades don't silently lose access. Two call sites (POST /interests/
[id]/payments, PATCH /payments/[id]) → payments.record; one
(DELETE /payments/[id]) → payments.delete. The PermissionGates on the
payments-section UI swap to the new keys.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
32
src/lib/db/migrations/0064_payments_permission.sql
Normal file
32
src/lib/db/migrations/0064_payments_permission.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- 0064_payments_permission.sql
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- Carve out a dedicated `payments` resource from `invoices.record_payment`.
|
||||
-- Existing role rows + per-user permission overrides are backfilled so the
|
||||
-- new resource defaults to whatever record_payment was, preventing silent
|
||||
-- access loss when the call sites switch over.
|
||||
|
||||
UPDATE roles
|
||||
SET permissions = permissions || jsonb_build_object(
|
||||
'payments', jsonb_build_object(
|
||||
'view', COALESCE((permissions->'invoices'->>'view')::boolean, false),
|
||||
'record', COALESCE((permissions->'invoices'->>'record_payment')::boolean, false),
|
||||
'delete', COALESCE((permissions->'invoices'->>'record_payment')::boolean, false)
|
||||
)
|
||||
)
|
||||
WHERE permissions IS NOT NULL
|
||||
AND NOT (permissions ? 'payments');
|
||||
|
||||
-- Per-user overrides store a Partial<RolePermissions> as JSONB. Mirror an
|
||||
-- explicit invoices.record_payment leaf into payments.{record, delete} so
|
||||
-- "Alice can record payments even though her role can't" survives the
|
||||
-- carve-out.
|
||||
UPDATE user_permission_overrides
|
||||
SET permission_overrides = permission_overrides || jsonb_build_object(
|
||||
'payments', jsonb_build_object(
|
||||
'record', (permission_overrides->'invoices'->>'record_payment')::boolean,
|
||||
'delete', (permission_overrides->'invoices'->>'record_payment')::boolean
|
||||
)
|
||||
)
|
||||
WHERE permission_overrides ? 'invoices'
|
||||
AND (permission_overrides->'invoices') ? 'record_payment'
|
||||
AND NOT (permission_overrides ? 'payments');
|
||||
Reference in New Issue
Block a user