Strutture e persone

La sede, il suo personale, le sue zone e le sue macchine — la mappa fissa a cui si aggancia ogni lettura di temperatura.

Queste sono le cose che non si muovono: il ristorante stesso, le persone che ci lavorano, le zone in cui è suddiviso, e le macchine che tengono il cibo a una temperatura. Niente di tutto ciò è un controllo.

Decide comunque cosa significano i controlli. Una temperatura è un numero senza alcun verdetto allegato finché non legge le soglie sull'apparecchiatura su cui è stata rilevata, e una lettura appartiene a una parte dell'edificio solo perché l'apparecchiatura dice in quale area si trova. Sbagli questa pagina e ogni dato a valle è sbagliato in silenzio.

CollezioneAmbitoChe cos'è un record
restaurantsrestaurants:readLa sede stessa: nome, indirizzo, giorni di chiusura, impostazioni.
usersusers:readUn account del personale che registra i controlli.
areasareas:readUna zona in cui la sede è suddivisa — cucina, cella frigorifera, bar.
equipmentequipment:readUn frigorifero, un congelatore, un'unità della catena del freddo, con le sue soglie.
sensorssensors:readUna sonda wireless, per indirizzo MAC.
cooking-equipmentcooking-equipment:readUn forno o un'altra unità di cottura, con le sue soglie.

Tutte e sei condividono l'involucro di istantanea: id, deleted, capturedAt, receivedAt, sequence e gli altri stanno accanto al data descritto qui sotto.

restaurants

La sede a cui è circoscritta la sua chiave, come record. Uno per ristorante, quindi questa collezione è di solito una pagina sola.

CampoTipoSignificato
namestringa, obbligatorioIl nome della sede, come viene mostrato nell'app.
addressstringa, obbligatorioL'indirizzo postale, come stringa unica.
closingDaysstringa[], obbligatorioI giorni in cui la sede è chiusa. Semplici stringhe, non vincolate dallo schema.
exceptionalClosuresoggetto[]Chiusure straordinarie, ciascuna un oggetto con from e to.
detailedAddressoggetto | nullL'indirizzo scomposto in parti, quando l'app lo tiene così.
deliveryAddressstringa | nullDove vengono consegnate le merci, quando differisce da address.
deliveryDetailedAddressoggetto | nullLo stesso, in parti.
preferredLanguagestringa | nullLa lingua in cui lavora il ristorante.
multiAreabooleano | nullSe la sede è suddivisa in più di un'area.
settingsoggetto | nullImpostazioni dell'app. La forma è dell'app e si muove.
{
  "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'esempio mostra solo la metà operativa. Accanto a essa il record porta quella commerciale: subscription, subscriptions, companyName, billingAddress, billingEmail, tvaNumber, trialEndDate, customerId, discount. Esistono, sono nel documento OpenAPI, e questa pagina non glieli illustrerà.

Legga i campi operativi e lasci stare quelli commerciali. Descrivono il contratto del ristorante con noi, non la sua cucina. Non sono un'API di fatturazione, cambiano per ragioni che non hanno nulla a che fare con la sicurezza alimentare, e un'integrazione che restituisce a un cliente lo stato del suo abbonamento è un ticket di assistenza in attesa di accadere.

closingDays è un elenco di semplici stringhe. Lo schema non impone loro alcuna forma, quindi le legga e le mostri; non ci faccia uno switch sopra e non dia per scontata una particolare grafia o un particolare uso delle maiuscole.

users

Gli account del personale che registra i controlli. È a questi che punta lo userId di un record di temperatura.

CampoTipoSignificato
firstNamestringa, obbligatorioNome di battesimo, così come inserito.
lastNamestringa, obbligatorioCognome, così come inserito.
emailstringa, obbligatorioL'indirizzo email dell'account.
phoneNumberstringa | nullUn recapito telefonico, quando ne è stato fornito uno.
modulesstringa[]Quali parti dell'app usa questa persona. Stringhe libere.
preferredNotificationTimestringa | nullQuando questa persona ha chiesto di ricevere un promemoria.
isChildrenbooleano | nullUn indicatore interno. Lo schema non gli dà alcun significato; non ci costruisca sopra.
isDeletedbooleano | nullL'indicatore di dismissione proprio dell'app — e qui ammette null, a differenza che altrove.
{
  "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
  }
}

Questi sono dati personali. email, phoneNumber e i due campi del nome identificano una persona reale, e sono in questa API per un solo motivo: un record di sicurezza alimentare deve dire chi ha effettuato il controllo. È l'unico compito che hanno qui.

Quindi memorizzi l'id e risolva un nome da mostrare quando le serve. Non copi i recapiti nelle sue tabelle, nei suoi log, nei suoi eventi di analytics o in uno strumento di terze parti solo perché si trovavano nel payload. Il ristorante è il titolare del trattamento per il suo personale, e ogni copia che lei fa è una copia di cui adesso deve rendere conto.

isDeleted ammette null in questa collezione dove altrove è obbligatorio, quindi tratti un indicatore mancante come «non dismesso» anziché lasciarlo passare per errore come qualcosa di simile a false. La differenza fra questo e il deleted dell'involucro è spiegata nella pagina del catalogo.

areas

Una zona del ristorante. Due campi, e molto più importante di quanto due campi lascino pensare.

CampoTipoSignificato
namestringa, obbligatorioLa zona come la nomina il ristorante — cucina, cella frigorifera, bar.
isDeletedbooleano, obbligatorioL'indicatore di dismissione dell'app. Veda la pagina del catalogo.
{
  "id": "9c2b17ae-4d80-4f55-b3e1-0a6c8f4d2b19",
  "collection": "areas",
  "deleted": false,
  "capturedAt": "1789301120440",
  "receivedAt": "1789301126871",
  "sequence": "12",
  "data": {
    "name": "Chambre froide",
    "isDeleted": false
  }
}

areaId è il modo in cui tutto viene raggruppato per zona. Apparecchiature, apparecchiature di cottura, friggitrici e compiti di pulizia ne portano tutti uno, ed è l'unico collegamento fra una lettura e una parte dell'edificio. Sui record stessi non c'è alcun areaId: ci si arriva passando per l'apparecchiatura.

areaId ammette null ovunque compaia. Un frigorifero senza area è normale — significa che nessuno gliel'ha assegnata, di solito in una sede a locale unico dove multiArea è false. Raggruppi quelli sotto «non assegnati» anziché scartarli.

equipment

Frigoriferi, congelatori e altre unità della catena del freddo. Il record che decide se una temperatura fosse accettabile.

CampoTipoSignificato
indexintero, obbligatorioLa posizione che il ristorante le ha dato nel proprio elenco. Ordine di visualizzazione, non un identificatore.
typestringa, obbligatorioChe tipo di unità è. Una stringa non vincolata — la legga, non ci faccia uno switch sopra.
namestringa, obbligatorioL'unità come la chiama la cucina.
minnumero | nullLa temperatura accettabile più bassa.
maxnumero | nullLa temperatura accettabile più alta.
areaIdstringa | nullL'area in cui si trova.
equipmentSensorIdstringa | nullIl sensore installato su di essa, quando ce n'è uno.
statestringa | nullLo stato attuale dell'unità, come stringa non vincolata.
outOfRangeCountnumero | nullUn conteggio di letture fuori intervallo che l'app mantiene.
isDeletedbooleano, obbligatorioL'indicatore di dismissione dell'app.
{
  "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 e max sono l'unico verdetto che ottiene. Un record di temperatura porta un valore, un'ora e l'apparecchiatura su cui è stato rilevato — niente che dica se andasse bene. -14,4 °C è una crisi in un frigorifero e un martedì normale in un congelatore, e questo record è ciò che distingue le due cose. Ogni dato di conformità che costruisce si unisce qui.

Ammettono anche null. Quando min o max manca lei non ha alcuna soglia, e la risposta onesta per quella lettura è «sconosciuto», non «nell'intervallo». Lo dica nella sua interfaccia anziché ripiegare sul verde.

Le soglie sono quelle attuali, non quelle che si applicavano. L'istantanea le dà il min e il max di oggi; una lettura di marzo è stata giudicata rispetto a ciò che era impostato a marzo, e il ristorante può cambiarli in qualsiasi momento. Se rivaluta vecchie letture rispetto ai numeri di oggi — che è tutto ciò che questa API le consente di fare — etichetti il risultato come il suo ricalcolo, non come ciò che ha visto la cucina.

type e state sono stringhe non vincolate. Nello schema sono entrambi singoli campi di testo libero senza alcun elenco alle spalle. Li mostri, ci raggruppi sopra se deve, e non lasci mai che un valore non riconosciuto scivoli in silenzio attraverso uno switch.

outOfRangeCount è il numero dell'app, non il suo. Lo schema non dice quale finestra copra, e lei non può ricostruirlo dalle istantanee che trova qui. Lo tratti come un segnale dell'app e, se le serve un conteggio che possa difendere, calcoli il suo a partire dalle letture e dalle soglie e lo etichetti come suo.

equipmentSensorId punta dall'apparecchiatura al suo sensore. Il record del sensore punta nell'altra direzione. Entrambi i lati ammettono null, e non è garantito che uno sia compilato solo perché lo è l'altro.

sensors

Le sonde wireless. Due campi, entrambi opzionali.

CampoTipoSignificato
macAddressstringa | nullL'indirizzo hardware della sonda — come distingue un dispositivo fisico da un altro.
sensorEquipmentIdstringa | nullL'apparecchiatura che questo sensore sorveglia.
{
  "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"
  }
}

Noti i due nomi di campo, che sono l'immagine speculare l'uno dell'altro e sono facili da invertire: l'apparecchiatura porta equipmentSensorId, un sensore porta sensorEquipmentId. Legga quello sbagliato e ottiene undefined senza alcun errore.

Qui non c'è alcun nome, alcun livello della batteria e alcun orario di ultimo contatto. Cosa ha riportato un sensore sta su temperature-readings, e quelle sono indicizzate per equipmentId anziché per sensore — quindi una sonda che è ammutolita è identica a una che non aveva nulla da riportare.

cooking-equipment

Forni, armadi di mantenimento e il resto del lato caldo. Stessa idea di equipment, con la meccanica della catena del freddo tolta di mezzo.

CampoTipoSignificato
indexintero, obbligatorioOrdine di visualizzazione nell'elenco del ristorante.
typestringa, obbligatorioChe tipo di unità è. Stringa non vincolata.
namestringa, obbligatorioL'unità come la chiama la cucina.
minnumero | nullLa temperatura accettabile più bassa.
maxnumero | nullLa temperatura accettabile più alta.
areaIdstringa | nullL'area in cui si trova.
isDeletedbooleano, obbligatorioL'indicatore di dismissione dell'app.
{
  "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
  }
}

Nessuno state, nessun outOfRangeCount, nessun sensore: le unità di cottura vengono lette da una persona, quindi non c'è nulla che le sorvegli fra un controllo e l'altro. Le loro letture sono cooking-temperature-records, che puntano qui con cookingEquipmentId — un nome di campo diverso da quello della catena del freddo, e il solito punto in cui un client legge il nulla in silenzio.

Leggere la mappa

Tutto ciò che sta in questa pagina è piccolo, cambia lentamente ed è necessario prima che qualsiasi altra collezione abbia senso.

  • La carichi per prima e la tenga. Percorra areas, equipment, cooking-equipment e sensors prima di percorrere qualsiasi collezione di record, e risolva nomi e soglie dalla sua copia anziché per ogni record.
  • La aggiorni comunque. Le apparecchiature vengono rinominate, le soglie corrette, un frigorifero si sposta in un'altra area. Una copia presa una volta e mai più percorsa diverge senza mai fallire.
  • Conservi gli id, non le copie — soprattutto per le persone. Memorizzi lo userId e risolva un nome al momento della visualizzazione. Veda la nota sui dati personali qui sopra.
  • Entrambi gli indicatori di cancellazione, ovunque. Il deleted dell'involucro e l'isDeleted di data sono cose diverse, e la pagina del catalogo spiega quale sia quale.

Ultimo aggiornamento 2026-09-20.