# 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.

Source: https://developers.backresto.com/it/docs/facilities

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](https://developers.backresto.com/it/docs/temperatures)
è 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.

| Collezione | Scope | Che cos'è un record |
| --- | --- | --- |
| `restaurants` | `restaurants:read` | La sede stessa: nome, indirizzo, giorni di chiusura, impostazioni. |
| `users` | `users:read` | Un account del personale che registra i controlli. |
| `areas` | `areas:read` | Una zona in cui la sede è suddivisa: cucina, cella frigorifera, bar. |
| `equipment` | `equipment:read` | Un frigorifero, un congelatore, un'unità della catena del freddo, con le sue soglie. |
| `sensors` | `sensors:read` | Una sonda wireless, per indirizzo MAC. |
| `cooking-equipment` | `cooking-equipment:read` | Un forno o un'altra unità di cottura, con le sue soglie. |

Tutte e sei condividono l'[involucro di snapshot](https://developers.backresto.com/it/docs/collections): `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.

| Campo | Tipo | Significato |
| --- | --- | --- |
| `name` | string, **obbligatorio** | Il nome della sede, come viene mostrato nell'app. |
| `address` | string, **obbligatorio** | L'indirizzo postale, come stringa unica. |
| `closingDays` | string[], **obbligatorio** | I giorni in cui la sede è chiusa. Semplici stringhe, non vincolate dallo schema. |
| `exceptionalClosures` | object[] | Chiusure straordinarie, ciascuna un oggetto con `from` e `to`. |
| `detailedAddress` | object \| null | L'indirizzo scomposto in parti, quando l'app lo tiene così. |
| `deliveryAddress` | string \| null | Dove vengono consegnate le merci, quando differisce da `address`. |
| `deliveryDetailedAddress` | object \| null | Lo stesso, in parti. |
| `preferredLanguage` | string \| null | La lingua in cui lavora il ristorante. |
| `multiArea` | boolean \| null | Se la sede è suddivisa in più di un'area. |
| `settings` | object \| null | Impostazioni dell'app. La forma è dell'app e si muove. |

```json
{
  "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 che prima o poi arriverà.

**`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](https://developers.backresto.com/it/docs/temperatures).

| Campo | Tipo | Significato |
| --- | --- | --- |
| `firstName` | string, **obbligatorio** | Nome di battesimo, così come inserito. |
| `lastName` | string, **obbligatorio** | Cognome, così come inserito. |
| `email` | string, **obbligatorio** | L'indirizzo email dell'account. |
| `phoneNumber` | string \| null | Un recapito telefonico, quando ne è stato fornito uno. |
| `modules` | string[] | Quali parti dell'app usa questa persona. Stringhe libere. |
| `preferredNotificationTime` | string \| null | Quando questa persona ha chiesto di ricevere un promemoria. |
| `isChildren` | boolean \| null | Un indicatore interno. Lo schema non gli dà alcun significato; non ci costruisca sopra. |
| `isDeleted` | boolean \| null | L'indicatore di dismissione proprio dell'app, e qui ammette null, a differenza che altrove. |

```json
{
  "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é lasciare che
per sbaglio scivoli via come se fosse false. La differenza fra questo e
il `deleted` dell'involucro è
[spiegata nella pagina del catalogo](https://developers.backresto.com/it/docs/catalogue).

## `areas`

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

| Campo | Tipo | Significato |
| --- | --- | --- |
| `name` | string, **obbligatorio** | La zona come la nomina il ristorante: cucina, cella frigorifera, bar. |
| `isDeleted` | boolean, **obbligatorio** | L'indicatore di dismissione dell'app. Veda [la pagina del catalogo](https://developers.backresto.com/it/docs/catalogue). |

```json
{
  "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](https://developers.backresto.com/it/docs/temperatures) 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.

| Campo | Tipo | Significato |
| --- | --- | --- |
| `index` | integer, **obbligatorio** | La posizione che il ristorante le ha dato nel proprio elenco. Ordine di visualizzazione, non un identificatore. |
| `type` | string, **obbligatorio** | Che tipo di unità è. Una stringa non vincolata: la legga, non ci faccia uno switch sopra. |
| `name` | string, **obbligatorio** | L'unità come la chiama la cucina. |
| `min` | number \| null | La temperatura accettabile più bassa. |
| `max` | number \| null | La temperatura accettabile più alta. |
| `areaId` | string \| null | L'area in cui si trova. |
| `equipmentSensorId` | string \| null | Il sensore installato su di essa, quando ce n'è uno. |
| `state` | string \| null | Lo stato attuale dell'unità, come stringa non vincolata. |
| `outOfRangeCount` | number \| null | Un conteggio di letture fuori intervallo che l'app mantiene. |
| `isDeleted` | boolean, **obbligatorio** | L'indicatore di dismissione dell'app. |

```json
{
  "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](https://developers.backresto.com/it/docs/temperatures) porta un valore, un'ora e
l'apparecchiatura su cui è stato rilevato, ma 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 fa una join 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.** Lo snapshot
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 dagli snapshot 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.

| Campo | Tipo | Significato |
| --- | --- | --- |
| `macAddress` | string \| null | L'indirizzo hardware della sonda: come distingue un dispositivo fisico da un altro. |
| `sensorEquipmentId` | string \| null | L'apparecchiatura che questo sensore sorveglia. |

```json
{
  "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`](https://developers.backresto.com/it/docs/temperatures), 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.

| Campo | Tipo | Significato |
| --- | --- | --- |
| `index` | integer, **obbligatorio** | Ordine di visualizzazione nell'elenco del ristorante. |
| `type` | string, **obbligatorio** | Che tipo di unità è. Stringa non vincolata. |
| `name` | string, **obbligatorio** | L'unità come la chiama la cucina. |
| `min` | number \| null | La temperatura accettabile più bassa. |
| `max` | number \| null | La temperatura accettabile più alta. |
| `areaId` | string \| null | L'area in cui si trova. |
| `isDeleted` | boolean, **obbligatorio** | L'indicatore di dismissione dell'app. |

```json
{
  "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`](https://developers.backresto.com/it/docs/temperatures), 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](https://developers.backresto.com/it/docs/catalogue) spiega quale sia quale.
