# Lieferungen

> Wareneingangskontrollen und die bei der Warenannahme aufgenommenen Fotos: zwei Collections, jedes Feld und was sie im laufenden Betrieb bedeuten.

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

Eine Lieferung ist eine Wareneingangskontrolle: Ein Mitarbeiter nimmt eine
Bestellung an, erfasst Temperaturen und entscheidet, ob er sie annimmt. Es ist
der Datensatz, nach dem ein Lebensmittelkontrolleur fragt. Die bei der
Warenannahme aufgenommenen Bilder sind eine zweite Collection daneben.

| Collection | Scope | Was ein Datensatz ist |
| --- | --- | --- |
| `deliveries` | `deliveries:read` | Eine Wareneingangskontrolle, mit einer Temperaturmessung pro Produkt. |
| `delivery-pictures` | `delivery-pictures:read` | Ein Foto, das während einer Lieferung aufgenommen wurde und auf sie zurückverweist. |

Beide sind gewöhnliche [Collections](https://developers.backresto.com/de/docs/collections): derselbe
Snapshot-Umschlag um `data`, dieselben Endpunkte, derselbe Cursor, dieselbe
90-Tage-Regel. Sie tragen die Lieferhistorie, die die App noch hielt, als sie
geöffnet wurden, nicht nur das, was seitdem erfasst wurde.

## `deliveries`

| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `timestamp` | string, **Pflicht** | Wann die Kontrolle stattfand, als String mit Millisekunden seit Epoch. |
| `isCompliant` | boolean, **Pflicht** | Das Urteil des Mitarbeiters über die Lieferung als Ganzes. |
| `supplierDeliveriesId` | string \| null | Der [Lieferant](https://developers.backresto.com/de/docs/catalogue), von dem die Ware kam: die `id` eines `suppliers`-Datensatzes. |
| `temperatureRecords` | array \| null | Ein Eintrag pro gemessenem Produkt. Siehe unten. |
| `nonComplianceReasons` | string[] \| null | Warum die Lieferung abgelehnt oder unter Vorbehalt angenommen wurde. Freitext, vom Mitarbeiter gewählt; leer, wenn sie konform ist. |
| `correctiveActions` | string[] \| null | Was daraufhin getan wurde. Leer, wenn nichts nötig war. |
| `commentary` | string \| null | Freitextnotiz. Oft `null`. |
| `userId` | string \| null | Der [Mitarbeiter](https://developers.backresto.com/de/docs/facilities), der sie angenommen hat, sofern die App einen erfasst hat. |

### `temperatureRecords[]`

| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `product` | string | Was gemessen wurde, so wie der Mitarbeiter es benannt hat. Nie leer. |
| `lotNumber` | string \| null | Die Charge oder das Los, sofern sie erfasst wurde. |
| `value` | number \| null | Der Messwert. `null`, wenn das Produkt ohne Messung erfasst wurde. |
| `unit` | `"C"` | Immer Celsius. Siehe unten. |

**Temperaturen sind immer in Celsius.** Ein Restaurant, dessen App in
Fahrenheit erfasst, sendet weiterhin Messwerte in Fahrenheit, und sie werden
beim Eingang umgerechnet, bevor sie gespeichert werden. `unit` ist in jedem
Eintrag `"C"`, ein Konsument muss also nie umrechnen oder raten.

```json
{
  "id": "8f2c1b04-0d5a-4b7e-9f31-6ad2c0e77a51",
  "collection": "deliveries",
  "deleted": false,
  "capturedAt": "1787932805120",
  "receivedAt": "1787932911004",
  "sequence": "88",
  "data": {
    "timestamp": "1787932800000",
    "isCompliant": false,
    "supplierDeliveriesId": "supplier-7",
    "temperatureRecords": [
      { "product": "Poulet fermier", "lotNumber": "L2291", "value": 6.4, "unit": "C" },
      { "product": "Farine T55", "lotNumber": null, "value": null, "unit": "C" }
    ],
    "nonComplianceReasons": ["Température trop élevée"],
    "correctiveActions": ["Produit refusé", "Fournisseur prévenu"],
    "commentary": "Camion en retard, rupture de chaîne du froid probable",
    "userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
  }
}
```

## `delivery-pictures`

Ein Etikett, eine beschädigte Palette, ein Fühler an einer Anzeige: Diese Bilder
sind der Beleg hinter `isCompliant`. Jedes ist ein Datensatz, der auf seine
Lieferung verweist, und es trägt eine Datei, die Bytes kommen also vom
Assets-Endpunkt.

| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `timestamp` | string, **Pflicht** | Wann das Bild aufgenommen wurde, als String mit Millisekunden seit Epoch. |
| `deliveryId` | string \| null | Der `deliveries`-Datensatz, zu dem es gehört. |
| `part` | number \| null | Position dieses Datensatzes innerhalb eines Bildes, das die App in mehreren Teilen gespeichert hat. Reichen Sie es durch, statt es zu interpretieren. |
| `groupId` | string \| null | Hält diese Teile zusammen. Datensätze mit derselben `groupId` sind dasselbe Bild. |
| `asset` | object, **Pflicht** | Benennt die Datei: immer `objectKey` und `status`, dazu `contentType`, `byteLength` und `sha256`, sobald sie hochgeladen ist. |

```json
{
  "id": "c61e0a7d-35b2-4f89-a1d4-7e90b2c45f18",
  "collection": "delivery-pictures",
  "deleted": false,
  "capturedAt": "1787932861240",
  "receivedAt": "1787932990517",
  "sequence": "143",
  "data": {
    "timestamp": "1787932860000",
    "deliveryId": "8f2c1b04-0d5a-4b7e-9f31-6ad2c0e77a51",
    "part": 4,
    "groupId": "b4d81f0a-c2e7-4a55-9d10-6f3e8a2b7c91",
    "asset": {
      "objectKey": "delivery-pictures/b4d81f0a/4.jpg",
      "status": "uploaded",
      "contentType": "image/jpeg",
      "byteLength": 902450,
      "sha256": "9f2a…"
    }
  }
}
```

Die Bytes sind ein zweiter Aufruf, mit der eigenen ID des Datensatzes:

```http
GET /v1/restaurants/{restaurantId}/collections/delivery-pictures/records/{recordId}/assets
```

Er antwortet mit signierten URLs, die fünfzehn Minuten gültig sind, genau wie
auf [der Collections-Seite](https://developers.backresto.com/de/docs/collections) beschrieben. Fragen Sie nach
irgendeinem Teil eines Bildes, und Sie bekommen dieselbe Datei: Die API löst die
Gruppe für Sie auf, Sie müssen also nicht herausfinden, welcher Teil sie hält.

Um die Fotos einer Lieferung anzuzeigen, durchlaufen Sie `delivery-pictures` und
behalten die Datensätze, deren `deliveryId` die `id` dieser Lieferung ist, einen
pro `groupId`.

## Richtig lesen

**`isCompliant` ist die Aufzeichnung, keine Berechnung.** Es ist das, was die
Person entschieden hat, welche die Lieferung angenommen hat, und es kann von dem
abweichen, was Sie allein aus den Temperaturen schließen würden: eine Messung
mit einem Oberflächenfühler, ein Produkt mit eigenem Grenzwert, eine
Ermessensentscheidung. Stellen Sie es als deren Urteil dar. Wenn Sie ein eigenes
wollen, berechnen Sie es daneben und kennzeichnen Sie es als Ihres.

**Freitextfelder sind Freitext.** `nonComplianceReasons`, `correctiveActions`
und `product` werden vom Personal während des Betriebs getippt, in der Sprache
des Restaurants und mit der Rechtschreibung eines vollen Dienstags. Bauen Sie
daraus kein Enum, verwenden Sie sie nicht als Join-Schlüssel und nehmen Sie
nicht an, dass es Französisch ist.

**`value` kann `null` sein.** Ein Produkt, das ohne Messwert erfasst wurde, ist
normal: Oft ist es Trockenware. Behandeln Sie `null` als „nicht gemessen“, nie
als `0`.

**Zeitstempel sind Strings.** `timestamp` ist ein dezimaler String mit
Millisekunden seit Epoch, weil die gesamte BackResto-Plattform Zeitstempel so
speichert. `Number(timestamp)` ist heute und für die nächsten
paar hunderttausend Jahre sicher, aber lassen Sie einen JSON-Parser den Wert
nicht stillschweigend umwandeln und ihn dann mit verlorener Genauigkeit
zurückschreiben.

**Ein Bild kann nach seiner Lieferung ankommen.** Ein Telefon lädt Fotos hoch,
wenn es kann, manchmal Minuten nach der Kontrolle selbst. Eine Lieferung, zu der
es noch kein Bild gibt, hat vielleicht einfach eines, das noch unterwegs ist;
durchlaufen Sie später erneut, bevor Sie schließen, dass sie keines hat.
