Aller au contenu

Modèle de données

Schéma d'origine (Etude.xlsx)

Premier jet, issu du fichier d'étude initial. Sert de base mais a été jugé incomplet pour le cas d'usage horeca (pas de suivi d'exécution, pas de notion de personnel/appareil) — voir schéma révisé ci-dessous.

AlarmesTypes Alarmes Scheduler ScheduleType Actions
id id id id id
name idtype idtype period name
description idschedule idscheduletype (énum period ci-dessous) procedure
yesno yesno massage (sic, = message)
text text mail
picture picture alarme
procedure date
idaction time
annuler

Valeurs de l'énumération period (ScheduleType) : journalier, hebdomadaire, mensuel, annuel, journalier répétitif, autre, oneshot.

Ambiguïté non résolue

Scheduler.idtype fait doublon avec AlarmesTypes.id dans le fichier source — pas encore clarifié.

Multi-tenant

Décision prise le 2026-08-30 (voir architecture pour le raisonnement coût) : un seul projet Supabase partagé sert plusieurs établissements clients, au lieu d'un projet dédié par client. Concrètement :

  • Nouvelle table etablissements (le "tenant") : id, nom, actif.
  • Une colonne id_etablissement (non nulle, references etablissements(id)) ajoutée sur chaque table métier : postes, personnel, devices, pairing_tokens, alarmes_types, schedules, alarmes, actions. Dénormalisée (redondante avec ce qui serait déductible via id_poste) volontairement — ça rend les futures policies de sécurité (RLS) simples et rapides à écrire, sans jointure.
  • Toutes les données de test existantes sont rattachées à un établissement pilote fixe, id 00000000-0000-0000-0000-000000000001, via une valeur DEFAULT sur la colonne — la migration ne casse rien de ce qui existait déjà.

Isolation pas encore garantie par la base

La colonne existe mais Row Level Security n'est pas activée — voir l'avertissement détaillé dans architecture.

Schéma révisé (validé, à traduire en DDL Postgres)

Ajouts par rapport au schéma d'origine : gestion du personnel, des appareils appairés par QR code, du ciblage des tâches, et surtout d'un statut de suivi d'exécution (absent du schéma d'origine, qui n'avait qu'un flag d'annulation).

  • personnel — membres du staff (nom, poste, actif)
  • postes — zones/stations génériques (cuisine, bar, salle, sanitaires…) pour cibler une tâche sans forcément nommer une personne
  • devices — tablette/téléphone appairé via QR code : token, lié à une personne ou un poste, horodatage du dernier appairage et du dernier vu (heartbeat en ligne/hors ligne) — voir encadré ci-dessous
  • alarmes_types — catalogue réutilisable des types de tâche (ex. Réassort, Nettoyage sanitaire)
  • schedules — règles de récurrence (journalier, hebdomadaire, mensuel, annuel, journalier répétitif, oneshot, autre), détachées du type pour pouvoir planifier un même type différemment selon les jours. L'heure/fréquence précise est stockée en expression cron dans cron_expr — voir encadré ci-dessous.
  • alarmes — tâches concrètes générées par un planning ou créées à la main par le responsable (id_schedule nullable pour couvrir l'envoi de listes ad hoc). Statut : en_attente / vue / faite / annulée / expirée, avec qui l'a traitée et quand.
  • actions — simplifié à message_ecran et procedure pour l'instant (mail et push retirés du scope, voir architecture)
  • pairing_tokens — jeton QR éphémère (5 min) utilisé pour appairer un téléphone à un poste (ajouté à l'implémentation, voir développement)

À quoi sert devices ?

C'est la mémoire de "qui/quel poste est branché sur quel appareil physique", remplie via le flux QR code — sans jamais demander de mot de passe.

  • Le token = l'identité de l'appareil. Quand un téléphone scanne le QR du kiosque, il reçoit un token généré côté client, stocké dans son localStorage. À chaque réouverture de l'app, le token est relu et cherché dans devices pour savoir "je suis le téléphone du poste Bar" (voir développement).
  • Cible du device : un poste OU une personne, jamais les deux (id_poste XOR id_personnel, contrainte CHECK). Une tablette de poste (kiosque fixe cuisine/bar/...) a id_poste rempli ; un téléphone nominatif aurait id_personnel rempli (pas encore utilisé dans le flux actuel, qui reste au niveau poste — voir points ouverts).
  • dernier_vu : horodatage mis à jour à l'ouverture — un futur "heartbeat" en ligne/hors ligne, pas encore exploité visuellement.
  • actif : permet de désactiver un appairage (ex. bouton "DÉCONNECTER" du handoff design, pas encore construit) sans supprimer l'historique.
  • Usage concret dans le code : c'est cette table qui alimente la liste de noms dans l'overlay "Qui a fait cette tâche ?" du kiosque (Kiosque.jsx interroge personnel joint à devicesactif=true pour le poste courant) — seules les personnes réellement appairées à un téléphone apparaissent.

C'est quoi une expression cron ?

La notation standard (utilisée depuis des décennies dans plein d'outils) pour décrire une récurrence, en 5 champs séparés par des espaces : minute heure jour-du-mois mois jour-de-semaine. * veut dire "n'importe quelle valeur", */N veut dire "tous les N". Exemples issus des données de test :

cron_expr Signification
0 10 * * * tous les jours à 10h00
0 8 * * * tous les jours à 8h00
0 */2 * * * toutes les 2 heures, pile à l'heure
0 */4 * * * toutes les 4 heures

Ce champ n'est pas encore exploité : rien ne lit cron_expr pour générer automatiquement une alarme à l'échéance — les alarmes de test ont été créées à la main. C'est le rôle du scheduler, dernière étape de la feuille de route (service qui regarde périodiquement les plannings et crée les tâches dues).

Schéma entité-relation

erDiagram
    ETABLISSEMENTS ||--o{ POSTES : "tenant"
    ETABLISSEMENTS ||--o{ PERSONNEL : "tenant"
    ETABLISSEMENTS ||--o{ DEVICES : "tenant"
    ETABLISSEMENTS ||--o{ ACTIONS : "tenant"
    ETABLISSEMENTS ||--o{ ALARMES_TYPES : "tenant"
    ETABLISSEMENTS ||--o{ SCHEDULES : "tenant"
    ETABLISSEMENTS ||--o{ ALARMES : "tenant"
    POSTES ||--o{ PERSONNEL : "poste habituel"
    POSTES ||--o{ DEVICES : "appairé à"
    PERSONNEL ||--o{ DEVICES : "appairé à"
    ACTIONS ||--o{ ALARMES_TYPES : "action par défaut"
    ALARMES_TYPES ||--o{ SCHEDULES : "planifié par"
    ALARMES_TYPES ||--o{ ALARMES : "génère"
    SCHEDULES ||--o{ ALARMES : "déclenche"
    POSTES ||--o{ ALARMES : "cible"
    PERSONNEL ||--o{ ALARMES : "assignée à"
    PERSONNEL ||--o{ ALARMES : "traitée par"

    ETABLISSEMENTS {
        uuid id PK
        text nom
        bool actif
    }
    POSTES {
        uuid id PK
        uuid id_etablissement FK
        text nom
    }
    PERSONNEL {
        uuid id PK
        uuid id_etablissement FK
        text nom
        uuid id_poste FK
        bool actif
    }
    DEVICES {
        uuid id PK
        uuid id_etablissement FK
        text token
        text type
        uuid id_personnel FK
        uuid id_poste FK
        timestamp date_appairage
        timestamp dernier_vu
        bool actif
    }
    ACTIONS {
        uuid id PK
        uuid id_etablissement FK
        text nom
        text type
    }
    ALARMES_TYPES {
        uuid id PK
        uuid id_etablissement FK
        text nom
        text description
        uuid id_poste_defaut FK
        uuid id_action FK
        bool actif
    }
    SCHEDULES {
        uuid id PK
        uuid id_etablissement FK
        uuid id_alarme_type FK
        text type_recurrence
        text cron_expr
        bool actif
    }
    ALARMES {
        uuid id PK
        uuid id_etablissement FK
        uuid id_alarme_type FK
        uuid id_schedule FK
        uuid id_poste FK
        uuid id_personnel_assigne FK
        text titre
        timestamp date_echeance
        text statut
        uuid id_personnel_traitant FK
        timestamp date_traitement
        timestamp created_at
    }

devices.id_personnel et devices.id_poste sont mutuellement exclusifs (un appareil est appairé à une personne ou à un poste, pas les deux) — de même pour alarmes.id_poste et alarmes.id_personnel_assigne, tant que le point ouvert sur le ciblage n'est pas tranché.

Points encore ouverts : voir besoin fonctionnel (ciblage poste vs personne, définition concrète de procedure).

Implémentation

Le DDL Postgres est dans db/schema.sql (appliqué sur le projet Supabase du projet), avec les exclusions mutuelles ci-dessus en CHECK constraints. Des données de test représentatives (postes, personnel, types d'alarme, plannings, alarmes dans tous les statuts, devices appairés) sont dans db/seed.sql, complété par db/seed_extra.sql (plus de volume par poste, dont un poste rempli à la capacité de la grille kiosque). Les deux fichiers s'appliquent l'un après l'autre, une seule fois chacun (pas de ON CONFLICT — pas idempotents).