Skip to content

MVP Role Matrix and Permissions ​

ADR status ​

FieldValue
ADRMHPAY-ADR-003
Date2026-06-25
StatusAccepted
DecisionMVP role set is reduced to MegaAdmin, MegaManager, OrganizationAdmin, Accountant, and Viewer.
Runtime changesImplemented by MHPAY-51: backend role permission profiles, seed roles, dev users, assignable-role filtering, and RBAC tests now follow this MVP matrix.

RBAC mistakes are security bugs. Future implementation must use deny-by-default authorization, least privilege, cross-tenant isolation, and auditable mutations.

Role names are canonical English identifiers and must not be localized.

Scope model ​

ScopeMeaningMVP rule
PlatformMyHoreca Pay operator scope across organizations.Used by MegaAdmin and MegaManager.
OrganizationCustomer/business ownership scope.Primary tenant boundary for users, credentials, rules, bank imports, and financial workflow.
Sub-organizationChild business scope under a parent organization.Inherits parent rules and parent OrganizationAdmin access as defined by the ownership ADR.
Outlet / locationOperational endpoint such as a restaurant, branch, or work location.Documentation uses Outlet as the product term. Existing runtime constants may still use location.* until implementation is updated.

OrganizationAdmin access inherits downward: an OrganizationAdmin for a parent organization receives organization-admin authority in child organizations. This inheritance must be visible in audit and must not make child organizations fully independent tenants.

Implementation note for MHPAY-50: parent OrganizationAdmin scope inheritance is now evaluated by the backend scoped authorization service for child organizations. Existing runtime permission constants still use organization.locations.manage for Outlet/sub-organization structure management until a future task adds organization.sub_organizations.manage / organization.outlets.manage.

MVP roles ​

RoleScopePurpose
MegaAdminPlatformPlatform owner for organizations, quotas, global settings, diagnostics, and production safety procedure.
MegaManagerPlatform / assigned support scopeCustomer onboarding and support without production write approval.
OrganizationAdminOrganization plus child organizationsCustomer admin for organizations, sub-organizations, Outlets, users, credentials, and rules.
AccountantAssigned organization / Outlet scopeFull payment workflow: import, review, match, allocate, manually approve scoped posting after safety checks, reconcile, anomalies, evidence.
ViewerAssigned organization / Outlet scopeRead-only visibility into allowed resources.

PaymentOperator and LocationAdmin are not MVP roles. They must receive no MVP permissions in this matrix. They may remain in existing runtime code for backward compatibility or target/future work, but implementing MVP flows must not depend on them.

Implementation note for MHPAY-51: RoleNames.PaymentOperator and RoleNames.LocationAdmin remain as future-only / legacy-compatible constants, but their runtime permission profiles are empty. DatabaseSeeder creates only the five MVP roles and no longer creates operator@myhoreca.id or locationadmin@myhoreca.id dev users.

Implementation note for MHPAY-52: platform.users.manage now protects the platform user-management API and UI. MegaAdmin and MegaManager can list/create/update users and directly assign/remove MVP roles across platform, organization, and Outlet scopes. MegaAdmin / MegaManager assignments are platform-scope only; OrganizationAdmin, Accountant, and Viewer require organization or Outlet scope. OrganizationAdmin continues to use organization-scoped user management and must not see the platform user workspace. Direct assignment excludes PaymentOperator, LocationAdmin, and legacy aliases.

Implementation note for MHPAY-123: platform user UI follows list → detail (/platform/users, /platform/users/new, /platform/users/:userId). The list page has no inline create form; create/edit/deactivate and role assignment/removal happen only on the detail page. Routes stay under platform.users.manage and outside Outlet operational gating. Frontend visibility is not authorization; direct route access still requires route guards and backend scoped checks.

Implementation note for MHPAY-124: organization user UI follows list → detail under /settings/users and /settings/users/:userId. The list page has no inline create or role-assignment form; role assignment/removal happens only on the detail page through organization-scoped endpoints. Access is guarded by organization.users.manage; OrganizationAdmin uses this workspace, while /platform/users remains separate and guarded by platform.users.manage. Assignable roles in the UI are limited to MVP roles and exclude PaymentOperator / LocationAdmin.

Implementation note for MHPAY-126: platform organization registry UI is available at /platform/organizations and is guarded by platform.organizations.view. The page reuses the existing GET /api/platform/organizations list endpoint and opens the existing /settings quota/context workflow for the selected organization; it does not add organization creation, hierarchy, detail-route, RBAC, or backend model behavior.

Implementation note for MHPAY-53: active organization/Outlet context selection is added after login. No new permissions are introduced; context selection relies on existing scoped RBAC checks (IScopedAuthorizationService) and denies cross-org/cross-outlet selections with 403.

Implementation note for MHPAY-59: Syrve credentials rotation UI uses the dedicated runtime permission syrve.credentials.rotate. The action is visible only for MegaAdmin, MegaManager, and OrganizationAdmin; Accountant and Viewer do not receive a button, form, or callable UI path.

Implementation note for MHPAY-61: original bank file download uses the dedicated runtime permission bankFiles.download. MegaAdmin and MegaManager can pass through platform support access; OrganizationAdmin and Accountant can download only within assigned organization/Outlet scope. Viewer, PaymentOperator, and LocationAdmin do not receive this permission. Cross-tenant/cross-location download attempts must return 403, while expired/deleted/missing originals return safe 404.

Implementation note for MHPAY-63: balance-mismatch review override uses location.bank_statements.quality_override. Allowed profiles: MegaAdmin, MegaManager, scoped OrganizationAdmin, scoped Accountant. Viewer is denied. Override requires a non-empty reason, persists actor/timestamp on the quality report, writes audit metadata without raw bank content, and unblocks Syrve/iiko queue posting when other guards pass.

Implementation note for MHPAY-66: stored bank statement reprocess preview is not a new scoped permission. Access is enforced by explicit platform role check (MegaAdmin, MegaManager only). Preview runs the current adapter/parser pipeline read-only against the original file, stores BankStatementReprocessPreview with diff summary/samples, writes audit metadata, and must not create or mutate BankTransactions, CounterpartyMatches, duplicate candidates, allocations, postings, or existing import/quality-report rows. OrganizationAdmin, Accountant, Viewer, PaymentOperator, and LocationAdmin receive 403. If the original file is expired/deleted/missing, API/UI return a safe OriginalFileUnavailable result. Real re-import remains a new upload with duplicate detection only.

Implementation note for counterparty manual-match wizard: legacy policy syrve.references.view on GET /api/syrve/counterparties is granted when the user has scoped location.counterparties.manage or location.transactions.match (PermissionCompatibility). OrganizationAdmin and Accountant can load the Syrve supplier picker; Viewer is denied. Legacy counterparty.match on POST /api/counterparty-matches uses the same scoped pair.

Implementation note for dashboard Syrve sync: manual document/supplier sync is not shown on dashboard or accountant workbench. Sync controls live on organization connection admin screens (/syrve/connections) for OrganizationAdmin only. Background workers handle routine sync; accountants consume synced data through payment workflow screens.

Implementation note for operational connection resolution: legacy policy syrve.connections.view on GET /api/syrve/connections is granted for scoped payment-workflow permissions (location.payments.queue, location.counterparties.manage, location.transactions.match, organization.payments.view) so references and write-target screens can resolve the default connection without connection-admin rights. Connection registry UI remains gated by syrve.connections.manage.

Implementation note for MHPAY-89: /admin/workers is protected by explicit platform role check (MegaAdmin, MegaManager, and legacy Admin compatibility only), not by a broad permission profile. The dashboard is read-only by default. Manual run is available only for allowlisted safe categories (read_sync and file_cleanup); payment posting and hard-delete manual retry are forbidden, and non-Mega roles receive 403.

Role definitions ​

MegaAdmin ​

Can do: manage platform organizations, quotas, platform settings, platform users, diagnostics, and global audit; configure controlled production enablement decisions when a future production-write ADR allows it.

Cannot do: bypass audit, bypass critical anomaly blocks, perform routine customer payment work as the default workflow, or approve production write without the full safety gates.

Write limits: no routine payment write. Production enablement is a platform gate, not a direct posting permission.

MegaManager ​

Can do: onboard and support organizations, view support diagnostics, assist with connection setup, and inspect assigned/support-scoped organization state.

Cannot do: approve production write, change platform-wide quotas/settings unless explicitly delegated, bypass tenant boundaries, or mutate customer financial records outside support workflows.

Write limits: no payment write approval; no production write approval.

OrganizationAdmin ​

Can do: manage organization, child organizations, Outlets, organization users, role assignments, Syrve/iiko credentials/connections, counterparty matching rules, manual counterparty matching, organization-level diagnostics, and organization audit within owned scope.

Cannot do: access another organization, bypass child/parent audit inheritance, approve production write, bypass critical anomaly blocks, or perform platform-only quota/global settings changes.

Write limits: no direct payment write authority. Can manage prerequisites such as credentials and rules within scope.

Accountant ​

Can do: organization-admin operational scope except Syrve/iiko connection registry, connection test, and credential rotation; full MVP payment workflow (import, review, match, allocate, approve/queue/reconcile, anomalies, users/locations within assigned scope, document sync, audit).

Cannot do: manage Syrve connection settings or rotate credentials, access platform-only settings, approve production write, or bypass open Critical anomaly blocks.

Write limits: posting only with scope, mandatory manual approval, idempotency, payable/amount/allocation/anomaly checks, server applicability, credentials, method-specific preflight and audit. Always-on capability does not grant production authorization.

organization.documents.sync is intentionally included for Accountant: refreshing document snapshots is an operational prerequisite for safe allocation. It does not grant connection registry, connection test, or credential access.

Viewer ​

Can do: read allowed dashboards, imports, transactions, documents, allocations, postings, anomalies, notifications, evidence, and audit entries.

Cannot do: upload, download original bank files, match, allocate, approve, retry, reconcile, mutate credentials, manage users, manage rules, manage quotas, run privileged diagnostics, or execute any mutating workflow action.

Write limits: none.

PaymentOperator — not MVP ​

No MVP permissions. Do not assign PaymentOperator any MVP responsibility or use it as a dependency for the Accountant-led MVP workflow.

LocationAdmin — not MVP ​

No MVP permissions. Do not require outlet-scoped administration for MVP. Future outlet admin behavior requires a separate ADR or an accepted update to the ownership/permissions ADRs.

Permission matrix ​

Legend:

  • Allow means permitted within the role's assigned scope and only after tenant checks.
  • Read means read-only access within assigned scope.
  • Support means support/onboarding access without financial write authority.
  • Deny means no MVP permission.
  • Gate means allowed only through additional safety gates.
AreaMegaAdminMegaManagerOrganizationAdminAccountantViewerPaymentOperatorLocationAdmin
UsersAllow platform/admin usersSupport onboarding onlyAllow org/sub-org usersDenyDenyNot MVP / DenyNot MVP / Deny
OrganizationsAllowSupportRead/manage own org and child orgsRead assigned scopeRead assigned scopeNot MVP / DenyNot MVP / Deny
Sub-organizationsAllowSupportAllow within parent inheritanceRead assigned scopeRead assigned scopeNot MVP / DenyNot MVP / Deny
Outlets / locationsAllowSupportAllow within org/sub-orgRead assigned scopeRead assigned scopeNot MVP / DenyNot MVP / Deny
Bank importRead/support diagnostics + original-file downloadSupport diagnostics + original-file downloadRead/manage settings + original-file download within scopeAllow import + original-file download within scopeRead metadata/parsed results only; no original-file downloadNot MVP / DenyNot MVP / Deny
Transaction reviewRead/support diagnosticsSupport diagnosticsRead within orgAllowReadNot MVP / DenyNot MVP / Deny
Counterparty matchingRead/support diagnosticsSupport diagnosticsAllow manual match and rules within org scopeAllowReadNot MVP / DenyNot MVP / Deny
AllocationRead/support diagnosticsSupport diagnosticsRead within orgAllowReadNot MVP / DenyNot MVP / Deny
PostingGate platform enablement onlyDeny production approvalDeny direct postingGate test posting approval/queue/retryReadNot MVP / DenyNot MVP / Deny
Credentials / Syrve connectionsPlatform visibility and emergency controlsSupport connection setupAllow within org/sub-orgDeny manual sync and connection admin; operational read for references/postings onlyRead status onlyNot MVP / DenyNot MVP / Deny
Matching rulesRead/support diagnosticsSupportAllow within org/sub-orgUse/create operational matches; rule mutation only if explicitly delegated laterReadNot MVP / DenyNot MVP / Deny
AuditGlobal readSupport readOrganization/sub-org readPayment workflow readRead assigned scopeNot MVP / DenyNot MVP / Deny
Workers / background operationsRead dashboard; safe read/sync and file-cleanup manual run only; no posting/hard-delete retrySupport diagnostics; safe read/sync and file-cleanup manual run onlyDeny /admin/workers; view org job status only through future scoped UITrigger allowed workflow actions onlyRead status only through future scoped UINot MVP / DenyNot MVP / Deny
DiagnosticsAllow platform diagnosticsSupport diagnosticsOrganization diagnosticsWorkflow diagnosticsRead basic statusNot MVP / DenyNot MVP / Deny
Quotas / platform settingsAllowSupport/read unless delegatedRead org quota; no platform mutationDenyDenyNot MVP / DenyNot MVP / Deny
Anomaly handlingRead global; cannot bypass blocksSupport/readRead/manage assignment only if future ADR allowsAllow manage within scope; cannot bypass Critical blockReadNot MVP / DenyNot MVP / Deny
Notifications / evidenceRead global/supportSupport/readRead within orgAllow within workflowReadNot MVP / DenyNot MVP / Deny

The accepted navigation hierarchy is defined in ADR: Navigation & Tenancy Information Architecture v1. Frontend sidebar visibility is not authorization; it is only the first user-facing filter. Direct route access must still be denied by route guards and backend scoped authorization when the user lacks the required permission.

MVP rolePlatform groupOrganization groupSyrve groupOperations / payment workflow
MegaAdminVisible for platform organizations, platform users, Workers, bank adapters, settings where permittedVisible for support/admin settings where permittedVisible for connection registry/support diagnosticsRead/support diagnostics; not routine workflow owner
MegaManagerVisible for platform organizations and support/platform flows where delegatedVisible for onboarding/support where permittedVisible for support connection setup/diagnosticsDiagnostics/read support only; no payment write approval
OrganizationAdminHiddenVisible for organization settings/user/tenant prerequisitesVisible for organization Syrve connection registry/managementRead/manage prerequisites; no direct posting approval
AccountantHiddenHidden for admin settingsConnection registry and credentials mutation hiddenVisible for assigned-scope payment workflow subject to gates
ViewerHiddenHidden for admin settingsRead/status only where scoped permissions allowRead-only views only
PaymentOperatorNot MVP / hiddenNot MVP / hiddenNot MVP / hiddenNot MVP / deny
LocationAdminNot MVP / hiddenNot MVP / hiddenNot MVP / hiddenNot MVP / deny

Permission-denied expectations:

  • If a role lacks the required permission, the sidebar item should be hidden.
  • If the user enters a route directly, frontend guards must show permission denied or redirect according to the route policy.
  • Backend scoped checks remain the source of truth; frontend visibility must never be treated as authorization.
  • API calls against resources outside the user's organization/Outlet scope must still fail or be concealed according to endpoint policy.

Active Syrve Connection Context ​

syrveConnectionId is not a permission scope. It is frontend UI/navigation context used to remember and switch the selected Syrve connection on connection-scoped screens.

Security rules:

  • MVP RBAC scopes remain Platform / Organization / Outlet.
  • UserRoleAssignment must not receive ConnectionId.
  • Legacy active-context keys remain organization/Outlet-only; there is no legacy Syrve connection key.
  • Selected Syrve connection must be validated as belonging to the allowed organization/Outlet context by backend read/write paths.
  • Active Syrve connection context does not replace organization/Outlet tenant checks.
  • Payment workflow routes remain Outlet-scoped and outside /syrve/connections/:id.

Backend permission constants ​

The canonical runtime scoped permission names are defined in ScopedPermissionNames in backend/src/MyHoreca.Pay.Domain/Enums/ScopedRbacEnums.cs.

Implementation note for MHPAY-86/MHPAY-87: GET /api/audit-log is read-only and append-only. GET /api/audit-log/export.csv and GET /api/audit-log/export.pdf reuse the same scoped audit query. MegaAdmin / MegaManager can query/export global audit with optional organization/location filters. OrganizationAdmin, Accountant, and Viewer can query/export only audit entries inside their effective organization/location scope; explicit foreign organization or location filters return a safe empty result. Audit export writes a safe audit event and exports only masked audit fields. Final hard delete is service-only backend foundation and remains MegaAdmin only.

MHPAY-51 does not add new permission-name constants. It updates the role permission profiles and seeds that consume the existing constants.

Current runtime constants ​

ConstantPermission name
PlatformOrganizationsViewplatform.organizations.view
PlatformOrganizationsManageplatform.organizations.manage
PlatformQuotasManageplatform.quotas.manage
PlatformSettingsViewplatform.settings.view
PlatformSettingsManageplatform.settings.manage
PlatformUsersManageplatform.users.manage
PlatformAuditViewAllplatform.audit.view_all
PlatformConnectionsViewAllplatform.connections.view_all
PlatformDiagnosticsRunplatform.diagnostics.run
PlatformProductionWriteApproveFutureplatform.payments.production_write_approve_future
OrganizationVieworganization.view
OrganizationUsersManageorganization.users.manage
OrganizationLocationsManageorganization.locations.manage
OrganizationQuotasVieworganization.quotas.view
OrganizationConnectionsManageorganization.connections.manage
OrganizationConnectionsTestorganization.connections.test
SyrveCredentialsRotatesyrve.credentials.rotate
OrganizationDocumentsSyncorganization.documents.sync
OrganizationAuditVieworganization.audit.view
OrganizationPaymentsVieworganization.payments.view
OrganizationPaymentsApproveorganization.payments.approve
OrganizationPaymentsTestWriteorganization.payments.test_write
LocationViewlocation.view
LocationUsersManagelocation.users.manage
LocationBankStatementsUploadlocation.bank_statements.upload
LocationBankStatementsViewlocation.bank_statements.view
LocationBankStatementsQualityOverridelocation.bank_statements.quality_override
BankFilesDownloadbankFiles.download
LocationTransactionsViewlocation.transactions.view
LocationTransactionsMatchlocation.transactions.match
LocationCounterpartiesManagelocation.counterparties.manage
LocationAllocationsPreparelocation.allocations.prepare
LocationAllocationsConfirmlocation.allocations.confirm
LocationPaymentsViewlocation.payments.view
LocationPaymentsApprovelocation.payments.approve
LocationPaymentsQueuelocation.payments.queue
LocationPaymentsRetrylocation.payments.retry
LocationPaymentsReconcilelocation.payments.reconcile
LocationEvidenceViewlocation.evidence.view
LocationAnomaliesViewlocation.anomalies.view
LocationAnomaliesManagelocation.anomalies.manage
LocationManualCorrectionsViewlocation.manual_corrections.view
LocationManualCorrectionsManagelocation.manual_corrections.manage
LocationNotificationsViewlocation.notifications.view

Target documentation / implementation follow-up ​

These names are target documentation only. They are not current runtime constants until a future implementation task updates ScopedPermissionNames, seeds, policies, tests, and docs:

Target permissionReason
organization.sub_organizations.manageExplicit sub-organization management separate from generic organization management.
organization.outlets.manageProduct terminology alignment with Outlet while runtime still uses location.*.
organization.matching_rules.manageExplicit organization-level matching rule management.
organization.diagnostics.viewOrganization-scoped diagnostics without platform-wide diagnostics.
organization.workers.viewOrganization-scoped worker/job visibility.
organization.evidence.viewOrganization-level evidence read permission.
organization.notifications.viewOrganization-level notifications read permission.

Future implementation must either map these to current scoped constants deliberately or add them with tests. Do not treat this table as already implemented behavior.

RBAC test scenarios ​

ScenarioExpected result
MegaAdmin can manage platform organizations, quotas, users, settings, diagnostics, and view global audit.Allow.
MegaManager can onboard/support organizations and view support diagnostics.Allow within support scope.
MegaManager cannot approve production write.Deny.
OrganizationAdmin can manage users, Outlets, credentials, matching rules, and manual counterparty matches inside own organization and inherited child organizations.Allow within scope.
OrganizationAdmin assigned to a parent organization can create and manage child organizations without creating a fake Outlet.Allow within inherited organization scope.
OrganizationAdmin cannot access another organization.Deny with 403 or conceal with 404 where read-path concealment is intentional.
Accountant can import, review, match, allocate, approve test write, queue/retry/reconcile, manage anomalies, and view evidence within assigned scope.Allow only within scope and safety gates.
Accountant can download an original bank file for an import in the assigned organization/Outlet before retention expiry.Allow only with bankFiles.download and matching scope.
Accountant cannot manage users, quotas, platform settings, credentials, or bypass Critical anomaly blocks.Deny.
Viewer can read visible resources only.Allow read-only within scope.
Viewer cannot download original bank files, upload, match, allocate, approve, retry, reconcile, mutate credentials, manage users, or manage rules.Deny.
PaymentOperator has no MVP permissions.Deny for all MVP permission checks.
LocationAdmin has no MVP permissions.Deny for all MVP permission checks.
Cross-tenant read attempts are made against resources in another organization.Deny or conceal according to endpoint policy.
Cross-tenant write attempts are made against resources in another organization.Deny; no mutation and audit/security signal where appropriate.
Background workers execute queued work.Allow only system-authorized, auditable work; workers must not bypass user approval, tenant scope, idempotency, preflight, anomaly, or reconciliation gates.

Out of scope for future RBAC iterations ​

  • Runtime backend changes.
  • Frontend changes.
  • EF migrations.
  • New permission-name constants, route guards, frontend permission UX, or migrations without a dedicated implementation task.
  • Real bank files, secrets, raw bank lines, full account numbers, or private customer data.
  • Unrelated code or documentation changes.
  • New business role decisions without a separate ADR.

Document recognition permissions ​

PermissionScopeCapability
location.bank_statements.recognition.correctOrganization / OutletEdit typed drafts and append correction events.
location.bank_statements.recognition.confirmOrganization / OutletConfirm or reject a reviewed document.
location.bank_statements.recognition.retryOrganization / OutletRetry failed or lease-expired recognition.
platform.recognition_learning.managePlatformReview anonymized examples and freeze global datasets.
platform.recognition_models.managePlatformRecord evaluation, canary, activate and rollback a global model.

Preview continues to require existing statement view and file download permissions. Tenant users cannot access another tenant's original, run, rows, corrections, candidates or audit. Only platform permissions expose anonymized examples, whose exported payloads contain no tenant ids.