# Produit configurable générique — cadrage produit et technique

## Statut du document
- Statut : étude de cadrage
- Objectif : poser un modèle générique pour des produits configurables à choix multiples et hiérarchiques
- Cas pratique de référence : `3 mini-crêpes`
- Principe : document vivant, à faire évoluer avant implémentation

---

## 1. Contexte métier

Cas concret initial :
- un produit commercial s'appelle `3 mini-crêpes`
- le vendeur doit configurer `3 unités internes`
- chaque mini-crêpe peut avoir un parfum différent :
  - `Nature`
  - `Sucrée`
  - `Nutella`
  - `Confiture` → puis choix d'un parfum de confiture
- le même mécanisme doit rester générique pour :
  - `6 mini-crêpes`
  - `12 mini-crêpes`
  - `18 mini-crêpes`
  - ou tout autre produit "multi-choix répétés"

Conséquence métier :
- ce n'est pas une simple `recette`
- ce n'est pas non plus une simple `variation`
- c'est un **configurateur produit** avec :
  - répétition d'un même slot (`3 mini-crêpes à configurer`)
  - chemins conditionnels (`si confiture, alors choisir le goût`)
  - potentiels surcoûts par choix
  - besoin de traçabilité claire dans la commande

---

## 2. Constats sur l'existant

### 2.1. Ce que fait bien l'existant
Le système actuel gère déjà :
- `Product`
- `Recipe`
- `Ingredient`
- `RecipeIngredient.role` (`BASE`, `DEFAULT`, `SUPPLEMENT`)
- `OrderItemCustomization` pour les ajouts / suppressions d'ingrédients
- une modal POS plate dans `ttm-shop-front/src/modules/pos/components/cashier/RecipeCustomizationModal.vue`

### 2.2. Limites de l'existant pour ce besoin
Le modèle actuel est trop plat pour un cas comme `3 mini-crêpes` :
- une `Recipe` décrit une composition, pas un arbre de décision
- `RecipeIngredient` ne sait pas représenter un choix conditionnel multi-niveaux
- `OrderItemCustomization` est plat, donc ne conserve pas la structure :
  - mini-crêpe 1 = nature
  - mini-crêpe 2 = nutella
  - mini-crêpe 3 = confiture/fraise
- la modal actuelle ne sait gérer que :
  - retirer des ingrédients par défaut
  - ajouter des suppléments
- il n'existe pas de notion de `slot répété` ni de `branche de choix`

### 2.3. Conclusion
Il ne faut **pas** essayer de résoudre ce besoin uniquement avec `Recipe` / `Ingredient` / `OrderItemCustomization`.

Ces briques peuvent rester utiles pour les effets de production / stock, mais **pas comme modèle principal de configuration UX**.

---

## 3. Recommandation d'architecture

## Décision recommandée
Introduire un **système dédié de configurateur produit** distinct de la recette plate.

### A. Produit
Le `Product` reste l'entité commerciale vendue.

### B. Configurateur
Un `ProductConfigurator` décrit les choix à faire pour ce produit.

### C. Snapshot de commande
La commande doit stocker un **snapshot structuré** des choix effectués.

### D. Effets dérivés
Les effets techniques (prix, ingrédients, production, affichage cuisine) peuvent être dérivés du snapshot.

---

## 4. Modèle métier cible

## 4.1. Niveau produit
Je recommande d'ajouter au produit une notion explicite de mode de personnalisation.

Exemple :
- `customizationMode = NONE`
- `customizationMode = RECIPE`
- `customizationMode = CONFIGURATOR`

Pourquoi :
- on ne casse pas les produits actuels basés sur la recette plate
- on peut déployer le nouveau système progressivement
- le POS saura quel moteur ouvrir

---

## 4.2. Configurateur produit

### Entité principale
`ProductConfigurator`
- `id`
- `shopId`
- `name`
- `description`
- `active`

### Lien produit
Un produit peut pointer vers un configurateur :
- `product.configuratorId`

---

## 4.3. Steps / slots répétables

Le configurateur doit définir les blocs de configuration à compléter.

### Entité recommandée
`ProductConfiguratorStep`
- `id`
- `configuratorId`
- `code`
- `label`
- `displayOrder`
- `minSelections`
- `maxSelections`
- `repeatCount`
- `entryGroupId`

### Exemple `3 mini-crêpes`
Un seul step :
- `label = Choix des mini-crêpes`
- `repeatCount = 3`
- `minSelections = 1`
- `maxSelections = 1`

Interprétation :
- on répète 3 fois le même parcours de choix
- pour chaque mini-crêpe, on choisit exactement 1 chemin final

---

## 4.4. Groupes de choix hiérarchiques

### Entité recommandée
`ProductOptionGroup`
- `id`
- `configuratorId`
- `code`
- `label`
- `selectionMode` (`SINGLE`, `MULTIPLE`)
- `minChoices`
- `maxChoices`
- `displayOrder`
- `active`

### Exemple
Groupe racine : `Parfum principal`
- Nature
- Sucrée
- Nutella
- Confiture

Sous-groupe : `Parfum de confiture`
- Fraise
- Abricot
- Myrtille
- etc.

---

## 4.5. Choix

### Entité recommandée
`ProductOptionChoice`
- `id`
- `groupId`
- `code`
- `label`
- `description`
- `priceDelta`
- `displayOrder`
- `isDefault`
- `nextGroupId` nullable
- `active`

### Exemple
Dans `Parfum principal` :
- `Nature` → terminal
- `Sucrée` → terminal
- `Nutella` → terminal
- `Confiture` → `nextGroupId = groupe_parfum_confiture`

Cela couvre exactement le besoin de hiérarchie niveau 1 / niveau 2.

---

## 4.6. Effets techniques optionnels

Le choix métier ne doit pas être confondu avec son effet technique.

Je recommande de garder des effets optionnels par choix final :
- `priceDelta`
- éventuellement `productionLabel`
- éventuellement `ingredientId` ou liste d'ingrédients dérivés
- éventuellement `kitchenLabel`

Cela permet deux niveaux :
1. **source de vérité UX** = l'arbre de configuration
2. **effets techniques** = calculés depuis les choix terminaux

---

## 5. Stockage dans la commande

## 5.1. Problème
`OrderItemCustomization` ne suffit pas pour ce cas, car il perd la notion de slot et la hiérarchie.

## 5.2. Recommandation
Ajouter sur `OrderItem` un snapshot JSON structuré.

Exemple de champ :
- `configurationSnapshot Json?`
- `configurationLabel String?` optionnel pour affichage rapide

### Exemple de snapshot
```json
{
  "configuratorId": "cfg-mini-crepes",
  "configuratorName": "Mini-crêpes",
  "steps": [
    {
      "stepCode": "mini_crepe",
      "entries": [
        {
          "index": 1,
          "path": [
            { "groupCode": "main_flavor", "choiceCode": "nature", "label": "Nature" }
          ],
          "unitPriceDelta": 0
        },
        {
          "index": 2,
          "path": [
            { "groupCode": "main_flavor", "choiceCode": "nutella", "label": "Nutella" }
          ],
          "unitPriceDelta": 0
        },
        {
          "index": 3,
          "path": [
            { "groupCode": "main_flavor", "choiceCode": "jam", "label": "Confiture" },
            { "groupCode": "jam_flavor", "choiceCode": "strawberry", "label": "Fraise" }
          ],
          "unitPriceDelta": 0
        }
      ]
    }
  ]
}
```

---

## 6. Pourquoi ne pas réutiliser uniquement les recettes

### Ce qu'on peut réutiliser
Les `Recipe` et `Ingredient` peuvent encore servir pour :
- la production
- le stock
- des surcoûts techniques
- l'impression cuisine

### Ce qu'il ne faut pas leur demander
Il ne faut pas les utiliser comme seul modèle de configuration pour :
- les slots répétés
- les parcours conditionnels
- la restitution fidèle de la sélection utilisateur

### Recommandation
Approche hybride :
- **configurateur** = modèle UX / commande
- **recette / ingrédients** = effets techniques dérivés, si nécessaire

---

## 7. Recommandation admin

## 7.1. Ne pas surcharger la fiche produit au début
Sur la fiche produit, je recommande seulement :
- type produit
- mode de personnalisation
- recette simple ou configurateur associé

Éviter de construire tout l'arbre directement dans la modal produit.

## 7.2. Préférer un écran admin dédié
Créer une section dédiée du style :
- `Configurateurs produit`

Puis, depuis la fiche produit :
- sélectionner un configurateur existant
- ou créer/éditer un configurateur dédié

## 7.3. UX admin proposée

### Écran 1 — liste des configurateurs
- nom
- statut actif
- nombre de produits liés

### Écran 2 — éditeur de configurateur
- informations générales
- steps
- groupes de choix
- choix
- branchements `nextGroupId`
- aperçu du parcours

### Exemple d'édition pour `3 mini-crêpes`
- Step : `Mini-crêpe` répété `3` fois
- Groupe racine : `Choix du parfum`
- Choix : `Nature`, `Sucrée`, `Nutella`, `Confiture`
- Sous-groupe lié à `Confiture` : `Goût de la confiture`

---

## 8. Impacts POS (plus tard)

Le POS devra ouvrir une modal dédiée différente de `RecipeCustomizationModal.vue`.

Cette modal devra gérer :
- progression par slot (`Mini-crêpe 1 / 2 / 3`)
- hiérarchie de choix
- retour arrière dans un chemin
- résumé final clair

Important :
- il ne faut pas essayer de faire rentrer ce besoin dans la modal recette actuelle
- il faut un composant dédié, piloté par le configurateur

---

## 9. Plan d'implémentation recommandé

## Phase 1 — Modèle et persistence
- ajouter `customizationMode` au `Product`
- ajouter `configuratorId` au `Product`
- créer les tables :
  - `ProductConfigurator`
  - `ProductConfiguratorStep`
  - `ProductOptionGroup`
  - `ProductOptionChoice`
- ajouter `configurationSnapshot` sur `OrderItem`
- exposer DTO + schémas Zod correspondants dans `ttm-shared`

## Phase 2 — Admin
- CRUD configurateurs
- rattachement d'un configurateur à un produit
- validation admin des arbres invalides :
  - cycle interdit
  - `nextGroupId` hors configurateur interdit
  - cardinalités incohérentes interdites

## Phase 3 — POS
- nouvelle modal de configuration arborescente
- ajout au panier avec snapshot structuré
- affichage lisible dans le panier et le détail de commande

## Phase 4 — Effets techniques avancés
- mapping vers ingrédients / production / stock
- affichage cuisine enrichi
- reporting par choix

---

## 10. Recommandation finale

### Recommandation forte
Pour ton cas, la bonne base n'est ni :
- une simple variation produit
- ni une simple recette
- ni un hack dans `OrderItemCustomization`

La bonne base est un **configurateur produit générique à arbre de choix**, relié au produit, avec un snapshot structuré dans la commande.

### Décision pragmatique
Pour éviter de casser l'existant :
- garder `RECIPE` pour les produits actuels
- introduire `CONFIGURATOR` pour les nouveaux cas complexes

### Cas `3 mini-crêpes`
Ce cas devient alors naturel :
- 1 produit commercial
- 1 step répété 3 fois
- 1 groupe racine de choix
- 1 sous-groupe optionnel pour les confitures

C'est la base la plus propre pour ensuite faire l'admin, puis le POS.

