# Livraisons

> Les contrôles à réception et les photographies prises à la réception : deux collections, chaque champ, et ce qu'ils signifient sur le terrain.

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

Une livraison est un contrôle à réception : un membre du personnel qui
réceptionne une commande, relève des températures, et décide de l'accepter ou
non. C'est l'enregistrement que demande un inspecteur de la sécurité
alimentaire. Les photos prises à la réception forment une seconde collection à
côté.

| Collection | Scope | Ce qu'est un enregistrement |
| --- | --- | --- |
| `deliveries` | `deliveries:read` | Un contrôle à réception, avec un relevé de température par produit. |
| `delivery-pictures` | `delivery-pictures:read` | Une photographie prise pendant une livraison, qui pointe vers celle-ci. |

Toutes deux sont des [collections](https://developers.backresto.com/fr/docs/collections) ordinaires : la même
enveloppe de snapshot autour de `data`, les mêmes endpoints, le même curseur, la
même règle des 90 jours. Elles portent l'historique des livraisons que
l'application détenait encore à leur ouverture, pas seulement ce qui a été
enregistré depuis.

## `deliveries`

| Champ | Type | Signification |
| --- | --- | --- |
| `timestamp` | string, **obligatoire** | Quand le contrôle a eu lieu, en millisecondes depuis l'epoch sous forme de chaîne de caractères. |
| `isCompliant` | boolean, **obligatoire** | Le verdict du membre du personnel sur la livraison dans son ensemble. |
| `supplierDeliveriesId` | string \| null | Le [fournisseur](https://developers.backresto.com/fr/docs/catalogue) dont viennent les marchandises : l'`id` d'un enregistrement `suppliers`. |
| `temperatureRecords` | array \| null | Une entrée par produit mesuré. Voir ci-dessous. |
| `nonComplianceReasons` | string[] \| null | Pourquoi la livraison a été refusée ou acceptée avec réserve. Texte libre choisi par le membre du personnel ; vide quand la livraison est conforme. |
| `correctiveActions` | string[] \| null | Ce qui a été fait en réponse. Vide quand rien n'était nécessaire. |
| `commentary` | string \| null | Note en texte libre. Souvent `null`. |
| `userId` | string \| null | Le [membre du personnel](https://developers.backresto.com/fr/docs/facilities) qui l'a réceptionnée, quand l'application en a enregistré un. |

### `temperatureRecords[]`

| Champ | Type | Signification |
| --- | --- | --- |
| `product` | string | Ce qui a été mesuré, tel que le membre du personnel l'a nommé. Jamais vide. |
| `lotNumber` | string \| null | Le lot ou le numéro de lot, quand il a été saisi. |
| `value` | number \| null | Le relevé. `null` quand le produit a été consigné sans mesure. |
| `unit` | `"C"` | Toujours des degrés Celsius. Voir ci-dessous. |

**Les températures sont toujours en degrés Celsius.** Un restaurant dont
l'application relève en Fahrenheit envoie toujours des relevés en Fahrenheit,
et ils sont convertis à leur arrivée, avant d'être stockés. `unit` vaut `"C"`
sur chaque entrée, si bien qu'un consommateur n'a jamais à convertir ni à
deviner.

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

Une étiquette, une palette abîmée, une sonde posée contre un afficheur : ces
photos sont la preuve derrière `isCompliant`. Chacune est un enregistrement qui
pointe vers sa livraison, et elle porte un fichier : les octets viennent donc
de l'endpoint des fichiers.

| Champ | Type | Signification |
| --- | --- | --- |
| `timestamp` | string, **obligatoire** | Quand la photo a été prise, en millisecondes depuis l'epoch sous forme de chaîne de caractères. |
| `deliveryId` | string \| null | L'enregistrement `deliveries` auquel elle appartient. |
| `part` | number \| null | Position de cet enregistrement au sein d'une photo que l'application a stockée en plusieurs parties. Transportez-le plutôt que de l'interpréter. |
| `groupId` | string \| null | Relie ces parties entre elles. Des enregistrements qui partagent un `groupId` sont la même photo. |
| `asset` | object, **obligatoire** | Nomme le fichier : `objectKey` et `status` toujours, plus `contentType`, `byteLength` et `sha256` une fois qu'il est téléversé. |

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

Les octets sont un second appel, sur l'identifiant propre à l'enregistrement :

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

Il répond par des URL signées valables quinze minutes, exactement comme le
décrit [la page des collections](https://developers.backresto.com/fr/docs/collections). Demandez sur n'importe
quelle partie d'une photo et vous obtenez le même fichier : l'API résout le
groupe pour vous, inutile donc de chercher quelle partie le contient.

Pour afficher les photographies d'une livraison, parcourez `delivery-pictures`
et gardez les enregistrements dont le `deliveryId` est l'`id` de cette
livraison, un par `groupId`.

## Le lire correctement

**`isCompliant` est l'enregistrement, pas un calcul.** C'est ce qu'a décidé la
personne qui a réceptionné la livraison, et cela peut diverger de ce que vous
concluriez des seules températures : une mesure prise avec une sonde de surface,
un produit qui a son propre seuil, une appréciation. Présentez-le comme leur
verdict. Si vous voulez le vôtre, calculez-le à côté du leur et indiquez qu'il
est de vous.

**Les champs en texte libre sont du texte libre.** `nonComplianceReasons`,
`correctiveActions` et `product` sont saisis par le personnel pendant le
service, dans la langue du restaurant, avec l'orthographe d'un mardi chargé.
N'en tirez pas une énumération, n'en faites pas la clé d'une jointure, et ne
supposez pas que c'est du français.

**`value` peut être `null`.** Un produit consigné sans relevé est normal. C'est
souvent une denrée sèche. Traitez `null` comme "non mesuré", jamais comme `0`.

**Les horodatages sont des chaînes de caractères.** `timestamp` est une chaîne
de caractères en millisecondes depuis l'epoch, en notation décimale, parce que
toute la plateforme BackResto stocke les horodatages ainsi.
`Number(timestamp)` est sûr aujourd'hui et pour les quelques centaines de
milliers d'années à venir, mais ne laissez pas un analyseur JSON le convertir
silencieusement pour le réécrire ensuite avec une précision perdue.

**Une photo peut arriver après sa livraison.** Un téléphone téléverse les
photographies quand il le peut, parfois quelques minutes après le contrôle
lui-même. Une livraison sans photo pour l'instant peut simplement en avoir une
encore en route ; parcourez à nouveau plus tard avant de conclure qu'elle n'en
a aucune.
