← Retour à l'index

Modèle de données du site — v1 (autonome, Laravel-ready)

Projet Site Maison Crosnier · 10 août 2026 · Remplace la v0 (« cache caisse »).

Cible technique : Laravel + Eloquent + MySQL, back-office Filament. Le site est autonome (il fait autorité sur ces données) et caisse-ready : chaque entité mappable porte un champ refCaisse (nullable, indexé), et les accès passent par des repositories (ports) pour brancher un adaptateur caisse plus tard.

Conventions : toute table a id, created_at, updated_at. Montants en decimal(8,2). Pas de gestion de stock (la disponibilité est calculée, cf. §Disponibilité).


1. Deux taxonomies distinctes (rappel important)

2. Catalogue (site-owned)

Categorie

Champ Type Notes
id bigint
parent_id fk Categorie | null arborescence (univers → sous-catégorie)
nom string
slug string unique URL / SEO
ordre int tri d'affichage
actif bool

Famille

Champ Type Notes
id bigint
code string unique pain, viennoiserie, patisserie, snacking
libelle string
politique_type enum(veille,delai) mode de calcul de l'heure limite
heure_limite time | null pour veille (ex. 10:00)
delai_minutes int | null pour delai (ex. 180)
retrait_au_plus_tot enum(J+0,J+1) horizon minimum
retrait_avant time | null fenêtre de retrait spécifique (ex. viennoiserie < 09:00)

Produit

Champ Type Notes
id bigint
refCaisse string | null (indexé) correspondance caisse (vide au lancement)
famille_id fk Famille politique de commande
type enum(simple,formule) formule = offre composée à prix fixe (voir §Formule)
nom string
slug string unique
description text
prix_base decimal prix si pas de déclinaison
actif bool
mis_en_avant bool merchandising
meta_seo json title, description

Relations : belongsToMany Categorie · hasMany Declinaison · hasMany GroupeChoix · hasMany Media.

Declinaison (variante : ex. taille / nombre de parts)

Champ Type Notes
id bigint
produit_id fk Produit
refCaisse string | null
libelle string ex. « 6 parts »
prix decimal prix absolu de la déclinaison
ordre int
actif bool

Si un produit a des déclinaisons, le client en choisit une (prix = declinaison.prix) ; sinon prix = produit.prix_base.

GroupeChoix + Option (options / choix sur un produit)

GroupeChoix : id, produit_id, libelle (ex. « Bougies », « Parfums »), min (int), max (int), obligatoire (bool), ordre. Option : id, groupe_choix_id, refCaisse (null), libelle, prix_delta (decimal, ex. +2,00), ordre, actif.

Formule (offre composée à prix fixe)

Une formule est un Produit de type = formule, à prix fixe (porté par prix_base), composée de plusieurs groupes de composition : pour chaque groupe, le client choisit un nombre défini de produits parmi une liste éligible. Le prix ne dépend pas des produits choisis. Exemple : « Formule à 15 € : 3 produits, un choisi dans chacun de 3 groupes ».

GroupeComposition : id, produit_id (la formule), libelle, quantite (nb à choisir, défaut 1), ordre. GroupeComposition_Produit (pivot) : groupe_composition_id, produit_id (produits éligibles).

Disponibilité d'une formule : elle suit la règle du panier mixte (§4/§7) appliquée aux produits choisis — la famille la plus contraignante fixe le créneau. (Micro-point à préciser : une formule a-t-elle sa propre famille, ou hérite-t-elle de ses composants ? proposé : héritée.)

Media

id, produit_id (null), chemin, alt, type (image…), ordre.

Promotion

id, cible (produit|famille|categorie), cible_id, type (pourcentage|montant), valeur, debut, fin, actif.

Prix affiché = prix (base ou déclinaison) éventuellement minoré par une promotion active.

3. Points de retrait & créneaux

PointDeRetrait : id, nom, adresse, duree_creneau_minutes (défaut 30), actif. HoraireOuverture : id, point_id, jour (enum lundi…dimanche), plages (json : [{debut, fin}]). FermetureExceptionnelle : id, point_id, date, type (ferme|horaires_speciaux), plages (json|null).

Créneau : non stocké — généré à la volée. Le créneau choisi est figé (heure de début T) sur le panier / la commande.

4. Disponibilité (calculée, non stockée)

commandable(famille, T, maintenant) ⇔ maintenant ≤ deadline(famille, T) ; pour un panier, valable si toutes les familles présentes le sont. Aucune table ; service de calcul (voir §9).

5. Panier

Panier : id, client_id (null), session_token, point_id, creneau_debut (datetime|null), statut (actif|converti|abandonne). LignePanier : id, panier_id, produit_id, declinaison_id (null), quantite, prix_unitaire_snapshot (decimal, promo incluse). LignePanierOption (pivot) : ligne_panier_id, option_id, prix_delta_snapshot. LignePanierComposition (formule) : ligne_panier_id, groupe_composition_id, produit_choisi_id.

6. Commande

Commande : id, **refCaisse** (null), client_id, point_id, creneau_debut, statut, montant_total, jalons (creee_le, payee_le, preparee_le, retiree_le, annulee_le). Statuts (pilotés par le back-office de préparation) : creee → payee → en_preparation → preparee → retiree ; ou annulee / non_retiree. LigneCommande : snapshot figé (libellé produit, déclinaison, options, quantité, prix) — pour l'intégrité de l'historique.

7. Paiement & Avoir

Paiement : id, commande_id, type (paiement|remboursement), prestataire (payplug), reference, montant, statut, date. Avoir (crédit boutique, site-owned) : id, **refCaisse** (null), client_id, code (unique, présentable), format_code (code_barres|qr_code|code), montant_initial, solde_restant, origine_commande_id, statut (actif|epuise|expire|edite_boutique), edite_le, montant_edite, expire_le.

8. Clients & back-office

Client : id, **refCaisse** (null), email, nom, telephone, mot_de_passe_hash (null pour invité), type (invite|enregistre). Favori : id, client_id, produit_id. Utilisateur (admin — modèle Filament, distinct du Client) : id, nom, email, mot_de_passe_hash, role (admin|preparateur), actif. Page (CMS — pages éditoriales) : id, titre, slug (unique), contenu (html), meta_seo (json), statut (brouillon|publie), ordre.

9. Services / règles (pas des entités)

CalculDisponibilite · GenerationCreneaux · ApplicationPromo · RattachementInvite (commande invité → compte, même e-mail) · EditionAvoir (édition auto-décrémentante pour usage boutique).

10. Ports / interfaces (caisse-ready)

Interfaces (contrats) : CatalogueRepository, CommandeRepository, AvoirRepository, ClientRepository. Implémentation Eloquent (base du site) aujourd'hui ; adaptateur caisse demain, branché via le conteneur de services Laravel — sans toucher à la logique métier. Le refCaisse porté par les entités permet la réconciliation.

11. Décisions catalogue (confirmées)

  1. Déclinaisons : prix absolu par déclinaison. ✔
  2. Options payantes : oui — gérées par GroupeChoix + Option. ✔
  3. Formules : offre à prix fixe composée de groupes (choix de N produits parmi des listes éligibles) — voir §Formule. ✔
  4. Multi-catégories : un produit peut figurer dans plusieurs catégories d'affichage. ✔

Micro-point restant : famille (politique de commande) d'une formule — propre ou héritée de ses composants (proposé : héritée).