Skip to content

fix(auth): billing permissions migration + night-auditor refund gate - #362

Merged
telivity-otaip merged 3 commits into
mainfrom
cursor/billing-permissions-refund-gate-4a4f
Sep 8, 2026
Merged

telivity-otaip merged 3 commits into
mainfrom
cursor/billing-permissions-refund-gate-4a4f

Conversation

@telivity-otaip

@telivity-otaip telivity-otaip commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Supersedes / completes #361 by Charles (@modernitconsultants / ModernIT Consultants) — full credit for the six-controller @Roles@RequirePermissions migration, accounting.manage, catalog widenings, migration 0023, and migration specs.

What Charles shipped (#361)

  • Folio, payment, cashier, house-account, notifications, accounting onto @RequirePermissions
  • New accounting.manage key + ROLE_DEFAULT_PERMISSIONS / 0023_billing_write_access_grants.sql backfill
  • Honest disclosure that reusing communications.manage also lets revenue_manager send guest notifications

What this PR adds on top

  1. payments.refund — void / refund / correct on PaymentController and deposit refund on AccountingController. Granted to front_desk, reservations, accounting (and admin/GM via ALL_PERMISSIONS). Not granted to night_auditor (overnight posting stays on folios.manage / accounting.manage).
  2. Honest payment narrativePaymentController on main was only admin | GM | front_desk | reservations, not the six-role list. Specs and migration comments say that.
  3. Seed cannot drift — permission catalog lives in @telivityhaip/shared/permissions-catalog; packages/database seed builds role grants from ROLE_DEFAULT_PERMISSIONS (Nest-free subpath export).

Validation

  • Full suite: 2245 passing across 268 files (CI=true)
  • Migration 0023 applies cleanly on haip_test

Side effects (unchanged from #361, still disclosed)

  • revenue_manager + guest notifications via shared communications.manage
  • reservations keeps folio/cashier write access the old realm-role gates already allowed on those controllers

Prefer merging this branch and closing #361 as superseded.

Open in Web Open in Cursor 

modernitconsultants and others added 3 commits September 8, 2026 07:42
…ns()

#340 migrated eighteen controllers off the legacy @roles() decorator and
deliberately left six behind: accounting, cashier, folio, house-account,
notifications and payment. Those six gated every route -- read and write
alike -- with one identical realm-role list, so no existing permission key
matched it without either widening or narrowing who can record a payment or
post a charge.

This finishes the migration on the rule of preserving what the realm-role gate
was granting in practice, rather than narrowing it silently:

- Six controllers move onto @RequirePermissions(), reusing existing keys where
  they exist (folios.manage, houseaccounts.manage, cashier.access,
  communications.manage) and adding one new key, accounting.manage, for the
  deposit / A-R / accounting-code routes, which had none.
- ROLE_DEFAULT_PERMISSIONS gains the keys four system roles were already being
  granted by the old list: front_desk, night_auditor, accounting and
  reservations. In particular `reservations` was in the allow-list of every
  billing controller, so its doc comment ("reservations without cashier /
  folio posting") described a split the code never enforced.
- Migration 0023_billing_write_access_grants.sql backfills those grants for
  properties that already have role_permissions rows, using the same
  every-property-with-RBAC cross-join as 0015. A property with no RBAC rows
  gets nothing, by design.
- payment-legacy-seam.spec.ts asserted PaymentController's legacy @roles()
  metadata; it now asserts the permission key, updated in the same commit that
  changes the controller.
- billing-role-controllers-migration.spec.ts covers all six, asserting both
  that no @roles() metadata is left behind and that the expected key is set.

One side effect worth disclosing rather than burying: communications.manage is
already held by revenue_manager for an unrelated existing route in
groups.controller.ts, so reusing that key on notifications.controller.ts also
lets revenue_manager send guest SMS/WhatsApp/Telegram. Reusing the existing key
seemed better than inventing a near-duplicate, but it is easy to split if you
would rather.

README.md and docs/test-stats.json updated for the 15 added test cases and one
added file.
Builds on #361 (Charles / modernitconsultants): billing controllers onto
@RequirePermissions, plus a payments.refund key so night_auditor can post
overnight without void/refund/correct or deposit refund. Seed now reads
ROLE_DEFAULT_PERMISSIONS from @telivityhaip/shared/permissions-catalog so
demo RBAC cannot drift from the API catalog.

Co-authored-by: ModernIT Consultants <charles@modernit.com.au>
2245 tests / 268 files after payments.refund gate + shared catalog move.

Co-authored-by: telivity-otaip <telivity-otaip@users.noreply.github.com>
@telivity-otaip

Copy link
Copy Markdown
Collaborator Author

@modernitconsultants thank you!

@telivity-otaip
telivity-otaip merged commit 53aa8e2 into main Sep 8, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants