> ## 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.

# User roles

> Three-tier role system for managers, cashiers, and waiters.

The POS system implements a three-tier role system derived from Odoo group membership. Roles control access to features, payment processing, price overrides, and reporting.

## Roles

<CardGroup cols={3}>
  <Card title="Manager" icon="shield">
    Full access to all POS features. Price overrides, refunds, settings, reports, and all cashier functions. Maps to `point_of_sale.group_pos_manager`.
  </Card>

  <Card title="Cashier" icon="cash-register">
    Standard POS operations — orders, payments, split checks, discounts. Cannot override price restrictions. Maps to `point_of_sale.group_pos_user`.
  </Card>

  <Card title="Waiter" icon="utensils">
    Order-taking only. Can create orders and add items. Cannot process payments or perform manager actions. Fallback when neither group is set.
  </Card>
</CardGroup>

## Role assignment

Roles are resolved server-side during bootstrap:

```python theme={null}
# Priority order:
# 1. group_pos_manager → role = 'manager'
# 2. group_pos_user   → role = 'cashier'
# 3. (fallback)       → role = 'waiter'
```

The resolved role is included in the bootstrap payload `user.role` and in `pos_core_data.pos_users[].role` for all POS users.

## Feature guards

Use `canPerformAction()` from `src/domain/users/roleGuards.ts` to check access:

```typescript theme={null}
import { canPerformAction, checkFeatureAccess, FeatureGuards } from "@/domain/users/roleGuards";

const canRefund  = canPerformAction(currentRole, "refund");
const canDiscount = canPerformAction(currentRole, "discount");
const canEditPrice = checkFeatureAccess(currentRole, FeatureGuards.priceEdit);
```

### Predefined feature guards

| Feature         | Manager | Cashier           | Waiter |
| --------------- | ------- | ----------------- | ------ |
| `priceEdit`     | ✓       | ✗ (if restricted) | ✗      |
| `discount`      | ✓       | ✓                 | ✗      |
| `refund`        | ✓       | ✗                 | ✗      |
| `payment`       | ✓       | ✓                 | ✗      |
| `createOrder`   | ✓       | ✓                 | ✓      |
| `modifyOrder`   | ✓       | ✓                 | ✓      |
| `settings`      | ✓       | ✗                 | ✗      |
| `cashDrawer`    | ✓       | ✓                 | ✗      |
| `switchUser`    | ✓       | ✓                 | ✓      |
| `reports`       | ✓       | ✗                 | ✗      |
| `reprint`       | ✓       | ✗                 | ✗      |
| `close_session` | ✓       | ✗                 | ✗      |

## Role utilities

`src/domain/users/types.ts`:

```typescript theme={null}
import { isManager, isCashier, hasRoleLevel } from "@/domain/users/types";

// Check specific role
if (isManager(currentRole)) {
  enablePriceOverrides();
}

// Hierarchy check (manager > cashier > waiter)
if (hasRoleLevel(currentRole, "cashier")) {
  showPaymentInterface();
}
```

## User switching

The `SwitchUserLockScreen` component provides full-screen lock with PIN and/or RFID card authentication. Dispatching `SWITCH_USER` to the session machine updates `context.currentRole`:

```typescript theme={null}
send({ type: "SWITCH_USER", user: selectedUser });
// currentRole automatically updates to selectedUser.role
```

### Login method configuration

Each terminal (`pos.config`) has a `nu_login_method` field that controls which authentication methods the lock screen allows:

| Value           | PIN numpad | RFID card reader | Users shown                      |
| --------------- | ---------- | ---------------- | -------------------------------- |
| `pin` (default) | ✓          | ✗                | Users with a PIN set             |
| `rfid`          | ✗          | ✓                | Users with an RFID card assigned |
| `both`          | ✓          | ✓                | Users with a PIN or card         |

On the frontend, `parseLockConfig()` reads `nu_login_method` from bootstrap and derives `pinEnabled` and `rfidEnabled` booleans on `LockConfig`.

<Note>
  RFID card assignment is managed via the Settings dialog (RFID section, visible to managers when the login method includes RFID). Cards are stored in the standard Odoo `res.users.barcode` field.
</Note>

## Permissions system

The newer permissions system resolves per-user permission maps server-side (see [architecture/permissions](/architecture/permissions)). `canPerformAction()` bridges legacy feature guards to the permission map:

```typescript theme={null}
// Checks permission map first, falls back to FeatureGuards
canPerformAction(role, "voidItem", user.permissions)
```

## Testing

111 tests across 4 files in `nu_pos_react/`:

| File                                        | Tests | Coverage                          |
| ------------------------------------------- | ----- | --------------------------------- |
| `src/domain/users/types.test.ts`            | 21    | Role utilities, hierarchy         |
| `src/state/machines/sessionMachine.test.ts` | 14    | State transitions, user switching |
| `src/lib/mockBootstrapData.test.ts`         | 26    | Mock data validation              |
| `src/domain/users/roleGuards.test.ts`       | 50    | Feature guards, access control    |

```bash theme={null}
cd nu_pos_react
npm test -- src/domain/users/
```
