# SPÉCIFICATION — Fonctionnalité « Événements » MyKollectOr®

> Document de référence à charger dans une future conversation de développement.
> Basé sur les structures réelles des bases au 12/06/2026.
> Aucune décision n'est à redébattre : tout ce qui suit est acté.

---

## 1. PRINCIPE FONDATEUR

Un événement est une **couche au-dessus des parcours existants**, pas un nouveau type de
parcours. On ne duplique jamais un parcours. Un événement regroupe, à une date donnée,
un ou plusieurs parcours du catalogue, avec ses propres règles d'inscription, de
classement et de médaille.

L'inscription est **payante via PrestaShop** : un événement = un produit d'inscription
PrestaShop. L'inscription est importée dans MKO par la sync existante (module mkosync +
cron), exactement comme une commande. On ne réinvente aucun circuit de paiement.

Le classement est **séparé par parcours** : chaque parcours de la journée a son propre
classement événementiel, filtré sur l'événement. Pas de classement général combiné.

---

## 2. DÉCISIONS ACTÉES

1. **Inscription** : payante, via PrestaShop (produit d'inscription).
2. **Classement** : un classement séparé par parcours, filtré sur l'événement. Pas de cumul.
3. **Rattachement d'un temps à l'événement** : double mécanisme combiné.
   - Prioritaire : la **borne** tague le temps avec l'evenement_id (canal officiel).
   - Filet : **tout temps réalisé le jour J** sur un parcours de l'événement, par un
     sportif inscrit, est rattaché automatiquement (couvre Strava / app / manuel).
4. **Médaille** : une **médaille spéciale gravée au nom de l'événement** (+ date + parcours
   + temps), distincte de la médaille standard du parcours.

---

## 3. MODÈLE DE DONNÉES

### 3.1 Nouvelles tables (base mykollufab)

```sql
-- Un événement = un regroupement daté de parcours, lié à un produit d'inscription PS
CREATE TABLE `evenements` (
  `id` int NOT NULL AUTO_INCREMENT,
  `nom` varchar(255) NOT NULL,
  `slug` varchar(150) NOT NULL,
  `date_evenement` date NOT NULL,
  `lieu` varchar(255) DEFAULT NULL,
  `description` text DEFAULT NULL,
  `image` varchar(255) DEFAULT NULL,
  `statut` enum('brouillon','publie','en_cours','termine','annule') NOT NULL DEFAULT 'brouillon',
  -- Produit PrestaShop d'INSCRIPTION (l'achat qui inscrit le sportif)
  `presta_inscription_product_id` int DEFAULT NULL,
  -- Produit MKO de la MÉDAILLE événement (gravure spéciale)
  `medaille_produit_id` int DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `slug` (`slug`),
  KEY `idx_date` (`date_evenement`),
  KEY `idx_statut` (`statut`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Parcours composant un événement (1 à N parcours sur la journée)
CREATE TABLE `evenement_parcours` (
  `id` int NOT NULL AUTO_INCREMENT,
  `evenement_id` int NOT NULL,
  `produit_id` int NOT NULL,              -- référence produits.id (parcours catalogue)
  `heure_depart` time DEFAULT NULL,
  `ordre` int NOT NULL DEFAULT 0,         -- ordre d'affichage dans la journée
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_event_parcours` (`evenement_id`,`produit_id`),
  KEY `idx_evenement` (`evenement_id`),
  KEY `idx_produit` (`produit_id`),
  CONSTRAINT `fk_ep_evenement` FOREIGN KEY (`evenement_id`) REFERENCES `evenements` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Sportifs inscrits à un événement (alimentée par l'import PrestaShop)
CREATE TABLE `evenement_inscriptions` (
  `id` int NOT NULL AUTO_INCREMENT,
  `evenement_id` int NOT NULL,
  `user_id` int NOT NULL,
  `presta_order_id` int DEFAULT NULL,     -- la commande PS d'inscription
  `presta_order_ref` varchar(50) DEFAULT NULL,
  `dossard` varchar(20) DEFAULT NULL,     -- optionnel, attribué le jour J
  `statut` enum('inscrit','present','absent','annule') NOT NULL DEFAULT 'inscrit',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_event_user` (`evenement_id`,`user_id`),
  KEY `idx_user` (`user_id`),
  KEY `idx_presta_order` (`presta_order_id`),
  CONSTRAINT `fk_ei_evenement` FOREIGN KEY (`evenement_id`) REFERENCES `evenements` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

### 3.2 Champs ajoutés à l'existant

```sql
-- Un chrono peut être rattaché à un événement (NULL = temps hors événement)
ALTER TABLE `chronos`
  ADD COLUMN `evenement_id` int DEFAULT NULL AFTER `produit_id`,
  ADD KEY `idx_evenement` (`evenement_id`);

-- Une commande de médaille peut découler d'un événement (pour la gravure spéciale)
ALTER TABLE `commandes`
  ADD COLUMN `evenement_id` int DEFAULT NULL AFTER `produit_id`,
  ADD KEY `idx_evenement` (`evenement_id`);
```

Aucune autre table existante n'est modifiée. Parcours (`produits`), sync, gravure,
classements et sources de temps restent inchangés.

---

## 4. RÈGLE DE RATTACHEMENT D'UN TEMPS À UN ÉVÉNEMENT

À l'enregistrement de tout chrono (quelle que soit la source), appliquer dans l'ordre :

1. **Si la borne fournit un evenement_id** (la borne est configurée pour l'événement ce
   jour-là) → utiliser cet evenement_id. Canal officiel, prioritaire.

2. **Sinon, rattachement automatique par date** si TOUTES ces conditions sont vraies :
   - le sportif a une ligne dans `evenement_inscriptions` avec statut `inscrit`/`present`,
   - pour un événement dont `date_evenement` = date du chrono (`date_debut`),
   - et le `produit_id` du chrono ∈ `evenement_parcours` de cet événement.
   → rattacher le chrono à cet evenement_id.

3. **Sinon** → `evenement_id` reste NULL (temps d'entraînement normal).

Afficher au sportif une confirmation : « Ce temps compte pour l'événement {nom} ✓ »
quand le rattachement (1) ou (2) a lieu, pour la transparence.

Cas limite à gérer : un même parcours couru deux fois le jour J (échauffement + course).
Décision recommandée : garder le **meilleur temps** par (sportif, parcours, événement)
pour le classement événementiel ; les autres restent visibles dans l'historique mais hors
classement du jour.

---

## 5. CLASSEMENTS

Réutiliser le système de classement par parcours existant, avec un filtre supplémentaire.

- **Classement permanent d'un parcours** (déjà en place) : tous les temps valides du
  parcours, toutes dates → inchangé.
- **Classement événementiel** (nouveau) : mêmes requêtes, filtrées sur
  `chronos.evenement_id = :id` ET `chronos.produit_id = :parcours`. Un classement par
  parcours de l'événement.

Règles d'éligibilité héritées de l'existant : seuls les temps non modifiés issus de
borne / Strava / app comptent. Un temps manuel ou modifié reste hors classement, événement
ou pas. (La borne est idéale ici : temps officiel du jour, non modifiable.)

---

## 6. INTÉGRATION PRESTASHOP

### 6.1 Côté PrestaShop
- Créer un produit « Inscription – {Nom événement} – {Date} » dans une **catégorie dédiée
  « Événements »** (c'est le marqueur qui permet à MKO de distinguer une inscription d'une
  commande de médaille).
- Optionnellement, le produit médaille événement est un second produit PS, ou bien la
  médaille est commandée plus tard depuis le dashboard (voir 7.3).

### 6.2 Côté import MKO (extension de la sync existante)
À l'import d'une commande, après récupération du produit :
- Si le produit appartient à la catégorie « Événements » → créer/mettre à jour une ligne
  `evenement_inscriptions` (matcher l'événement via `presta_inscription_product_id`),
  au lieu de créer une commande de médaille classique.
- Sinon → comportement actuel inchangé (commande de médaille).

La déduplication reste par `presta_order_id`, comme aujourd'hui. Idempotent.

---

## 7. INTERFACES À CONSTRUIRE

### 7.1 Admin événements (workflow / fab)
- CRUD événements : nom, date, lieu, description, image, statut.
- Association des parcours de la journée (`evenement_parcours`) : choisir N parcours du
  catalogue, heure de départ, ordre.
- Lien vers le produit PrestaShop d'inscription (`presta_inscription_product_id`).
- Le jour J : liste des inscrits, marquage présent/absent, attribution dossard,
  configuration de la borne pour l'événement.

### 7.2 Dashboard sportif — section « Événements » (nouvel onglet)
- **À venir** : événements publiés, avec bouton « S'inscrire » → fiche PrestaShop.
- **Mes événements** : événements où le sportif est inscrit, avec ses parcours du jour,
  heures de départ, et accès aux classements du jour.
- Intégration dans la nav existante (desktop sidebar + barre mobile / menu « Plus »).

### 7.3 Commande de la médaille événement
- Après l'événement, depuis « Mes événements », le sportif commande sa médaille
  événement pour un parcours où il a un temps rattaché.
- La commande porte l'`evenement_id`. Le template de gravure choisit la mise en page
  « événement » (nom événement + date + parcours + temps) au lieu de la mise en page
  parcours standard.

---

## 8. GRAVURE

Le fichier de gravure transmis au fabricant gagne un champ optionnel : le contexte
événement (nom + date). Le template de gravure a deux variantes :
- **Médaille parcours** (actuelle) : nom parcours + temps.
- **Médaille événement** (nouvelle) : nom événement + date + nom parcours + temps.

Le choix de variante se fait sur la présence de `commandes.evenement_id`.

---

## 9. SÉQUENCE DE DÉVELOPPEMENT RECOMMANDÉE

Ne pas tout faire d'un coup. Ordre conseillé :

1. **Tables + champs** (section 3) : migration SQL, sans logique encore.
2. **Rattachement des temps** (section 4) : la règle borne + date, testée avec des temps
   manuels et un événement fictif. C'est le cœur.
3. **Classement événementiel** (section 5) : filtre sur l'existant.
4. **Section dashboard sportif** (7.2) : affichage, inscription, classements du jour.
5. **Extension de la sync PS** (6.2) : import des inscriptions.
6. **Admin événements** (7.1) : création et gestion.
7. **Médaille événement + gravure** (7.3, 8) : template spécial.

---

## 10. FICHIERS PROBABLEMENT IMPACTÉS

- `mykollufab` : 3 nouvelles tables + 2 ALTER (chronos, commandes).
- API : nouveau `EvenementController`, extension de `PrestashopService` (import inscriptions),
  extension du service de chrono (rattachement), extension du service de classement (filtre).
- `chrono.mykollector.com/dashboard.html` : nouvel onglet Événements + commande médaille.
- Admin (workflow ou fab) : interface de gestion des événements.
- Template de gravure : variante événement.
- PrestaShop : catégorie « Événements », produits d'inscription.

---

## 11. POINTS OUVERTS À TRANCHER AVANT DEV

- Médaille événement : commandée pendant l'inscription (bundle) ou séparément après
  l'événement ? (Recommandation : séparément, depuis « Mes événements », pour ne facturer
  la médaille qu'à ceux qui ont un temps.)
- Cas du même parcours couru plusieurs fois le jour J : confirmer la règle « meilleur
  temps retenu » (section 4).
- Dossards : utiles dès le départ ou plus tard ? (champ déjà prévu, optionnel.)
- Gestion des absents/remboursements : ta règle actuelle est « pas de remboursement » ;
  confirmer qu'elle s'applique aussi aux inscriptions événement.
