> ## Documentation Index
> Fetch the complete documentation index at: https://docs.laportenard.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Permissions

> How the role-based permission system works across frontend and backend.

## User roles

| Role    | Odoo group          | Access level                                 |
| ------- | ------------------- | -------------------------------------------- |
| Manager | `group_pos_manager` | Full access, price overrides, void authority |
| Cashier | `group_pos_user`    | Standard POS, payments, split checks         |
| Waiter  | (fallback)          | Order-taking only                            |

## Permission resolution

Permissions resolve through three layers, in order:

<Steps>
  <Step title="Factory defaults">
    Defined in `permission_registry.py` (`PERMISSION_DEFAULTS`) and mirrored in `permissions.ts` on the frontend.
  </Step>

  <Step title="Per-config role overrides">
    The `nu.pos.permission.config` model allows overriding defaults per POS configuration and role.
  </Step>

  <Step title="Per-user grant/deny">
    The `nu.pos.permission.user.override` model allows granting or denying specific permissions to individual users.
  </Step>
</Steps>

The bootstrap endpoint resolves permissions server-side and sends a flat `permissions: Record<string, boolean>` map per user.

## Frontend permission check

```typescript theme={null}
// Primary: check resolved permissions map
hasPermission(user.permissions, 'void_item')

// Fallback: legacy FeatureGuards (when permissions is null)
canPerformAction('voidItem', user)
```

The `canPerformAction()` function bridges the legacy `FeatureGuards` system to the new permission system via an `ACTION_TO_PERMISSION` mapping.

## Adding a new permission

<Steps>
  <Step title="Add the backend default">
    Add the key and default value to `PERMISSION_DEFAULTS` in `nu_restaurant_pos/models/permission_registry.py`.
  </Step>

  <Step title="Mirror on the frontend">
    Add the same key and default to `PERMISSION_DEFAULTS` in `src/domain/users/permissions.ts`.
  </Step>

  <Step title="Use in code">
    Check the permission with `hasPermission()` on the frontend or `resolve_user_permissions()` on the backend.
  </Step>
</Steps>

<Warning>
  Both Python and TypeScript `PERMISSION_DEFAULTS` must stay in sync. A missing key on either side causes silent failures.
</Warning>

## Security notes

* Permission configuration models are writable only by `base.group_system` (admin) — managers cannot self-grant privileges
* Role resolution uses `user.has_group()`, never `config.read()` group ID extraction (the `read()` output format is fragile)
* The frontend rejects non-string role values to guard against Python `False` → JSON `false` propagation
