# Standort & Personen

> Der Standort, sein Personal, seine Zonen und seine Geräte: die feste Karte, an der jede Temperaturmessung hängt.

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

Das sind die Dinge, die sich nicht bewegen: das Restaurant selbst, die Menschen,
die darin arbeiten, die Zonen, in die es aufgeteilt ist, und die Geräte, die
Lebensmittel auf Temperatur halten. Nichts davon ist eine Kontrolle.

Es entscheidet trotzdem, was die Kontrollen bedeuten. Eine
[Temperatur](https://developers.backresto.com/de/docs/temperatures) ist eine Zahl ohne Urteil, solange Sie nicht
die Schwellen von dem Gerät lesen, an dem sie gemessen wurde, und eine Messung
gehört nur deshalb zu einem Teil des Gebäudes, weil das Gerät sagt, in welchem
Bereich es steht. Liegen Sie bei dieser Seite falsch, ist jede Kennzahl danach
still und leise falsch.

| Collection | Scope | Was ein Datensatz ist |
| --- | --- | --- |
| `restaurants` | `restaurants:read` | Der Standort selbst: Name, Adresse, Schließtage, Einstellungen. |
| `users` | `users:read` | Ein Mitarbeiterkonto, das Kontrollen erfasst. |
| `areas` | `areas:read` | Eine Zone, in die der Standort aufgeteilt ist: Küche, Kühlraum, Bar. |
| `equipment` | `equipment:read` | Ein Kühlschrank, ein Gefriergerät, eine Kühlketten-Einheit, mit ihren Schwellen. |
| `sensors` | `sensors:read` | Ein Funkfühler, über seine MAC-Adresse. |
| `cooking-equipment` | `cooking-equipment:read` | Ein Ofen oder ein anderes Gargerät, mit seinen Schwellen. |

Alle sechs teilen sich den [Snapshot-Umschlag](https://developers.backresto.com/de/docs/collections): `id`,
`deleted`, `capturedAt`, `receivedAt`, `sequence` und der Rest stehen neben dem
`data`, das unten beschrieben ist.

## `restaurants`

Der Standort, auf den Ihr Schlüssel begrenzt ist, als Datensatz. Einer pro
Restaurant, diese Collection ist also meist eine einzige Seite.

| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `name` | string, **Pflicht** | Der Name des Standorts, so wie er in der App angezeigt wird. |
| `address` | string, **Pflicht** | Die Postanschrift, als ein einziger String. |
| `closingDays` | string[], **Pflicht** | Die Tage, an denen der Standort geschlossen ist. Schlichte Strings, vom Schema nicht eingeschränkt. |
| `exceptionalClosures` | object[] | Einmalige Schließungen, jede ein Objekt mit `from` und `to`. |
| `detailedAddress` | object \| null | Die Adresse in ihre Teile zerlegt, wenn die App sie so hält. |
| `deliveryAddress` | string \| null | Wohin Waren geliefert werden, wenn es von `address` abweicht. |
| `deliveryDetailedAddress` | object \| null | Dasselbe, in Teilen. |
| `preferredLanguage` | string \| null | Die Sprache, in der das Restaurant arbeitet. |
| `multiArea` | boolean \| null | Ob der Standort in mehr als einen Bereich aufgeteilt ist. |
| `settings` | object \| null | App-Einstellungen. Die Struktur gehört der App, und sie bewegt sich. |

```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
  }
}
```

Das Beispiel zeigt nur die betriebliche Hälfte. Daneben trägt der Datensatz die
kaufmännische: `subscription`, `subscriptions`, `companyName`, `billingAddress`,
`billingEmail`, `tvaNumber`, `trialEndDate`, `customerId`, `discount`. Es gibt
sie, sie stehen im OpenAPI-Dokument, und diese Seite führt Sie nicht durch sie
hindurch.

**Lesen Sie die betrieblichen Felder und lassen Sie die kaufmännischen in
Ruhe.** Sie beschreiben den Vertrag des Restaurants mit uns, nicht seine Küche.
Sie sind keine Abrechnungs-API, sie ändern sich aus Gründen, die nichts mit
Lebensmittelsicherheit zu tun haben, und eine Integration, die einem Kunden
seinen Abonnementstatus zurückspiegelt, ist ein Support-Ticket, das nur auf
seinen Termin wartet.

**`closingDays` ist eine Liste schlichter Strings.** Das Schema gibt ihnen keine
Form, lesen Sie sie also und zeigen Sie sie; verzweigen Sie nicht darauf
und nehmen Sie keine bestimmte Schreibweise und keine bestimmte Groß- und
Kleinschreibung an.

## `users`

Die Mitarbeiterkonten, die Kontrollen erfassen. Auf sie zeigt das `userId` eines
[Temperaturdatensatzes](https://developers.backresto.com/de/docs/temperatures).

| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `firstName` | string, **Pflicht** | Vorname, so wie eingetragen. |
| `lastName` | string, **Pflicht** | Nachname, so wie eingetragen. |
| `email` | string, **Pflicht** | Die E-Mail-Adresse des Kontos. |
| `phoneNumber` | string \| null | Eine Rufnummer, sofern eine angegeben wurde. |
| `modules` | string[] | Welche Teile der App diese Person nutzt. Freie Strings. |
| `preferredNotificationTime` | string \| null | Wann diese Person erinnert werden möchte. |
| `isChildren` | boolean \| null | Ein internes Flag. Das Schema gibt ihm keine Bedeutung; bauen Sie nicht darauf. |
| `isDeleted` | boolean \| null | Das eigene Flag der App für ausgemusterte Einträge. Hier darf es `null` sein, anders als sonst. |

```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
  }
}
```

**Das sind personenbezogene Daten.** `email`, `phoneNumber` und die beiden
Namensfelder benennen eine echte Person, und sie stehen aus einem einzigen Grund
in dieser API: Ein Datensatz zur Lebensmittelsicherheit muss sagen, wer die
Kontrolle durchgeführt hat. Das ist ihre einzige Aufgabe hier.

Speichern Sie also die `id` und lösen Sie einen Anzeigenamen auf, wenn Sie einen
brauchen. Kopieren Sie Kontaktdaten nicht in Ihre eigenen Tabellen, Ihre
Protokolle, Ihre Analytics-Ereignisse oder ein Werkzeug eines Dritten, nur weil
sie zufällig in der Antwort standen. Das Restaurant ist für sein Personal der
Verantwortliche im Sinne des Datenschutzes, und jede Kopie, die Sie anlegen, ist
eine, für die es nun geradestehen muss.

`isDeleted` darf in dieser Collection `null` sein, wo es sonst Pflicht ist;
behandeln Sie ein fehlendes Flag also als „nicht ausgemustert“, statt es
versehentlich als falsch-artig durchfallen zu lassen. Der Unterschied zum
`deleted` des Umschlags ist
[auf der Katalogseite erklärt](https://developers.backresto.com/de/docs/catalogue).

## `areas`

Eine Zone des Restaurants. Zwei Felder, und weit wichtiger, als zwei Felder
vermuten lassen.

| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `name` | string, **Pflicht** | Die Zone, so wie das Restaurant sie nennt: Küche, Kühlraum, Bar. |
| `isDeleted` | boolean, **Pflicht** | Das Flag der App für ausgemusterte Einträge. Siehe [die Katalogseite](https://developers.backresto.com/de/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
  }
}
```

**Über `areaId` wird alles nach Zone gruppiert.** Geräte, Gargeräte, Fritteusen
und Reinigungsaufgaben tragen alle eines, und es ist die einzige Verbindung
zwischen einer [Messung](https://developers.backresto.com/de/docs/temperatures) und einem Teil des Gebäudes. Auf
den Datensätzen selbst gibt es kein `areaId`: Sie kommen über das Gerät dorthin.

`areaId` darf überall, wo es auftaucht, `null` sein. Ein Kühlschrank ohne
Bereich ist normal: Es heißt, dass ihn niemand zugeordnet hat, meist an einem
Standort mit einem einzigen Raum, wo `multiArea` falsch ist. Fassen Sie solche
unter „nicht zugeordnet“ zusammen, statt sie wegzulassen.

## `equipment`

Kühlschränke, Gefriergeräte und andere Einheiten der Kühlkette. Der Datensatz,
der entscheidet, ob eine Temperatur zulässig war.

| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `index` | integer, **Pflicht** | Die Position, die das Restaurant ihm in seiner eigenen Liste gegeben hat. Anzeigereihenfolge, keine Kennung. |
| `type` | string, **Pflicht** | Um welche Art Einheit es sich handelt. Ein nicht eingeschränkter String. Lesen Sie ihn, verzweigen Sie nicht darauf. |
| `name` | string, **Pflicht** | Die Einheit, so wie die Küche sie nennt. |
| `min` | number \| null | Die niedrigste zulässige Temperatur. |
| `max` | number \| null | Die höchste zulässige Temperatur. |
| `areaId` | string \| null | Der Bereich, in dem sie steht. |
| `equipmentSensorId` | string \| null | Der Sensor, der daran angebracht ist, sofern es einen gibt. |
| `state` | string \| null | Der aktuelle Zustand der Einheit, als nicht eingeschränkter String. |
| `outOfRangeCount` | number \| null | Eine Anzahl von Messungen außerhalb des Bereichs, die die App führt. |
| `isDeleted` | boolean, **Pflicht** | Das Flag der App für ausgemusterte Einträge. |

```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` und `max` sind das einzige Urteil, das Sie bekommen.** Ein
[Temperaturdatensatz](https://developers.backresto.com/de/docs/temperatures) trägt einen Wert, eine Zeit und das
Gerät, an dem gemessen wurde, aber nichts, was sagt, ob es in Ordnung
war. -14,4 °C sind in einem Kühlschrank eine Krise und in einem Gefriergerät
ein normaler Dienstag, und dieser Datensatz ist es, der beides unterscheidet.
Jede Compliance-Kennzahl, die Sie bauen, verbindet sich hier.

Sie dürfen auch `null` sein. Fehlt `min` oder `max`, haben Sie keine Schwelle,
und die ehrliche Antwort für diese Messung lautet „unbekannt“, nicht „im
Bereich“. Sagen Sie das in Ihrer Oberfläche, statt auf Grün zurückzufallen.

**Die Schwellen sind die heutigen, nicht die, die damals galten.** Der Snapshot
gibt Ihnen das `min` und `max` von heute; eine Messung vom März wurde an dem
gemessen, was im März eingestellt war, und das Restaurant kann sie jederzeit
ändern. Wenn Sie alte Messungen gegen die heutigen Zahlen neu bewerten (mehr
lässt diese API nicht zu), kennzeichnen Sie das Ergebnis als Ihre
Neuberechnung, nicht als das, was die Küche gesehen hat.

**`type` und `state` sind nicht eingeschränkte Strings.** Beide sind im Schema
einzelne Freitextfelder ohne Liste dahinter. Zeigen Sie sie an, gruppieren Sie
darauf, wenn es sein muss, und lassen Sie einen unbekannten Wert niemals
stillschweigend durch ein `switch` fallen.

**`outOfRangeCount` ist die Zahl der App, nicht Ihre.** Das Schema sagt nicht,
welchen Zeitraum sie abdeckt, und Sie können sie aus den Snapshots hier nicht
nachbauen. Behandeln Sie sie als Hinweis aus der App, und wenn Sie eine Zahl
brauchen, die Sie vertreten können, berechnen Sie Ihre eigene aus den Messungen
und den Schwellen und kennzeichnen Sie sie als Ihre.

`equipmentSensorId` zeigt vom Gerät auf seinen Sensor. Der Sensordatensatz zeigt
in die andere Richtung zurück. Beide Seiten dürfen `null` sein, und keine ist
garantiert gefüllt, nur weil die andere es ist.

## `sensors`

Die Funkfühler. Zwei Felder, beide optional.

| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `macAddress` | string \| null | Die Hardware-Adresse des Fühlers. So unterscheiden Sie ein physisches Gerät vom anderen. |
| `sensorEquipmentId` | string \| null | Das Gerät, das dieser Sensor überwacht. |

```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"
  }
}
```

Achten Sie auf die beiden Feldnamen, die Spiegelbilder sind und sich leicht
vertauschen lassen: Ein Gerät trägt `equipmentSensorId`, ein Sensor trägt
`sensorEquipmentId`. Lesen Sie das falsche, bekommen Sie `undefined` ohne
Fehler.

Hier gibt es keinen Namen, keinen Batteriestand und keinen Zeitpunkt der letzten
Meldung. Was ein Sensor gemeldet hat, steht in
[`temperature-readings`](https://developers.backresto.com/de/docs/temperatures), und deren Schlüssel ist
`equipmentId`, nicht der Sensor. Ein Fühler, der verstummt ist, sieht also
genau so aus wie einer, der nichts zu melden hatte.

## `cooking-equipment`

Öfen, Warmhalteschränke und der Rest der heißen Seite. Derselbe Gedanke wie bei
`equipment`, ohne die Technik der Kühlkette.

| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `index` | integer, **Pflicht** | Anzeigereihenfolge in der eigenen Liste des Restaurants. |
| `type` | string, **Pflicht** | Um welche Art Einheit es sich handelt. Nicht eingeschränkter String. |
| `name` | string, **Pflicht** | Die Einheit, so wie die Küche sie nennt. |
| `min` | number \| null | Die niedrigste zulässige Temperatur. |
| `max` | number \| null | Die höchste zulässige Temperatur. |
| `areaId` | string \| null | Der Bereich, in dem sie steht. |
| `isDeleted` | boolean, **Pflicht** | Das Flag der App für ausgemusterte Einträge. |

```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
  }
}
```

Kein `state`, kein `outOfRangeCount`, kein Sensor: Gargeräte werden von einer
Person abgelesen, zwischen den Kontrollen beobachtet sie also nichts. Ihre
Messungen sind [`cooking-temperature-records`](https://developers.backresto.com/de/docs/temperatures), die mit
`cookingEquipmentId` hierher zeigen: ein anderer Feldname als der der
Kühlkette, und die übliche Stelle, an der ein Client stillschweigend nichts
liest.

## Die Karte lesen

Alles auf dieser Seite ist klein, ändert sich langsam und wird gebraucht, bevor
irgendeine andere Collection Sinn ergibt.

- **Laden Sie es zuerst und halten Sie es.** Durchlaufen Sie `areas`,
  `equipment`, `cooking-equipment` und `sensors`, bevor Sie irgendeine
  Datensatz-Collection durchlaufen, und lösen Sie Namen und Schwellen aus Ihrer
  eigenen Kopie auf, statt pro Datensatz.
- **Frischen Sie es trotzdem auf.** Geräte werden umbenannt, Schwellen werden
  korrigiert, ein Kühlschrank zieht in einen anderen Bereich. Eine Kopie, die
  einmal genommen und nie wieder durchlaufen wird, läuft auseinander, ohne je zu
  scheitern.
- **Halten Sie Kennungen, keine Kopien, besonders bei Personen.** Speichern Sie
  `userId` und lösen Sie einen Namen beim Anzeigen auf. Siehe die Anmerkung zu
  personenbezogenen Daten oben.
- **Beide Lösch-Flags, überall.** Das `deleted` des Umschlags und das
  `isDeleted` in `data` sind verschiedene Dinge, und
  [die Katalogseite](https://developers.backresto.com/de/docs/catalogue) erklärt, welches welches ist.
