# Consigne générique — cadrage produit et technique

## Statut du document
- Statut : brouillon de référence
- Objectif : centraliser la réflexion sur la **prise** et le **retour** de consigne
- Périmètre initial : POS / caisse
- Principe : document vivant, à faire évoluer avant et pendant l'implémentation

---

## 1. Contexte métier

Cas concret initial :
- une bière est vendue `3 €`
- une ecocup est consignée `1 €`
- le client peut :
  - acheter une boisson **avec prise de consigne**
  - revenir plus tard pour **rendre une ou plusieurs consignes**
  - rendre des consignes **sans autre achat**

Conséquence métier :
- la **prise de consigne** augmente le montant encaissé
- le **retour de consigne** diminue le cash de la caisse

Le besoin n'est donc pas seulement un "montant négatif" :
- c'est un **sous-système métier de consigne**
- avec deux flux distincts mais liés :
  - **prise**
  - **retour**

---

## 2. Objectifs

### Objectifs fonctionnels
- gérer un **système générique de consigne**
- permettre la **prise de consigne** pendant une vente
- permettre le **retour de consigne** même sans achat simultané
- imposer que le **retour soit remboursé en espèces uniquement**
- faire agir le retour sur le **fond de caisse**
- garder une **traçabilité claire** en historique

### Objectifs produit
- ne pas complexifier inutilement le tunnel de vente
- garder une UX claire pour le caissier
- séparer ce qui relève de :
  - la **vente**
  - la **sortie de caisse**

### Objectifs techniques
- éviter d'introduire une "commande négative" en v1
- éviter d'intégrer le retour de consigne comme une ligne de panier négative
- conserver un modèle extensible pour d'autres consignes que l'ecocup

---

## 3. Hypothèses retenues à ce stade

- la consigne doit devenir un **système générique**
- le **retour de consigne est toujours remboursé en cash**
- le retour de consigne doit créer une **sortie de caisse**
- la prise de consigne doit pouvoir être faite **en même temps qu'une vente**
- le retour de consigne doit pouvoir être fait :
  - seul
  - ou en même temps qu'une vente
- on privilégie une **v1 simple et sûre**

---

## 4. Recommandation d'architecture (version cible v1)

## Décision recommandée
Mettre en place un **système dédié de consigne** avec séparation métier entre :

### A. Prise de consigne
- fait partie du **flux de vente**
- visible côté caisse pendant la construction / validation d'une commande
- augmente le montant du ticket

### B. Retour de consigne
- **ne fait pas partie d'une commande négative**
- crée une **opération de caisse sortante dédiée**
- agit sur le fond de caisse
- remboursement **cash uniquement**

### C. Cas mixte (achat + retour de consigne)
- UX unifiée au moment du checkout
- mais logique métier séparée :
  - commande de vente normale
  - remboursement de consigne séparé

---

## 5. Pourquoi on évite la commande négative en v1

### Option écartée pour la v1
Traiter le retour de consigne comme :
- une commande à total négatif
- ou une ligne négative dans le panier

### Raisons
Cette approche complexifie fortement :
- `Order.total`
- `OrderItem.unitPrice`
- `Payment.amount`
- le flux d'encaissement
- les statuts de commande
- les exports et le reporting
- la lisibilité métier

### Conclusion
En v1, le retour de consigne est mieux représenté par une **sortie de caisse dédiée** que par une commande négative.

---

## 6. Modèle métier cible

## 6.1. Définition de consigne
Introduire un concept générique de type :
- `DepositDefinition`
- ou `ConsignmentDefinition`

Propriétés minimales envisagées :
- `id`
- `label`
- `amount` (centimes)
- `active`
- `displayOrder`
- éventuellement `code`, `imageUrl`, `description`

Exemples :
- Ecocup 25cl → `100`
- Ecocup 50cl → `100`
- Pichet → `300`

## 6.2. Flux de prise
La prise de consigne représente :
- une somme ajoutée à la vente
- visible sur le ticket / récapitulatif

## 6.3. Flux de retour
Le retour de consigne représente :
- une restitution d'espèces
- une opération de caisse sortante
- un mouvement traçable en historique

---

## 7. Recommandation frontend

## 7.1. Principe général
Le **panier** reste dédié à la vente.

Le **retour de consigne** ne doit **pas** être intégré comme une ligne produit négative dans le panier.

## 7.2. Point d'entrée pressenti
Le point d'entrée peut être proche de `CashierBrowsingPhase.vue`, car on est dans l'univers de la caisse.

Mais la recommandation actuelle est :
- **oui** pour un point d'accès dans la caisse
- **non** pour intégrer le retour de consigne directement au `cartStore`

## 7.3. Prise de consigne côté frontend
Deux possibilités ont été identifiées :

### Option A — ajout explicite d'une consigne à la vente
Le caissier ajoute manuellement :
- type de consigne
- quantité

### Option B — suggestion semi-automatique
Quand un produit concerné est ajouté (ex: bière pression), l'UI suggère :
- `Ajouter une consigne ?`

### Recommandation v1
Commencer par une **prise explicite** ou semi-guidée, pas automatique stricte.

## 7.4. Retour de consigne côté frontend
Recommandation :
- **modal dédiée**
- flux distinct du panier
- accessible depuis la caisse / checkout
- sélection du type de consigne
- quantité
- total à rembourser
- confirmation

## 7.5. Cas mixte dans l'UX
Le checkout devrait afficher :
- total vente
- consignes prises
- consignes rendues
- **net à encaisser / net à rendre**

Donc :
- UX potentiellement unifiée
- logique métier séparée

---

## 8. Recommandation backend

## 8.1. Retour de consigne
Le retour doit être persisté comme une **opération de caisse dédiée**.

### Recommandation
Créer un type métier dédié du style :
- `DEPOSIT_REFUND`
- ou `CONSIGNMENT_REFUND`

Plutôt qu'un simple `MANUAL_OUT` sans sémantique forte.

### Pourquoi
- historique clair
- reporting futur plus propre
- distinction métier explicite
- moins d'ambiguïté côté cash session

## 8.2. Prise de consigne
La prise doit être reliée à la vente.

Selon la stratégie retenue plus tard, elle peut être modélisée comme :
- une ligne dédiée dans le flux de commande
- ou un sous-ensemble spécialisé du ticket de vente

## 8.3. Impact cash session
Le **retour de consigne** doit :
- décrémenter le cash attendu
- apparaître en historique
- être associé à la session de caisse active
- rester limité à l'espèce

---

## 9. Règles métier proposées

## 9.1. Retour de consigne
- remboursable **en espèces uniquement**
- quantité strictement positive
- type de consigne actif obligatoire
- session de caisse ouverte obligatoire
- opération traçable avec auteur et station

## 9.2. Prise de consigne
- quantité strictement positive
- type de consigne actif obligatoire
- visible dans le récapitulatif de vente
- compte dans le total à encaisser

## 9.3. Cas mixte
Exemple :
- vente = `6 €`
- consignes prises = `2 €`
- consignes rendues = `1 €`
- net = `7 €`

Autre exemple :
- vente = `2 €`
- consignes rendues = `4 €`
- net = `-2 €`
- dans ce cas, l'UI doit clairement afficher :
  - `À rendre au client : 2 € en espèces`

---

## 10. UX cible v1 (recommandée)

## 10.1. Pendant la vente
Le caissier peut :
- ajouter des produits
- ajouter une ou plusieurs consignes prises

## 10.2. Pendant le checkout
Le caissier peut :
- ajouter un retour de consigne
- voir le net final
- comprendre immédiatement si :
  - il doit encaisser
  - il doit rendre du cash

## 10.3. Historique
L'historique de caisse doit afficher des lignes explicites, par exemple :
- `Remboursement consigne`
- `Ecocup × 3`
- `-3,00 €`

---

## 11. Décisions provisoires

### Décisions déjà bien orientées
- système **générique** de consigne : **oui**
- retour en **cash uniquement** : **oui**
- impact sur le fond de caisse : **oui**
- type dédié préférable pour le retour : **oui**
- éviter la commande négative en v1 : **oui**

### Orientation frontend retenue à ce stade
- `CashierBrowsingPhase.vue` peut servir de **point d'entrée côté caisse**
- mais le **retour de consigne ne doit pas être intégré au panier**
- il faut un **flux dédié via modal dédiée + opération de caisse sortante**

### Orientation produit retenue à ce stade
- **prise de consigne** = composante de la vente
- **retour de consigne** = opération de caisse cash dédiée

---

## 12. Questions ouvertes

1. Quel nom métier retenir ?
   - `consigne`
   - `deposit`
   - `caution`
   - `returnable deposit`

2. La prise de consigne doit-elle être :
   - manuelle
   - suggérée
   - automatique selon certains produits

3. Veut-on dès la v1 un écran de gestion des types de consigne ?
   - oui / non

4. Faut-il relier explicitement les retours de consigne aux prises historiques ?
   - pas nécessaire en v1
   - potentiellement utile en v2

5. Le ticket doit-il afficher explicitement les lignes de consigne ?
   - probablement oui pour la prise
   - à confirmer pour le retour selon le support utilisé

---

## 13. Plan d'implémentation proposé

## Phase 1 — cadrage produit
- valider les décisions de ce document
- choisir les noms métier finaux
- décider du niveau de généricité v1

## Phase 2 — modèle de données
- introduire le concept de définition de consigne
- introduire le type dédié de retour de consigne
- cadrer la persistance de la prise de consigne dans le flux de vente

## Phase 3 — backend
- prise de consigne dans le flux de vente
- retour de consigne via opération de caisse dédiée
- validations métier
- historisation

## Phase 4 — frontend caisse
- point d'entrée prise de consigne
- modal de retour de consigne
- récapitulatif net au checkout
- affichage historique

## Phase 5 — stabilisation
- tests métier
- tests edge cases
- vérification impacts cash session / clôture de caisse
- ajustements UX

---

## 14. Recommandation finale actuelle

### Recommandation v1
Implémenter un **système générique de consigne** avec :

- **prise de consigne** intégrée au flux de vente
- **retour de consigne** via une opération de caisse **cash-only** dédiée
- un **type métier dédié** pour le retour
- un **checkout capable de présenter un net global** sans mélanger techniquement les deux flux

### Résumé en une phrase
> La consigne doit être pensée comme un sous-système métier dédié : la prise appartient à la vente, le retour appartient à la caisse.

---

## Historique du document
- 2026-04-28 : création du document de cadrage initial

---

## 15. Phase 1 — cadrage produit v1 à figer

Cette section sert de **base de validation produit** avant de passer au modèle de données.

## 15.1. Décisions v1 proposées

### Décision 1 — séparation métier stricte
- la **prise de consigne** appartient à la **vente**
- le **retour de consigne** appartient à la **caisse**

### Décision 2 — pas de commande négative en v1
- aucun retour de consigne ne doit créer une commande à total négatif
- aucun retour de consigne ne doit être représenté comme une ligne panier négative

### Décision 3 — retour cash uniquement
- le retour de consigne est **remboursé en espèces uniquement**

### Décision 4 — type métier dédié
- le retour de consigne doit créer une **opération de caisse dédiée**
- recommandation technique actuelle : type du style `DEPOSIT_REFUND`

### Décision 5 — affichage net unifié au checkout
- le checkout doit afficher séparément :
  - la vente
  - les consignes prises
  - les consignes rendues
  - le **net final**
- mais la persistance reste séparée

### Décision 6 — généricité v1 pragmatique
- le système est **générique dans son modèle**
- mais le périmètre fonctionnel v1 reste volontairement simple

### Décision 7 — vocabulaire produit
- côté UI et métier, le terme retenu est **consigne**
- côté code, un nom technique anglais cohérent reste recommandé

## 15.2. Périmètre v1

### In scope
- prise de consigne pendant une vente
- retour de consigne sans achat
- cas mixte achat + retour de consigne
- types de consigne actifs / inactifs
- affichage explicite de la consigne sur le récapitulatif de vente
- affichage explicite du retour dans l'historique de caisse
- affichage du net final :
  - **à encaisser**
  - ou **à rendre en espèces**

### Out of scope
- appairage précis entre un retour et une prise historique
- automatisation stricte par produit dès la v1
- écran d'administration complet des types de consigne
- remboursement par carte
- reporting avancé dédié à la consigne
- gestion stock / logistique des contenants

## 15.3. Règles métier non négociables

### Retour de consigne
- remboursable **en espèces uniquement**
- session de caisse ouverte obligatoire
- type de consigne actif obligatoire
- quantité strictement positive
- mouvement de caisse traçable obligatoire

### Prise de consigne
- type de consigne actif obligatoire
- quantité strictement positive
- visible dans le récapitulatif de vente
- intégrée au total à encaisser

### Règle de sécurité caisse
- le système ne doit pas permettre un retour qui ferait passer le cash attendu sous zéro

## 15.4. Parcours UX v1 recommandé

### Cas 1 — vente avec prise de consigne
Le caissier :
- ajoute les produits
- ajoute une ou plusieurs consignes prises
- voit ces consignes dans le récapitulatif
- encaisse un total augmenté

### Cas 2 — retour de consigne seul
Le caissier :
- ouvre une action dédiée depuis l'univers caisse
- sélectionne le type de consigne
- saisit la quantité
- voit le total à rembourser
- confirme l'opération
- remet l'espèce au client

### Cas 3 — passage mixte
Le caissier :
- construit une vente normale
- ajoute un retour de consigne au moment du checkout
- voit distinctement :
  - la vente
  - les consignes prises
  - les consignes rendues
  - le net final

### Règle d'affichage du net
- si le net est positif : afficher **À encaisser**
- si le net est négatif : afficher **À rendre au client en espèces**

## 15.5. Cas d'usage prioritaires à couvrir

1. acheter un produit avec une consigne
2. acheter plusieurs produits avec plusieurs consignes
3. rendre une consigne sans achat
4. rendre plusieurs consignes sans achat
5. acheter et rendre en même temps avec net positif
6. acheter et rendre en même temps avec net négatif
7. refuser un retour sans session de caisse ouverte
8. refuser un retour si le type de consigne est inactif
9. refuser une quantité vide, nulle ou négative
10. refuser un retour si le cash attendu devient négatif

## 15.6. Critères d'acceptation v1

- un caissier peut vendre avec consigne sans détourner le panier produit standard
- un caissier peut rembourser une consigne sans créer de commande négative
- le retour est historisé comme mouvement de caisse métier dédié
- le checkout affiche distinctement vente, prise, retour et net final
- un net négatif est supporté proprement en UX
- la prise de consigne apparaît explicitement dans le support de vente
- le retour apparaît explicitement dans l'historique de caisse
- les validations bloquantes sont bien appliquées
- le modèle reste extensible à plusieurs types de consigne

## 15.7. Arbitrages proposés pour clore la phase 1

### Nom métier
- UI / produit : **consigne**
- technique : `DepositDefinition` + `DEPOSIT_REFUND`

### Mode de prise v1
- en v1 simple : **manuel explicite** possible partout
- à terme, deux comportements doivent être supportés selon le couple **station + produit + consigne** :
  - **consigne indissociable / imposée à la vente**
  - **consigne suggérée / retirable par le caissier**
- éviter de porter cette règle au niveau global de `DepositDefinition`

### Administration v1
- pas d'écran d'admin dédié en v1
- initialisation via données préparées / seed / configuration

### Lien historique prise ↔ retour
- non requis en v1
- reporté en v2 si besoin métier confirmé

### Support ticket
- oui pour la **prise** de consigne sur le ticket de vente
- pas obligatoire pour un ticket séparé de **retour** en v1

## 15.8. Lien produit ↔ consigne ↔ station

Cette section cadre l'idée de **lier une ou plusieurs consignes à un produit vendu dans le contexte d'une station caisse**.

### Intuition produit
Exemple :
- une bière 50cl peut suggérer une consigne `Ecocup 50cl`
- une limonade en bouteille verre peut imposer une consigne `Bouteille verre`

L'objectif n'est pas de rendre la consigne automatique à tout prix.
L'objectif est de **réduire les oublis**, de **simplifier la saisie caisse** et de **refléter les cas où produit et contenant sont matériellement indissociables**.

### Deux catégories produit à distinguer

#### A. Consigne indissociable / imposée à la vente
Exemple :
- une limonade vendue dans une bouteille verre consignée

Dans ce cas :
- la consigne fait partie du scénario de vente normal
- elle doit être ajoutée par défaut à la commande
- elle n'est pas censée être retirée librement par le caissier
- si une exception existe, elle doit relever d'une règle métier explicite

#### B. Consigne suggérée / retirable
Exemple :
- une bière servie en ecocup alors que le client peut déjà avoir sa propre ecocup

Dans ce cas :
- la consigne est préconisée par défaut
- mais le caissier doit pouvoir la retirer très facilement
- la logique reste assistée, pas bloquante

### Bénéfices attendus
- moins d'oubli de consigne par le caissier
- parcours de vente plus rapide
- meilleure cohérence entre produit vendu et consigne proposée
- possibilité de préremplissage ou de suggestion dans le détail commande / checkout

### Risques si c'est mal conçu
- ajout automatique non désiré alors que le client possède déjà son contenant
- couplage trop rigide entre produit et consigne
- difficulté à gérer plusieurs consignes pour un même produit
- impossibilité de gérer des différences selon la station
- confusion entre une consigne réellement obligatoire et une simple suggestion UX

### Recommandation produit
La bonne approche n'est **pas** :
- une règle unique pour tous les types de consigne liés à un produit

La bonne approche est plutôt :
- un **lien configuré** entre un produit et une ou plusieurs consignes
- dans le **contexte d'une station**
- avec un **mode de comportement** explicite par lien :
  - **indissociable / imposé**
  - **suggéré / retirable**

Conséquence UX :
- une consigne **indissociable** est ajoutée d'office à la vente
- une consigne **suggérée** est ajoutée ou proposée par défaut, mais reste facilement supprimable

### Recommandation v1
Pour la v1, si cette idée est introduite, elle peut prendre deux formes :
- **assistée et retirable** pour les contenants optionnels
- **pré-appliquée et non librement retirable** pour les contenants matériellement indissociables

Autrement dit :
- la caisse peut suggérer une ou plusieurs consignes liées à certains produits
- la caisse peut aussi imposer certaines consignes quand la vente du produit est matériellement indissociable du contenant consigné
- le niveau de liberté du caissier dépend donc du **mode configuré sur le lien produit ↔ consigne**

### Recommandation de périmètre
Le meilleur endroit pour porter cette logique semble être :
- le couple **station + produit**

Plutôt que :
- un lien global rigide `Product -> DepositDefinition`

Pourquoi :
- un même produit peut ne pas avoir la même logique de consigne selon le contexte opérationnel
- certaines stations peuvent vouloir activer, désactiver ou surcharger les consignes suggérées

### Recommandation UX caisse
Dans le POS, le comportement recommandé est :
- pour une consigne **indissociable** : l'afficher comme déjà ajoutée à la commande
- pour une consigne **suggérée** : l'afficher comme suggestion ou présélection facilement retirable
- permettre une action rapide du type :
  - `Ajouter les consignes suggérées`
  - ou validation ligne par ligne
  - ou suppression rapide d'une consigne suggérée si le client apporte déjà son contenant

### Décision provisoire
Le document recommande donc :
- **oui** à un lien produit ↔ consigne à terme
- **oui** à un pilotage par station
- **oui** à deux comportements distincts : `indissociable` et `suggéré`
- **oui** à une logique de suggestion / préremplissage pour les cas optionnels
- **oui** à une application imposée pour les cas matériellement indissociables

## 15.9. Conclusion de phase 1

Si cette phase 1 est validée, la **phase 2** doit consister à définir précisément :
- les entités de consigne
- le type d'opération de caisse dédié au retour
- la persistance de la prise de consigne dans le flux de vente
- les contraintes minimales de cohérence caisse / checkout

---

## 16. Phase 2 — proposition de modèle de données v1

Cette section propose le **modèle de persistance cible v1** compatible avec les décisions de phase 1.

## 16.1. Principe général de persistance

On conserve une séparation stricte entre :

- la **prise de consigne**, persistée dans le **flux de vente**
- le **retour de consigne**, persisté dans le **flux de caisse**

Le cas mixte n'introduit donc pas une entité "commande négative".
Il produit :

- une `Order` normale
- une `CashSessionOperation` dédiée au remboursement

## 16.2. Entité de référence — `DepositDefinition`

### Rôle
Définir un type de consigne réutilisable dans tout le shop.

### Champs minimaux recommandés
- `id`
- `shopId`
- `label`
- `amount` (centimes)
- `active`
- `displayOrder`
- `code` optionnel
- `description` optionnelle
- `createdAt`
- `updatedAt`

### Relations recommandées
- `Shop 1 -> n DepositDefinition`
- `DepositDefinition 1 -> n OrderDepositLine`
- `DepositDefinition 1 -> n CashSessionOperationDepositLine`

### Intention produit
Le modèle est générique même si la v1 démarre avec peu de types de consigne.

### Complément possible — association produit / station / consigne
Si l'équipe décide d'introduire une consigne liée à un produit, la recommandation n'est pas de surcharger directement `Product` avec un seul champ de consigne.

La recommandation cible serait plutôt une table dédiée du style :
- `StationProductDepositLink`

### Décision de modélisation recommandée
La recommandation est de créer une **table dédiée** `StationProductDepositLink`.

Il est préférable de **ne pas** surcharger `StationProductLink`, car :
- `StationProductLink` répond aujourd'hui à une logique de **présence du produit sur une station**
- la consigne introduit une logique différente :
  - multiplicité possible
  - ordre d'affichage
  - comportement `REQUIRED` / `SUGGESTED`
  - retirabilité éventuelle côté caisse

Les deux liens doivent donc rester séparés :
- `StationProductLink` → visibilité / rattachement du produit à la station
- `StationProductDepositLink` → comportement de consigne pour ce produit sur cette station

### Enum recommandé
Nom technique recommandé :
- `StationProductDepositApplicationMode`

Valeurs recommandées :
- `REQUIRED`
- `SUGGESTED`

Champs pressentis :
- `id`
- `stationId`
- `productId`
- `depositDefinitionId`
- `defaultQuantity`
- `displayOrder`
- `applicationMode` (`REQUIRED`, `SUGGESTED`)
- `isRemovableByCashier`
- `autoAddEnabled`
- `createdAt`
- `updatedAt`

### Contraintes recommandées
- unicité métier sur `stationId + productId + depositDefinitionId`
- index sur `stationId + productId`
- suppression cascade si la station ou le produit est supprimé
- pas de duplication du même type de consigne pour un même produit sur une même station

### Relations Prisma recommandées
- `Station 1 -> n StationProductDepositLink`
- `Product 1 -> n StationProductDepositLink`
- `DepositDefinition 1 -> n StationProductDepositLink`

### Intention des champs de comportement
- `applicationMode = REQUIRED`
  - cas d'une consigne matériellement liée à la vente
  - exemple : bouteille verre consignée vendue avec une limonade
- `applicationMode = SUGGESTED`
  - cas d'une consigne recommandée mais non systématique
  - exemple : ecocup proposée pour une bière
- `isRemovableByCashier`
  - `false` pour une consigne indissociable en fonctionnement normal
  - `true` pour une consigne suggérée
- `autoAddEnabled`
  - permet de distinguer une simple suggestion affichée d'une présélection ajoutée automatiquement

### Pourquoi cette approche est préférable
- elle permet plusieurs consignes pour un même produit
- elle laisse la configuration dépendre de la station
- elle évite de figer un comportement global au niveau produit
- elle reste compatible avec la logique actuelle de `OrderDepositLine`
- elle permet d'exprimer proprement la différence entre une consigne imposée et une consigne simplement proposée

### Position recommandée pour la roadmap
- **v1** : pas indispensable pour lancer le système de consigne
- **v1.5 / v2** : très bonne extension UX une fois le socle vente + caisse stabilisé

### Contrats partagés recommandés si cette extension est lancée
Dans `ttm-shared`, l'extension la plus propre serait d'ajouter :

#### Enums
- `StationProductDepositApplicationMode`

#### DTO
- `StationProductDepositLinkDto`
  - `id`
  - `stationId`
  - `productId`
  - `depositDefinitionId`
  - `depositLabel`
  - `depositAmount`
  - `defaultQuantity`
  - `displayOrder`
  - `applicationMode`
  - `isRemovableByCashier`
  - `autoAddEnabled`

#### Schémas Zod
- un schéma ligne du style `StationProductDepositLinkInputSchema`
- un schéma batch du style `ReplaceStationProductDepositLinksSchema`

### Flux API recommandé
Pour rester cohérent avec le reste du projet, la recommandation serait un endpoint dédié du style :
- `PUT /stations/:stationId/products/:productId/deposit-links`

Comportement recommandé :
- remplace toutes les règles de consigne d'un produit pour une station donnée
- valide que le produit appartient bien au shop
- valide que les `DepositDefinition` sont actives et du bon shop
- retourne la configuration à jour

## 16.3. Prise de consigne côté vente — `OrderDepositLine`

### Rôle
Tracer les consignes **prises** dans une commande sans détourner `OrderItem`.

### Pourquoi ne pas utiliser `OrderItem`
- une consigne n'est pas un produit de production
- elle ne doit pas entrer dans les statuts cuisine / runner
- elle ne doit pas polluer la logique de logistique produit

### Champs minimaux recommandés
- `id`
- `orderId`
- `depositDefinitionId`
- `labelSnapshot`
- `unitAmountSnapshot`
- `quantity`
- `totalAmount`
- `createdAt`

### Snapshots recommandés
Même si `DepositDefinition` change plus tard, la commande doit conserver :
- le libellé vendu au moment du ticket
- le montant unitaire au moment du ticket

### Relations recommandées
- `Order 1 -> n OrderDepositLine`
- `DepositDefinition 1 -> n OrderDepositLine`

## 16.4. Retour de consigne côté caisse — `CashSessionOperationDepositLine`

### Rôle
Détailler les consignes remboursées derrière une opération de caisse.

### Pourquoi ne pas se limiter à `metadata`
Un JSON libre serait rapide, mais moins propre pour :
- la lisibilité métier
- les requêtes futures
- le reporting ultérieur
- la validation de structure

### Champs minimaux recommandés
- `id`
- `cashSessionOperationId`
- `depositDefinitionId`
- `labelSnapshot`
- `unitAmountSnapshot`
- `quantity`
- `totalAmount`
- `createdAt`

### Convention recommandée
- `totalAmount` reste **positif** au niveau ligne
- l'opération parent `CashSessionOperation.amountDelta` porte la **valeur négative** globale

### Relations recommandées
- `CashSessionOperation 1 -> n CashSessionOperationDepositLine`
- `DepositDefinition 1 -> n CashSessionOperationDepositLine`

## 16.5. Extension recommandée de `CashSessionOperationType`

### Nouveau type métier
Ajouter un type dédié :

- `DEPOSIT_REFUND`

### Pourquoi
- éviter de noyer la consigne dans `MANUAL_OUT`
- clarifier l'historique métier
- préparer les lectures, exports et stats futures

## 16.6. Évolutions recommandées des entités existantes

## `Shop`
Ajouter la relation :
- `depositDefinitions`

## `Order`
Ajouter :
- relation `depositLines`
- champ `depositTotal` (centimes, défaut `0`)

### Pourquoi ajouter `depositTotal`
Le code actuel recalcule `Order.total` uniquement à partir des `OrderItem` puis de `discountTotal`.
Or, en v1 :

- les **promotions** doivent continuer à s'appliquer au panier produit
- les **consignes prises** doivent s'ajouter au total final

La formule recommandée devient donc :

`total = subtotalProduits - discountTotal + depositTotal`

### Conséquence
`OrderItem` reste inchangé.
La consigne de vente n'entre pas dans le sous-système de production.

## `CashSessionOperation`
Ajouter :
- relation `depositLines`

### Recommandation complémentaire
Un champ optionnel `relatedOrderId` peut être envisagé pour les cas mixtes.

### Position recommandée
- **optionnel**
- utile pour l'audit
- **non obligatoire** pour lancer la v1

## 16.7. Stratégie de calcul recommandée

## Côté vente

### Source de vérité
- les `OrderItem` portent le sous-total produit
- les `OrderDepositLine` portent le sous-total consigne
- `discountTotal` reste séparé

### Formule recommandée
- `productSubtotal = somme des OrderItem non annulés`
- `depositTotal = somme des OrderDepositLine`
- `total = max(productSubtotal - discountTotal, 0) + depositTotal`

### Point d'attention
Le `max(..., 0)` ne doit pas effacer la consigne.
La borne à zéro s'applique à la partie produit après remise, puis on rajoute la consigne.

## Côté caisse

### Source de vérité
Le retour de consigne est une `CashSessionOperation` de type `DEPOSIT_REFUND` avec :

- `amountDelta < 0`
- `countsTowardExpectedCash = true`
- `resultingExpectedCash` recalculé

### Règle de sécurité
On conserve le garde-fou existant :
- interdiction si `resultingExpectedCash < 0`

## 16.8. Cas mixte — persistance recommandée

Pour un passage combinant achat + retour :

### Persistance
- une `Order`
- éventuellement un ou plusieurs `Payment`
- une `CashSessionOperation` de type `DEPOSIT_REFUND`

### Important
Même si l'UI affiche un **net final**, les écritures doivent rester séparées.

### Conséquence métier
Le checkout orchestre plusieurs mouvements, mais le modèle de données reste cohérent avec :
- la vente
- la caisse

## 16.9. Impacts techniques identifiés sur l'existant

## Backend `order`
Le service de commande devra être ajusté pour que tous les recalculs de total intègrent :
- les `OrderItem`
- `discountTotal`
- `depositTotal`

Cela concerne notamment les flux qui recalculent déjà `Order.total`.

## Backend `cashSession`
Le service de caisse pourra réutiliser la mécanique existante de calcul de `expectedCash`, à condition d'ajouter :
- le nouveau type `DEPOSIT_REFUND`
- une création dédiée de mouvement de caisse de consigne
- les lignes détaillées de remboursement

## Contrats partagés `ttm-shared`
Il faudra faire évoluer :
- les enums de type `CashSessionOperationType`
- les DTO de commande pour exposer la consigne prise
- les DTO d'historique caisse pour exposer la consigne remboursée si nécessaire
- les schémas Zod dédiés aux flux de consigne

### Point important
Le schéma générique actuel de création d'opération de caisse ne couvre que :
- `MANUAL_IN`
- `MANUAL_OUT`

La recommandation v1 est donc de créer un **flux dédié consigne** plutôt que de détourner ce schéma.

## 16.10. Ordre d'implémentation recommandé

### Lot 1 — Prisma
- ajouter `DepositDefinition`
- ajouter `OrderDepositLine`
- ajouter `CashSessionOperationDepositLine`
- ajouter `DEPOSIT_REFUND`
- ajouter `depositTotal` sur `Order`

### Lot 2 — contrats partagés
- enums
- DTO
- schémas Zod d'entrée / sortie

### Lot 3 — backend métier
- service de prise de consigne dans le flux de vente
- service de retour de consigne dans le flux de caisse
- recalculs de total et d'expected cash

### Lot 4 — lectures / historique
- lecture des lignes de consigne sur la commande
- lecture des lignes de remboursement dans l'historique caisse
- préparation du checkout mixte

## 16.11. Points de vigilance migration

### Totaux de commande
Le plus gros point de vigilance est la cohérence de tous les recalculs de `Order.total`.

### Paiement / checkout
Les flux de paiement ne doivent pas être cassés par l'introduction de `depositTotal`.

### Zéro euro
Les traitements spéciaux de commande à `0 €` doivent rester cohérents une fois la consigne ajoutée.

### Historique caisse
Le retour de consigne doit apparaître clairement sans casser l'historique existant des mouvements manuels.

## 16.12. Recommandation finale de phase 2

Le meilleur compromis v1 est :

- `DepositDefinition` pour la référence métier
- `OrderDepositLine` pour la prise de consigne
- `CashSessionOperationDepositLine` pour le retour de consigne
- `DEPOSIT_REFUND` pour le type de sortie de caisse
- `depositTotal` sur `Order` pour garder un calcul de total propre et explicite

### Résumé
> En base, la consigne doit être modélisée comme une donnée métier dédiée, avec une persistance distincte entre vente et caisse, sans jamais détourner la commande en écriture négative.

---

## 17. Phase 3 — plan backend v1 détaillé

Cette phase décrit **comment brancher le modèle de consigne dans le backend existant** sans casser les flux `Order`, `Payment` et `CashSession`.

## 17.1. Principe backend à respecter

Le backend v1 doit toujours respecter cette séparation :

- **prise de consigne** → flux `Order`
- **retour de consigne** → flux `CashSessionOperation`

Le checkout mixte reste un **assemblage de deux écritures métier**, jamais une seule écriture comptable négative.

## 17.2. Services backend à modifier

## A. Module `order`

Le module commande doit être étendu pour gérer les consignes prises.

### Impacts attendus
- exposer les lignes de consigne dans la sérialisation des commandes
- recalculer `depositTotal`
- recalculer `total` avec la nouvelle formule
- garantir que la consigne n'entre jamais dans la logique cuisine / production / runner

### Recommandation technique
Créer une logique dédiée du style :
- `setDepositLines(orderId, lines)`
- ou `replaceDepositLines(orderId, lines)`
- ou un helper interne de synchronisation des lignes de consigne

### Décision importante
Ne pas mélanger l'ajout produit et l'ajout de consigne dans les mêmes structures d'entrée.

## B. Module `cashSession`

Le module caisse doit être étendu pour créer des remboursements de consigne dédiés.

### Impacts attendus
- nouveau flux `DEPOSIT_REFUND`
- validation métier spécifique
- création des lignes détaillées de remboursement
- recalcul de `resultingExpectedCash`

### Décision importante
Ne pas détourner le flux actuel de création de mouvements manuels génériques si cela oblige à traiter la consigne comme un simple `MANUAL_OUT`.

## C. Module `payment`

Le module paiement ne porte pas la logique de consigne, mais doit rester compatible avec :
- le nouveau calcul de `Order.total`
- le cas où le checkout affiche un net global

### Décision importante
Le paiement continue de payer une **commande positive**.
Le remboursement de consigne ne devient pas un `Payment` négatif.

## 17.3. Helpers backend recommandés

## A. Helper unique de recalcul de commande

La recommandation forte est d'introduire un helper backend unique responsable du recalcul :

- `productSubtotal`
- `discountTotal`
- `depositTotal`
- `total`

### Pourquoi
Aujourd'hui, plusieurs flux recalculent `Order.total` directement.
Avec la consigne, ce risque de divergence augmente fortement.

### Recommandation
Créer un point central de recalcul utilisé après :
- ajout produit
- suppression / décrément produit
- remplacement d'items
- application / revalidation promo
- modification des consignes prises

## B. Helper de remboursement de consigne

Créer un helper métier dédié qui :
- charge la session ouverte
- vérifie les types de consigne
- calcule le remboursement total
- bloque si `resultingExpectedCash < 0`
- crée l'opération de caisse et ses lignes

## 17.4. Flux API recommandés en v1

## A. Flux commande — prise de consigne

### Recommandation
Ajouter un endpoint dédié côté POS pour modifier les consignes d'une commande.

Exemples de direction API possibles :
- `PATCH /orders/:id/deposits`
- ou `PUT /orders/:id/deposits`

### Comportement recommandé
- remplace l'ensemble des lignes de consigne prises
- recalcule `depositTotal`
- recalcule `total`
- retourne la commande sérialisée à jour

### Pourquoi un endpoint dédié
- plus clair métier
- plus simple à valider
- évite de polluer les endpoints produit existants

## B. Flux caisse — retour de consigne

### Recommandation
Ajouter un endpoint dédié côté caisse du style :
- `POST /cash-session/sessions/:id/deposit-refunds`

### Comportement recommandé
- vérifie la session ouverte
- vérifie les lignes de retour demandées
- calcule le total remboursé
- crée une `CashSessionOperation` de type `DEPOSIT_REFUND`
- crée les lignes détaillées associées
- retourne la session mise à jour + le mouvement créé

### Pourquoi un endpoint dédié
- évite de forcer `CreateCashSessionOperationSchema` à absorber un cas métier trop spécifique
- garde un contrat API explicite

## C. Cas mixte — recommandation d'orchestration

### Recommandation v1
Le checkout mixte doit être orchestré par le frontend via **deux appels distincts** :

1. validation / mise à jour de la commande
2. création du remboursement de consigne si nécessaire

### Pourquoi
- plus simple à brancher sur l'existant
- moins risqué qu'un endpoint composite transactionnel dès la v1
- plus lisible pour le debug

### Position recommandée
Pas d'endpoint composite global en v1, sauf besoin bloquant découvert plus tard.

## 17.5. Validations métier backend obligatoires

## Prise de consigne
- commande existante obligatoire
- commande non verrouillée / non incompatible avec modification
- type de consigne actif obligatoire
- quantité strictement positive
- montant snapshot cohérent au moment de l'écriture

## Retour de consigne
- session de caisse ouverte obligatoire
- type de consigne actif obligatoire
- quantité strictement positive
- total remboursé strictement positif
- remboursement cash uniquement
- `resultingExpectedCash >= 0`

## Paiement
- le paiement d'une commande continue de se faire sur `Order.total`
- aucun `Payment` ne doit être créé pour matérialiser le retour de consigne

## 17.6. Impacts sur les recalculs existants

## A. Recalcul de `Order.total`

Tous les points qui recalculent aujourd'hui `Order.total` devront intégrer `depositTotal`.

### Cela concerne en particulier
- ajout d'items
- décrément / suppression
- remplacement d'items
- recalcul promo
- tout helper générique de recalcul de commande

## B. Recalcul promo

Le moteur promo doit continuer à travailler sur les **produits**.

### Règle recommandée
- les promos recalculent `discountTotal` à partir des `OrderItem`
- ensuite le recalcul commande ajoute `depositTotal`

### Point important
La consigne ne doit pas entrer dans les remises produit en v1.

## C. Recalcul de `expectedCash`

Le moteur existant de caisse peut être conservé si `DEPOSIT_REFUND` est créé comme une opération avec :

- `amountDelta < 0`
- `countsTowardExpectedCash = true`

## 17.7. Contrats partagés à faire évoluer

La phase backend impose aussi des évolutions dans `ttm-shared`.

### Enums
- ajout de `DEPOSIT_REFUND` dans `CashSessionOperationType`

### DTO commande
- exposer les lignes de consigne prises
- exposer `depositTotal`

### DTO caisse
- permettre d'exposer les lignes détaillées d'un remboursement de consigne si nécessaire

### Schémas Zod
- créer un schéma dédié pour les lignes de consigne prises
- créer un schéma dédié pour le remboursement de consigne

### Décision recommandée
Éviter d'élargir le schéma générique manuel si cela dilue la sémantique métier.

## 17.8. Ordre d'implémentation backend recommandé

### Lot backend 1 — contrats et Prisma alignés
- schéma Prisma appliqué
- client Prisma régénéré
- enums partagés alignés
- DTO / schémas Zod créés

### Lot backend 2 — commande
- lecture des lignes de consigne dans les commandes
- endpoint de modification des consignes prises
- helper de recalcul commande unifié

### Lot backend 3 — caisse
- endpoint dédié au remboursement de consigne
- création `CashSessionOperation` + lignes détaillées
- contrôle de non-négativité du cash attendu

### Lot backend 4 — checkout / paiements
- vérification du comportement paiement avec `depositTotal`
- vérification du cas mixte
- ajustements des retours DTO finaux

## 17.9. Points de vigilance backend

## A. Risque principal — divergence de total
Si plusieurs endroits continuent à recalculer `Order.total` chacun de leur côté, la consigne introduira vite des incohérences.

### Décision recommandée
Centraliser le recalcul le plus tôt possible.

## B. Risque principal — confusion entre mouvement manuel et remboursement consigne
Si la consigne est branchée comme un simple `MANUAL_OUT`, l'historique métier deviendra ambigu.

### Décision recommandée
Créer `DEPOSIT_REFUND` dès la première itération backend.

## C. Risque principal — paiement et net global
Le frontend affichera un net, mais le backend ne doit pas confondre :
- montant de commande à payer
- montant de cash à rendre

### Décision recommandée
Le backend reste strictement séparé entre vente et caisse.

## D. Cas zéro euro
Les traitements existants de commande gratuite doivent être retestés une fois `depositTotal` introduit.

## 17.10. Critères de done backend v1

Le backend v1 est considéré prêt si :

- une commande peut porter des consignes prises persistées proprement
- `Order.total` intègre correctement `depositTotal`
- les promos continuent de s'appliquer uniquement aux produits
- un retour de consigne crée un mouvement `DEPOSIT_REFUND`
- `expectedCash` est décrémenté correctement
- un retour impossible car caisse négative est bloqué
- les DTO exposent les informations nécessaires au frontend caisse
- le cas mixte fonctionne sans commande négative ni paiement négatif

## 17.11. Recommandation finale de phase 3

La stratégie backend v1 recommandée est donc :

- un endpoint dédié pour les consignes prises sur `Order`
- un endpoint dédié pour les retours sur `CashSession`
- un recalcul centralisé de `Order.total`
- un type métier `DEPOSIT_REFUND`
- une orchestration checkout mixte en **deux appels**

### Résumé
> Côté backend, la consigne doit être ajoutée comme une extension maîtrisée des flux `Order` et `CashSession`, sans casser les endpoints existants ni diluer la sémantique métier dans des mouvements manuels génériques.

## Historique du document
- 2026-04-28 : création du document de cadrage initial
- 2026-04-28 : ajout du cadrage produit phase 1 v1 à figer

