# ADR-015 : Transferts de stock entre entrepôts

**Statut :** Accepté
**Date :** 2026-10-15
**Contexte :** Lot 8 de la feuille de route restauration (« bar modélisé en entrepôt avec transferts »), sur le socle des mouvements typés (ADR-007) et du CMUP (ADR-014)

---

## Contexte

Un maquis stocke ses casiers en réserve et sert au bar. L'ADR-006 et l'ADR-010 ont volontairement gardé un stock **par entrepôt**, avec des emplacements de comptage seulement, pour ne pas imposer la saisie de chaque mouvement entre réserve et bar. Certains établissements veulent pourtant suivre le bar à part, avec son propre stock et son propre écart d'inventaire, ou font tourner deux points de vente. Il faut alors pouvoir **déplacer du stock d'un entrepôt à l'autre** sans passer par une fausse vente et un faux achat.

## Décision

### 1. Un transfert, deux mouvements typés

- Table `stock_transfers` : référence, entrepôt d'origine, entrepôt de destination, date, notes, auteur.
- Table `stock_transfer_items` : produit, quantité, **coût unitaire figé** (le CMUP de l'origine au moment du transfert, ADR-014).
- Chaque ligne écrit, dans une seule transaction, un mouvement `transfer_out` (sortie de l'origine) et un mouvement `transfer_in` (entrée dans la destination). La ligne est leur source. Les motifs étant distincts, la contrainte unique `(source_type, source_id, reason)` garantit qu'une ligne ne s'applique qu'une fois de chaque côté.
- Les deux mouvements sont immuables (ADR-007). Un transfert erroné se corrige par un **transfert inverse** : la trace des deux déplacements reste visible.

### 2. Le coût suit la marchandise

L'entrée `transfer_in` porte le coût figé de l'origine. Le CMUP de la destination la traite **comme une réception** : il la pondère par le stock présent. La sortie `transfer_out` ne change pas le CMUP de l'origine, comme toute sortie. Le stock du tenant garde ainsi sa valeur totale : on déplace de la valeur, on n'en crée pas.

### 3. Règles

- Origine et destination sont deux entrepôts distincts **du même tenant**. Un utilisateur qui n'est pas admin ne transfère qu'au départ de son entrepôt.
- Seuls les produits stockés se transfèrent : un plat composé n'a pas de stock (ADR-013).
- La date n'est ni dans le futur, ni dans une période clôturée de l'**un ou l'autre** entrepôt (ADR-007 §3).
- Le stock théorique de l'origine **n'est pas un plafond** : un écart de comptage ne doit pas empêcher d'enregistrer un déplacement physique réel. L'inventaire suivant révélera l'écart.

### 4. Accès

| Action | Permission | Garde |
|---|---|---|
| Voir les transferts de l'entrepôt (entrants et sortants) | `stock_transfers_view` | `check.feature:multi_warehouse` |
| Créer un transfert | `stock_transfers_create` | `check.feature:multi_warehouse` |

Les deux permissions sont accordées au rôle `admin`. Le personnel n'en a pas par défaut : déplacer du stock change l'écart d'inventaire de deux entrepôts.

### Révision du 27/10/2026 : fonctionnalité de plan à part entière

Les transferts deviennent une fonctionnalité de plan propre, `plans.has_stock_transfer` (`Feature::StockTransfer`), réglable par le superadmin. Elle **suppose le multi-entrepôts** (dépendance dans `FeatureGate`) : sans au moins deux entrepôts, un transfert n'a pas de sens. La garde des deux routes devient `check.feature:stock_transfer`. La migration reprend le droit actuel : toute formule multi-entrepôts reçoit les transferts (Business, Entreprise, démo restauration), aucun client ne les perd. L'écran, le menu, la page d'abonnement et les cartes de tarifs suivent `stock_transfer_enabled`.

## Alternatives Considérées

### Une vente depuis l'origine et un achat dans la destination
Rejeté. Cela fausse le chiffre d'affaires, les dettes fournisseurs et la marge, et ne garde aucun lien entre les deux côtés.

### Transfert en deux temps (expédié, puis reçu)
Différé. Utile entre sites éloignés, inutile entre la réserve et le bar d'un même maquis. L'ajout restera possible avec un statut et une réception, sur le modèle de l'ADR-012.

### Stock par emplacement à l'intérieur d'un entrepôt
Rejeté, comme dans les ADR-006 et ADR-010. L'entrepôt reste l'unité de stock ; qui veut suivre le bar à part en fait un entrepôt.

## Conséquences

- Nouvelles tables `stock_transfers` et `stock_transfer_items`, nouveaux motifs `transfer_out` et `transfer_in`.
- Le CMUP traite `transfer_in` comme une entrée avec coût.
- Nouvel écran « Transferts », visible si le plan donne les transferts (révision du 27/10/2026).
- Nouvelles permissions `stock_transfers_view` et `stock_transfers_create`, accordées aux rôles `admin` existants.

## Quand Réviser

- Si des transferts entre sites distants doivent être contrôlés à l'arrivée : statut « expédié » puis réception, comme les achats (ADR-012).
