# 01 — Organization & Access

## 1. Organizational hierarchy

```
Organization (the cafe company)
│
├── Branch: Baghdad — MAIN            ← owner is based here; head-office functions
│     └── Building: <workspace name>  ← source of tenant accounts
├── Branch: Baghdad — <second site>
├── Branch: Basrah  — <site>
├── Branch: Karbala — <site>
└── Branch: <fifth site>
```

There is exactly **one organization**. Branches are its only structural children.
No regions, no franchises — but branches carry a `region` label (`Baghdad` / `Basrah` /
`Karbala`) from day one so a region layer can be added later without migrating data.

### Branch

| Field | Notes |
|---|---|
| `code` | Short, unique, human-typed. Prefixes order numbers, e.g. `BGD1`, `BSR`, `KRB`. |
| `name_ar` / `name_en` | Both required. Receipts and invoices are Arabic-first. |
| `is_main` | Exactly one branch. Grants head-office visibility, not extra sale rights. |
| `region` | Baghdad / Basrah / Karbala. |
| `building_id` | The workspace property this branch operates inside. |
| `timezone` | `Asia/Baghdad` for all branches. Stored per branch anyway. |
| `currencies_accepted` | e.g. `[IQD]` or `[IQD, USD]`. |
| `business_day_cutoff` | Local time the trading day rolls over (e.g. `04:00`). |
| `opening_hours` | Default daily hours. **Every day is a trading day** — closures are exceptions declared by the owner, not a weekly pattern ([10 §3.1](10-cross-cutting-rules.md)). |
| `status` | `active` / `suspended` / `closed`. Closed branches keep their history. |

**Rule:** a branch is never deleted. Closing sets `status = closed`; all historical sales,
invoices and expenses remain queryable and still appear in period reports covering the
time it traded.

### Building

The workspace property. Separate from the branch because it owns the **tenant list** — the
accounts entitled to charge at that branch.

| Field | Notes |
|---|---|
| `name`, `address`, `city` | |
| `offices[]` | Office identifiers (`201`, `305`, `Reception`) used for delivery-to-office |
| `operator_contact` | Building management contact, for collections escalation |
| `branch_id` | The cafe branch serving this building (1:1 in practice) |

---

## 2. Users

A **user** is a person who logs in. A user belongs to the organization and gains access to
**one or more branches** through role assignments.

| Field | Notes |
|---|---|
| `full_name_ar` / `full_name_en` | |
| `phone` | Primary staff identifier in Iraq; used for recovery |
| `email` | Optional |
| `pin` | 4–6 digits, hashed. Fast terminal login. |
| `password` | Back-office users only (Owner, Accountant, Manager) |
| `status` | `active` / `suspended` / `terminated` |
| `employee_id` | Optional link to the payroll employee record (doc 08) |

### Two authentication modes — deliberately different

| Mode | Who | Why |
|---|---|---|
| **PIN on shared terminal** | Cashier, Barista, Storekeeper | The terminal stays signed in as a *branch device*; staff identify per action-batch with a PIN. Cashiers change every few minutes — email+password would be unusable. |
| **Password (+ optional OTP)** | Owner, Accountant, Branch Manager | Back office, reporting, money movement, master data. |

**Rules**
- A PIN is unique **within a branch**, not globally.
- PIN login reaches POS / KDS / stock only. It can never reach financial configuration.
- A device is registered to a branch (`device_id`, `branch_id`). A PIN works only on devices
  belonging to a branch that user is assigned to.
- Terminal auto-locks after N seconds idle (configurable, default 60s).

---

## 3. Roles & permissions

Permissions are **capability-based**, and every check is evaluated against a **branch
scope**. A role assignment is always `(user, role, branch | ALL_BRANCHES)`.

### Standard roles

| Role | Scope | Capability summary |
|---|---|---|
| **Owner** | ALL | Everything: cross-branch financials, credit limits, user management, price master, branch creation. |
| **Accountant** | ALL | Billing runs, invoices, receipts, write-offs (with approval), payables, payroll, rent, expense approval, all financial reports. **No POS sales.** |
| **Branch Manager** | One branch | Own-branch operations: staffing, stock, purchase orders, discounts, void/refund approval, own reports, local price overrides (if enabled). |
| **Cashier** | One branch | Open/close own shift, create orders, take payment, charge to account within limits, request void/refund, print receipts. |
| **Barista / Kitchen** | One branch | View and progress the preparation queue. No money access. |
| **Storekeeper** | One branch | Receive goods, record waste, initiate/accept transfers, stock counts. No money access. |

### Capability matrix (abridged)

| Capability | Owner | Accountant | Manager | Cashier | Barista | Store |
|---|:--:|:--:|:--:|:--:|:--:|:--:|
| `sale.create` | ✓ | — | ✓ | ✓ | — | — |
| `sale.charge_to_account` | ✓ | — | ✓ | ✓ | — | — |
| `sale.void` | ✓ | — | ✓ | request | — | — |
| `sale.refund` | ✓ | ✓ | ✓ | request | — | — |
| `sale.discount.apply` | ✓ | — | ✓ | ≤ limit | — | — |
| `shift.open_close` | ✓ | — | ✓ | ✓ (own) | — | — |
| `shift.review_variance` | ✓ | ✓ | ✓ | — | — | — |
| `account.create` | ✓ | ✓ | ✓ | — | — | — |
| `account.set_credit_limit` | ✓ | ✓ | — | — | — | — |
| `account.waive_or_write_off` | ✓ | request | — | — | — | — |
| `billing.run` | ✓ | ✓ | — | — | — | — |
| `payment.receive` | ✓ | ✓ | ✓ | ✓ | — | — |
| `inventory.receive` | ✓ | — | ✓ | — | — | ✓ |
| `inventory.adjust` | ✓ | — | ✓ | — | — | ✓ |
| `inventory.transfer.send` | ✓ | — | ✓ | — | — | ✓ |
| `purchase_order.approve` | ✓ | ✓ | ✓ ≤ limit | — | — | — |
| `expense.record` | ✓ | ✓ | ✓ | — | — | — |
| `expense.approve` | ✓ | ✓ | — | — | — | — |
| `payroll.run` | ✓ | ✓ | — | — | — | — |
| `catalog.edit_global` | ✓ | — | — | — | — | — |
| `catalog.edit_branch_overrides` | ✓ | — | ✓ | — | — | — |
| `calendar.declare_closure` | ✓ | — | request | — | — | — |
| `report.branch` | ✓ | ✓ | ✓ (own) | — | — | — |
| `report.consolidated` | ✓ | ✓ | — | — | — | — |
| `user.manage` | ✓ | — | ✓ (own branch, non-financial roles) | — | — | — |

`request` = the actor may initiate, but it takes effect only after approval by a higher
role. Approval is captured as a supervisor PIN on the terminal (stored as
`approved_by_user_id`), or asynchronously in the back office.

### Hard rules

1. **Branch scoping is enforced at the data layer.** Every query for branch-owned data
   receives the caller's permitted branch set. A missing scope is an error, never "all".
2. **No self-approval.** `approved_by_user_id` must differ from `performed_by_user_id`.
3. **The last remaining Owner cannot be demoted or deleted.**
4. **Accountants cannot sell; cashiers cannot bill.** Separating the person who takes the
   order from the person who invoices and collects it is the primary internal control in a
   credit-selling business.
5. **Branch access can be time-bounded** (`valid_from`, `valid_to`) so temporary cover
   between branches does not become permanent access.

---

## 4. Audit

Every state-changing action writes an immutable audit event:

```
audit_event {
  id, occurred_at_utc, occurred_at_local, business_day,
  org_id, branch_id, device_id, shift_id?,
  actor_user_id, approved_by_user_id?,
  entity_type, entity_id, action,
  before?, after?,            // for master-data edits
  reason_code?, note?,
  source: pos | back_office | sync | system
}
```

Append-only, synced to the centre with the same guarantees as sales, retained indefinitely.
See [10 — Cross-Cutting Rules](10-cross-cutting-rules.md).

**Always audited:** price changes, credit-limit changes, voids, refunds, discounts,
write-offs, stock adjustments, shift variance, payroll edits, user/permission changes, and
opening the cash drawer without a sale.
