---
title: "Point of Sale"
description: "Commerce et restauration. Chaque application est un module sur un même modèle de données."
canonical: "https://obop.dev/fr/apps/pos/"
lang: "fr"
---

# Point of Sale

## À quoi elle sert

Model a shop till: configuration, cash-controlled sessions, orders with lines and payment lines per method. It records what happened at the till; it is not a payment terminal integration, and card numbers must never be stored.

## Ce que fait et ne fait pas le paquet

Shop tills: a point-of-sale configuration, cash-controlled sessions that are opened and closed, orders with lines, and payment lines per method. A refund and the closing of a session need a human approval from a manager; agents may only propose them. Line subtotals and the order total are computed from the lines, receipts get a gapless yearly number, and paying is refused until the payments cover the total; change and cash differences are still entered. Paying also creates a draft stock move per line from the order's stock location, for the warehouse to validate. Card numbers must never be stored; keep only the payment provider's reference. Needs the Products package.

## Détails du module

- **Version:** `0.3.0`
- **Auteur:** SpaceCorps
- **Licence:** [AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later.html)
- **Noyau requis:** `^0.2` (le noyau actuel est 0.2.0)
- **Maturité:** Expérimental
- **Catégories:** Ventes

## Modèles

### `pos_config`

A point of sale (a till or a shop) and its rules

Groupe de lecture: `pos.user`. Groupe d'écriture: `pos.manager`.

| Champ | Type | Classe de données | Données personnelles | Groupe | Vu par les agents | Description |
|---|---|---|---|---|---|---|
| `name` | text | `none` | non | `pos.user` | en clair | Name of the till or shop, for example Main street shop |
| `currency` | text | `none` | non | `pos.user` | en clair | ISO 4217 currency code, for example EUR |
| `receipt_header` | longtext | `none` | non | `pos.user` | en clair | Text printed at the top of receipts |
| `cash_control` | bool | `none` | non | `pos.user` | en clair | Whether sessions must count the cash in the drawer when they open and close |
| `default_opening_cash` | money | `none` | non | `pos.user` | en clair | Cash normally left in the drawer overnight (minor currency units) |
| `allow_refunds` | bool | `none` | non | `pos.user` | en clair | Whether cashiers may propose refunds at this point of sale |
| `active` | bool | `none` | non | `pos.user` | en clair | Whether the point of sale is in use |

### `pos_session`

One shift of selling at a point of sale, from opening to closing the cash drawer

Groupe de lecture: `pos.user`. Groupe d'écriture: `pos.user`.

| Champ | Type | Classe de données | Données personnelles | Groupe | Vu par les agents | Description |
|---|---|---|---|---|---|---|
| `config_id` | ref → pos_config | `none` | non | `pos.user` | en clair | The point of sale the session belongs to |
| `cashier` | text | `internal` | oui | `pos.user` | masqué | Name of the cashier who opened the session |
| `opened_on` | date | `none` | non | `pos.user` | en clair | Day the session was opened (YYYY-MM-DD) |
| `closed_on` | date | `none` | non | `pos.user` | en clair | Day the session was closed (YYYY-MM-DD) |
| `opening_cash` | money | `financial` | non | `pos.user` | non communiqué | Cash counted in the drawer at opening (minor currency units) |
| `expected_cash` | money | `financial` | non | `pos.user` | non communiqué | Cash the drawer should hold at closing: opening cash plus cash payments (entered, not computed) |
| `closing_cash` | money | `financial` | non | `pos.user` | non communiqué | Cash actually counted in the drawer at closing |
| `cash_difference` | money | `financial` | non | `pos.user` | non communiqué | Closing cash minus expected cash; negative means money is missing |
| `note` | longtext | `none` | non | `pos.user` | en clair | Explanation for any cash difference |
| `state` | select (open, closed) | `none` | non | `pos.user` | en clair | Session status: open, closed |

### `pos_order`

A sale rung up at a point of sale

Groupe de lecture: `pos.user`. Groupe d'écriture: `pos.user`.

| Champ | Type | Classe de données | Données personnelles | Groupe | Vu par les agents | Description |
|---|---|---|---|---|---|---|
| `session_id` | ref → pos_session | `none` | non | `pos.user` | en clair | The session the sale was made in |
| `receipt_number` | sequence | `none` | non | `pos.user` | en clair | Number printed on the receipt, such as R-2026-000042: a gapless series that starts again each year, assigned when the sale is created |
| `stock_location_id` | ref → inventory_location | `none` | non | `pos.user` | en clair | Where the goods leave stock; paying creates the stock moves from this location |
| `ordered_on` | date | `none` | non | `pos.user` | en clair | Day of the sale (YYYY-MM-DD) |
| `customer_id` | ref → party | `none` | non | `pos.user` | en clair | The customer, when one was identified |
| `tax_total` | money | `none` | non | `pos.user` | en clair | Tax included in the total (minor currency units) |
| `total` | money | `none` | non | `pos.user` | en clair | Total paid by the customer including tax: the sum of the line subtotals (minor currency units) |
| `note` | longtext | `none` | non | `pos.user` | en clair | Remark about the sale |
| `state` | select (draft, paid, refunded, cancelled) | `none` | non | `pos.user` | en clair | Sale status: draft, paid, refunded, cancelled |

### `pos_order_line`

One product line of a sale

Groupe de lecture: `pos.user`. Groupe d'écriture: `pos.user`.

| Champ | Type | Classe de données | Données personnelles | Groupe | Vu par les agents | Description |
|---|---|---|---|---|---|---|
| `order_id` | ref → pos_order | `none` | non | `pos.user` | en clair | The sale the line belongs to |
| `product_id` | ref → product | `none` | non | `pos.user` | en clair | The product sold |
| `description` | text | `none` | non | `pos.user` | en clair | Text shown on the receipt |
| `quantity` | number | `none` | non | `pos.user` | en clair | Units sold |
| `unit_price` | money | `none` | non | `pos.user` | en clair | Price per unit including tax (minor currency units) |
| `discount_bp` | number | `none` | non | `pos.user` | en clair | Discount in basis points: 1000 means 10.00 percent |
| `subtotal` | money | `none` | non | `pos.user` | en clair | Line total after discount: quantity times unit price, less the discount (minor currency units) |

### `pos_payment`

A payment received against a sale; a sale may be paid with several methods

Groupe de lecture: `pos.user`. Groupe d'écriture: `pos.user`.

| Champ | Type | Classe de données | Données personnelles | Groupe | Vu par les agents | Description |
|---|---|---|---|---|---|---|
| `order_id` | ref → pos_order | `none` | non | `pos.user` | en clair | The sale the payment is for |
| `method` | select (cash, card, bank_transfer, voucher, other) | `none` | non | `pos.user` | en clair | How the customer paid: cash, card, bank_transfer, voucher, other |
| `amount` | money | `financial` | non | `pos.user` | non communiqué | Amount paid with this method (minor currency units) |
| `paid_on` | date | `none` | non | `pos.user` | en clair | Day of the payment (YYYY-MM-DD) |
| `provider_reference` | text | `financial` | oui | `pos.user` | non communiqué | Reference from the card terminal or payment provider; never a card number |

## Flux de travail

### `pos_session`

| Action | Transition | Qui peut l'exécuter | Approbation |
|---|---|---|---|
| `pos_session.close` | state: open → closed | `pos.manager` | oui |

### `pos_order`

| Action | Transition | Qui peut l'exécuter | Approbation |
|---|---|---|---|
| `pos_order.pay` | state: draft → paid | `pos.user` | non |
| `pos_order.cancel` | state: draft → cancelled | `pos.user` | non |
| `pos_order.refund` | state: paid → refunded | `pos.manager` | oui |

## Groupes de permissions

Groupes utilisés par cette application :

- `pos.manager`
- `pos.user`

## Étapes d'approbation

Étapes qu'un agent ne peut que proposer :

- `pos_session.close`
- `pos_order.refund`

## Ce que les agents peuvent et ne peuvent pas faire

Un agent peut lire, rechercher, créer et modifier les enregistrements de cette application, et exécuter les actions qui ne requièrent pas d'approbation, dans les limites des groupes de permissions de son jeton.

Il ne peut que proposer ces étapes, qu'une personne doit approuver : `pos_session.close`, `pos_order.refund`, `pos_config.delete`, `pos_session.delete`, `pos_order.delete`, `pos_order_line.delete`, `pos_payment.delete`.

Les données personnelles sont masquées sous forme de jetons de substitution : `pos_session.cashier`.

Ces champs ne sont pas communiqués aux agents : `pos_session.opening_cash`, `pos_session.expected_cash`, `pos_session.closing_cash`, `pos_session.cash_difference`, `pos_payment.amount`, `pos_payment.provider_reference`.

## Dépendances

Chaque dépendance indique les versions qu’elle accepte. Les applications intégrées suivent les versions du noyau : une exigence à leur égard est donc vérifiée par rapport à la version du noyau.

- [Contacts](/fr/apps/contacts/): `^0.2` (intégré)
- [Inventory](/fr/apps/inventory/): `>=0.3, <1`
- [Products](/fr/apps/products/): `^0.2` (intégré)

Installer cette application depuis la place de marché installe aussi les dépendances manquantes, dans un seul plan à confirmer une fois.

## Installation

Installez-la depuis l'application Customize (Place de marché), ou en ligne de commande :

```bash
obop modules install-from pos
```

Nécessite une base de données et des droits d'administrateur.

## Créer la vôtre

[Le guide des modules montre comment sont écrites les applications de ce type](/fr/docs/reference/module-manifest/)
