Technique/Modules HACCP — fonctionnement interne

Modules HACCP — fonctionnement interne

Comment fonctionnent techniquement les relevés de température, nettoyages, traçabilité, stocks, planifications, capteurs IoT et l'API publique

Cette page décrit le fonctionnement interne des modules métier pour les développeurs. Pour l'usage produit, voir les pages de la section Fonctionnalités.

Modèle de base : site → lieux → relevés

Une organisation possède des sites (établissements), chaque site des lieux (zones typées : froid, chaud, ambiant, nettoyage, stockage). Les lieux de type froid/chaud portent des consignes seuilMin/seuilMax : tout relevé hors bornes est marqué anomalie: true à l'insertion. L'accès aux données d'un site passe par requireSiteAccess (convex/sites/helpers.ts), qui vérifie l'appartenance à l'organisation.

Températures

  • convex/temperatures/ : createReleve calcule l'anomalie selon les seuils du lieu ; listBySite, recentAnomaliesBySite et lastByLieuForSite alimentent la page temperatures/ et le tableau de bord.
  • Deux modules frères sur la même page : mises en température (misesEnTemperature/ — refroidissement, remise, cuisson… avec température de début/fin et anomalie) et contrôles d'huile de friture (controlesHuile/ — conforme ou non, action : changée/filtrée).
  • Les relevés issus des sondes IoT ont userId: "iot:<deviceId>", ce qui permet de les distinguer des saisies manuelles.

Nettoyages

  • Les zones de nettoyage (nettoyageZones/) définissent les checklists d'un lieu (libellé, étapes ordonnées).
  • Un relevé (nettoyages/) enregistre les tâches cochées taches[{libelle, fait}], l'auteur et éventuellement les horodatages debutAt/finAt. createNettoyageBatch permet la saisie groupée.
  • Les nettoyages planifiés (nettoyagesPlanifies/) portent une frequenceJours et une prochaineEcheance que advancerPlanifie fait avancer à chaque réalisation. countDueByOrg/listDueBySite alimentent les rappels dans l'UI.

Traçabilité et stocks

  • Deux types d'enregistrements (tracabilite/) : réception (fournisseur, numéro de BL, lot, DLC, température de réception, conformité livraison, photos R2) et fabrication (ingrédients [{libelle, numLot, quantite}], quantité produite, recettes réutilisables via recettes/).
  • Champs conditionnels selon les modules activés (optionsCheckKit) : allergènes, décongélation, origine viande bovine (naissance/élevage/abattage), expéditions, non-conformités.
  • Le stock est une vue dérivée de la traçabilité : stockBySite liste les produits non sortis ; moveStock, setSortie et consumeStock tracent chaque opération dans stockMouvements (entrée, mouvement, sortie, retour, modification) — l'historique complet d'un produit est reconstituable.
  • Étiquettes et registres PDF : générés côté client avec @react-pdf/renderer (etiquette-pdf.tsx, stock-pdf.tsx, registre-pdf.tsx dans les routes correspondantes).

Alertes DLC

Le cron notify expiring DLC (07:00 UTC, convex/tracabilite/crons.ts) envoie chaque jour un email aux admins listant les produits dont la DLC approche. Le seuil est réglable par organisation (alerteParametres.joursAvant, 1 à 5 jours) et chaque produit n'est alerté qu'une fois (dlcAlerteEnvoyee).

Planification

Trois mécanismes complémentaires, tous rattachés à un site :

TableUsageAvancement
nettoyagesPlanifiesNettoyages récurrents (fréquence en jours)advancerPlanifie à la réalisation
controlesPlanifiesContrôles récurrents (température, mise en température, huile)advancerProgramme
tachesPlanifieesRappels ponctuels avec échéancecompleteTache (statut non_commence → fait)

Capteurs IoT

  • Enrôlement : createCapteur (rôles owner/admin uniquement) génère le token côté serveur (32 octets aléatoires hex) ; le deviceId est unique. Le token et l'URL du webhook s'affichent dans Paramètres → Capteurs.
  • Ingestion : POST /sensors/reading sur le déploiement Convex (*.convex.site), corps JSON :
{ "deviceId": "sonde-chambre-froide-1", "token": "<token>", "temperature": 3.4 }
  • Sécurité : rate-limit par IP (60 requêtes/min, token bucket), comparaison de token en temps constant, température bornée (−100 à 200 °C), réponse d'erreur générique 401 unauthorized (pas d'énumération des devices). En cas de succès la réponse indique si le relevé est en anomalie : { "ok": true, "anomalie": false }.
  • Le relevé est inséré dans temperatureReadings comme une saisie normale (anomalie calculée selon les seuils du lieu associé), et le capteur mémorise lastReadingAt/lastTemperature.

API publique v1

  • Authentification par clé API d'organisation (Better Auth plugin apiKey, préfixe nsk_, expiration 1–365 jours, rate-limit 1000 req/h), passée en header x-api-key ou Authorization: Bearer.
  • Endpoints (convex/http.ts, accessibles aussi via le proxy web /api/v1/*) :
    • GET /api/v1/me — l'organisation liée à la clé
    • GET /api/v1/members — liste des membres
    • GET /api/v1/members/:memberId — détail d'un membre
  • Les clés se créent dans Réglages → Clés API (settings/api-keys/).

Modules optionnels et démo

  • optionsCheckKit active/désactive par organisation les blocs de formulaire avancés (allergènes, décongélation, origine viande, expéditions, non-conformités, documents) — l'UI des wizards de traçabilité s'adapte dynamiquement.
  • demo/ (seedDemoData/removeDemoData) installe un jeu de données de démonstration inventorié dans demoRegistry, ce qui garantit une purge complète sans toucher aux données réelles.
  • export/getFullExportData fournit l'export intégral des données d'une organisation (page Réglages → Export).