Équipements et personnes
Le site, son personnel, ses zones et ses machines — la carte fixe à laquelle se rattache chaque relevé de température.
Voici les choses qui ne bougent pas : le restaurant lui-même, les personnes qui y travaillent, les zones dans lesquelles il est découpé, et les machines qui maintiennent les denrées à une température. Rien de tout cela n'est un contrôle.
Cela décide pourtant de ce que veulent dire les contrôles. Une température est un nombre sans verdict attaché tant que vous n'avez pas lu les seuils sur l'équipement où elle a été prise, et un relevé n'appartient à une partie du bâtiment que parce que l'équipement dit dans quelle zone il se trouve. Ratez cette page et tous les chiffres en aval sont faux en silence.
| Collection | Portée | Ce qu'est un enregistrement |
|---|---|---|
restaurants | restaurants:read | Le site lui-même : nom, adresse, jours de fermeture, paramètres. |
users | users:read | Un compte du personnel qui enregistre des contrôles. |
areas | areas:read | Une zone dans laquelle le site est découpé — cuisine, chambre froide, bar. |
equipment | equipment:read | Un réfrigérateur, un congélateur, un équipement de la chaîne du froid, avec ses seuils. |
sensors | sensors:read | Une sonde sans fil, par adresse MAC. |
cooking-equipment | cooking-equipment:read | Un four ou un autre équipement de cuisson, avec ses seuils. |
Toutes les six partagent l'enveloppe d'instantané : id,
deleted, capturedAt, receivedAt, sequence et le reste entourent le
data décrit ci-dessous.
restaurants
Le site auquel votre clé est limitée, sous forme d'enregistrement. Un par restaurant : cette collection tient donc en général sur une seule page.
| Champ | Type | Signification |
|---|---|---|
name | string, obligatoire | Le nom du site, tel qu'il est affiché dans l'application. |
address | string, obligatoire | L'adresse postale, en une seule chaîne de caractères. |
closingDays | string[], obligatoire | Les jours où le site est fermé. Des chaînes simples, que le schéma ne contraint pas. |
exceptionalClosures | object[] | Les fermetures ponctuelles, chacune un objet avec from et to. |
detailedAddress | object | null | L'adresse découpée en parties, quand l'application la détient ainsi. |
deliveryAddress | string | null | Où les marchandises sont livrées, quand cela diffère d'address. |
deliveryDetailedAddress | object | null | La même, en parties. |
preferredLanguage | string | null | La langue dans laquelle le restaurant travaille. |
multiArea | boolean | null | Si le site est découpé en plus d'une zone. |
settings | object | null | Les paramètres de l'application. La forme appartient à l'application et elle bouge. |
{
"id": "e5a1d3c9-7b40-4a28-9df6-18c0b2e46a75",
"collection": "restaurants",
"deleted": false,
"capturedAt": "1789300481002",
"receivedAt": "1789300489517",
"sequence": "7",
"data": {
"name": "Le Comptoir de Nanterre",
"address": "12 rue des Anciennes Mairies, 92000 Nanterre",
"closingDays": ["sunday"],
"preferredLanguage": "fr",
"multiArea": true
}
}
L'exemple ne montre que la moitié opérationnelle. À côté, l'enregistrement
porte la moitié commerciale : subscription, subscriptions, companyName,
billingAddress, billingEmail, tvaNumber, trialEndDate, customerId,
discount. Ils existent, ils sont dans le document OpenAPI, et cette page ne
vous les expliquera pas.
Lisez les champs opérationnels et laissez les champs commerciaux tranquilles. Ils décrivent le contrat du restaurant avec nous, pas sa cuisine. Ce n'est pas une API de facturation, ils changent pour des raisons qui n'ont rien à voir avec la sécurité alimentaire, et une intégration qui renvoie à un client l'état de son abonnement est un ticket de support en devenir.
closingDays est une liste de chaînes simples. Le schéma ne leur impose
aucune forme : lisez-les et affichez-les ; n'aiguillez pas dessus et ne
supposez ni orthographe ni casse particulière.
users
Les comptes du personnel qui enregistrent les contrôles. C'est vers eux que
pointe le userId d'un enregistrement de température.
| Champ | Type | Signification |
|---|---|---|
firstName | string, obligatoire | Le prénom, tel qu'il a été saisi. |
lastName | string, obligatoire | Le nom de famille, tel qu'il a été saisi. |
email | string, obligatoire | L'adresse e-mail du compte. |
phoneNumber | string | null | Un numéro de contact, quand il a été donné. |
modules | string[] | Les parties de l'application que cette personne utilise. Des chaînes libres. |
preferredNotificationTime | string | null | Quand cette personne a demandé à être rappelée. |
isChildren | boolean | null | Un indicateur interne. Le schéma ne lui donne aucun sens ; ne construisez rien dessus. |
isDeleted | boolean | null | L'indicateur de retrait propre à l'application — et il peut être null ici, contrairement à ailleurs. |
{
"id": "2774953d-8d9b-4a68-8ec4-1209edd90777",
"collection": "users",
"deleted": false,
"capturedAt": "1789315002664",
"receivedAt": "1789315010218",
"sequence": "31",
"data": {
"firstName": "Amina",
"lastName": "Berthier",
"email": "amina.berthier@example.test",
"phoneNumber": null,
"modules": ["temperatures", "cleaning"],
"isDeleted": false
}
}
Ce sont des données personnelles. email, phoneNumber et les deux champs
de nom identifient une personne réelle, et ils sont dans cette API pour une
seule raison : un enregistrement de sécurité alimentaire doit dire qui a
effectué le contrôle. C'est leur unique rôle ici.
Stockez donc l'id et résolvez un nom d'affichage quand vous en avez besoin.
Ne recopiez pas les coordonnées dans vos propres tables, vos journaux, vos
événements d'analyse ou un outil tiers sous prétexte qu'elles se trouvaient
dans la charge utile. Le restaurant est le responsable de traitement pour son
personnel, et chaque copie que vous faites est une copie dont il doit désormais
rendre compte.
isDeleted peut être null sur cette collection alors qu'il est obligatoire
ailleurs : traitez un indicateur absent comme « non retiré » plutôt que de le
laisser passer pour un faux par accident. La différence entre lui et le
deleted de l'enveloppe est
expliquée sur la page du catalogue.
areas
Une zone du restaurant. Deux champs, et bien plus importante que deux champs ne le laissent croire.
| Champ | Type | Signification |
|---|---|---|
name | string, obligatoire | La zone telle que le restaurant la nomme — cuisine, chambre froide, bar. |
isDeleted | boolean, obligatoire | L'indicateur de retrait de l'application. Voyez la page du catalogue. |
{
"id": "9c2b17ae-4d80-4f55-b3e1-0a6c8f4d2b19",
"collection": "areas",
"deleted": false,
"capturedAt": "1789301120440",
"receivedAt": "1789301126871",
"sequence": "12",
"data": {
"name": "Chambre froide",
"isDeleted": false
}
}
areaId est ce qui permet de tout grouper par zone. Les équipements, les
équipements de cuisson, les friteuses et les tâches de nettoyage en portent
tous un, et c'est le seul lien entre un relevé et une
partie du bâtiment. Il n'y a pas d'areaId sur les enregistrements eux-mêmes :
vous y arrivez par l'équipement.
areaId peut être null partout où il apparaît. Un réfrigérateur sans zone
est normal — cela veut dire que personne ne la lui a attribuée, en général sur
un site d'une seule pièce où multiArea est faux. Groupez ceux-là sous
« non attribué » plutôt que de les jeter.
equipment
Réfrigérateurs, congélateurs et autres équipements de la chaîne du froid. L'enregistrement qui décide si une température était acceptable.
| Champ | Type | Signification |
|---|---|---|
index | integer, obligatoire | La position que le restaurant lui a donnée dans sa propre liste. Un ordre d'affichage, pas un identifiant. |
type | string, obligatoire | Le genre d'équipement dont il s'agit. Une chaîne non contrainte — lisez-la, n'aiguillez pas dessus. |
name | string, obligatoire | Le nom que la cuisine lui donne. |
min | number | null | La température la plus basse acceptable. |
max | number | null | La température la plus haute acceptable. |
areaId | string | null | La zone où il se trouve. |
equipmentSensorId | string | null | La sonde qui y est installée, quand il y en a une. |
state | string | null | L'état actuel de l'équipement, sous forme de chaîne non contrainte. |
outOfRangeCount | number | null | Un décompte des relevés hors plage que l'application tient à jour. |
isDeleted | boolean, obligatoire | L'indicateur de retrait de l'application. |
{
"id": "84ce5c13-3237-40d4-901a-cbba59a6406f",
"collection": "equipment",
"deleted": false,
"capturedAt": "1789302455901",
"receivedAt": "1789302461330",
"sequence": "58",
"data": {
"index": 2,
"type": "freezer",
"name": "Congélateur bas",
"min": -22,
"max": -18,
"areaId": "9c2b17ae-4d80-4f55-b3e1-0a6c8f4d2b19",
"equipmentSensorId": "b31f7d64-0e29-42ca-9a57-51d3c8e07f28",
"state": "ok",
"outOfRangeCount": 0,
"isDeleted": false
}
}
min et max sont le seul verdict que vous obtiendrez. Un
enregistrement de température porte une valeur, une heure
et l'équipement sur lequel elle a été prise — rien qui dise si c'était bon.
-14,4 °C est une crise dans un réfrigérateur et un mardi ordinaire dans un
congélateur, et c'est cet enregistrement qui fait la différence entre les deux.
Tous les chiffres de conformité que vous construisez passent par cette
jointure.
Ils peuvent aussi être null. Quand min ou max est absent, vous n'avez pas
de seuil, et la réponse honnête pour ce relevé est « inconnu », pas « dans la
plage ». Dites-le dans votre interface plutôt que de basculer par défaut sur le
vert.
Les seuils sont ceux d'aujourd'hui, pas ceux qui s'appliquaient.
L'instantané vous donne les min et max du jour ; un relevé de mars a été
jugé sur ce qui était réglé en mars, et le restaurant peut les changer à tout
moment. Si vous réévaluez d'anciens relevés avec les chiffres d'aujourd'hui —
ce qui est tout ce que cette API permet — présentez le résultat comme votre
recalcul, pas comme ce que la cuisine a vu.
type et state sont des chaînes non contraintes. Ce sont dans le schéma
deux simples champs de texte libre, sans aucune liste derrière. Affichez-les,
groupez dessus s'il le faut, et ne laissez jamais une valeur non reconnue
traverser un aiguillage pour tomber dans le silence.
outOfRangeCount est le chiffre de l'application, pas le vôtre. Le schéma
ne dit pas quelle fenêtre il couvre, et vous ne pouvez pas le reconstruire à
partir des instantanés disponibles ici. Traitez-le comme un signal venu de
l'application, et si vous avez besoin d'un décompte défendable, calculez le
vôtre à partir des relevés et des seuils, et présentez-le comme le vôtre.
equipmentSensorId pointe de l'équipement vers sa sonde. L'enregistrement de
la sonde pointe dans l'autre sens. Les deux côtés peuvent être null, et rien
ne garantit que l'un soit renseigné parce que l'autre l'est.
sensors
Les sondes sans fil. Deux champs, tous les deux facultatifs.
| Champ | Type | Signification |
|---|---|---|
macAddress | string | null | L'adresse matérielle de la sonde — c'est ainsi que vous distinguez un appareil physique d'un autre. |
sensorEquipmentId | string | null | L'équipement que cette sonde surveille. |
{
"id": "b31f7d64-0e29-42ca-9a57-51d3c8e07f28",
"collection": "sensors",
"deleted": false,
"capturedAt": "1789302501764",
"receivedAt": "1789302509002",
"sequence": "61",
"data": {
"macAddress": "F4:12:9D:3A:77:0B",
"sensorEquipmentId": "84ce5c13-3237-40d4-901a-cbba59a6406f"
}
}
Notez les deux noms de champ, qui sont l'image l'un de l'autre et faciles à
intervertir : l'équipement porte equipmentSensorId, la sonde porte
sensorEquipmentId. Lisez le mauvais et vous obtenez undefined, sans erreur.
Il n'y a pas de nom ici, pas de niveau de batterie et pas de date de dernière
vue. Ce qu'une sonde a remonté est sur
temperature-readings, et ces relevés sont indexés sur
equipmentId plutôt que sur la sonde — si bien qu'une sonde devenue muette
ressemble exactement à une sonde qui n'avait rien à remonter.
cooking-equipment
Fours, armoires de maintien et le reste du côté chaud. Même idée
qu'equipment, la mécanique de la chaîne du froid en moins.
| Champ | Type | Signification |
|---|---|---|
index | integer, obligatoire | L'ordre d'affichage dans la liste du restaurant. |
type | string, obligatoire | Le genre d'équipement dont il s'agit. Chaîne non contrainte. |
name | string, obligatoire | Le nom que la cuisine lui donne. |
min | number | null | La température la plus basse acceptable. |
max | number | null | La température la plus haute acceptable. |
areaId | string | null | La zone où il se trouve. |
isDeleted | boolean, obligatoire | L'indicateur de retrait de l'application. |
{
"id": "3d90f4c8-62b7-4e13-8a05-ff71d6b9c204",
"collection": "cooking-equipment",
"deleted": false,
"capturedAt": "1789303880115",
"receivedAt": "1789303887640",
"sequence": "64",
"data": {
"index": 1,
"type": "oven",
"name": "Four à sole",
"min": 63,
"max": 260,
"areaId": "9c2b17ae-4d80-4f55-b3e1-0a6c8f4d2b19",
"isDeleted": false
}
}
Pas de state, pas d'outOfRangeCount, pas de sonde : les équipements de
cuisson sont lus par une personne, rien ne les surveille donc entre deux
contrôles. Leurs relevés sont les
cooking-temperature-records, qui pointent ici avec
cookingEquipmentId — un nom de champ différent de celui de la chaîne du
froid, et l'endroit habituel où un client ne lit rien en silence.
Lire la carte
Tout ce qui est sur cette page est petit, lent à bouger et nécessaire avant qu'aucune autre collection n'ait de sens.
- Chargez-la en premier et gardez-la. Parcourez
areas,equipment,cooking-equipmentetsensorsavant de parcourir la moindre collection d'enregistrements, et résolvez les noms et les seuils depuis votre propre copie plutôt qu'enregistrement par enregistrement. - Actualisez-la quand même. Les équipements sont renommés, les seuils sont corrigés, un réfrigérateur passe dans une autre zone. Une copie prise une fois et jamais reparcourue dérive sans jamais tomber en panne.
- Gardez les identifiants, pas les copies — surtout pour les personnes.
Stockez le
userIdet résolvez un nom au moment de l'affichage. Voyez la note sur les données personnelles ci-dessus. - Les deux indicateurs de suppression, partout. Le
deletedde l'enveloppe et l'isDeleteddedatasont deux choses différentes, et la page du catalogue explique lequel est lequel.