# Refroidissement et procédés alimentaires

> Refroidissement, congélation, remise en température et transport : quatre collections qui enregistrent une transition de température, et à quoi ressemble une transition inachevée.

Source: https://developers.backresto.com/fr/docs/food-processes

Un plat chaud qui refroidit lentement passe des heures dans la plage où les
bactéries se multiplient : la règle est de le descendre vite et de pouvoir le
prouver. Quatre collections enregistrent ce genre de transition. Trois d'entre
elles sont une transition dans le temps (descendre, descendre encore,
remonter) et la quatrième est la même idée dans l'espace.

| Collection | Scope | Ce qu'est un enregistrement |
| --- | --- | --- |
| `cooling` | `cooling:read` | Un produit chaud ramené à la température de conservation au froid. |
| `freezing` | `freezing:read` | Un produit descendu jusqu'à la congélation. |
| `reheating` | `reheating:read` | Un produit froid remonté à la température de service. |
| `transport` | `transport:read` | Un produit déplacé d'un lieu à un autre, avec la température à chaque extrémité. |

Toutes les quatre partagent l'[enveloppe de snapshot](https://developers.backresto.com/fr/docs/collections) :
`id`, `deleted`, `capturedAt`, `receivedAt`, `sequence` et le reste entourent
le `data` décrit ci-dessous.

## `cooling`

Un cycle : un produit, quand il a commencé et à quelle température, quand il
s'est terminé et à quelle température.

| Champ | Type | Signification |
| --- | --- | --- |
| `timestamp` | string, **obligatoire** | L'horodatage propre à l'enregistrement, en millisecondes depuis l'epoch sous forme de chaîne de caractères. Pas l'horloge du cycle : ce sont les deux dates ci-dessous. |
| `product` | string, **obligatoire** | Ce qui a été refroidi, sous forme de texte. |
| `beginDate` | string, **obligatoire** | Quand le cycle a commencé, en millisecondes depuis l'epoch sous forme de chaîne de caractères. |
| `beginTemperature` | number, **obligatoire** | La température de départ. |
| `endDate` | string \| null | Quand il s'est terminé. `null` tant qu'il ne l'est pas. |
| `endTemperature` | number \| null | La température d'arrivée. `null` en même temps qu'`endDate`. |
| `reminderId` | string \| null | Le rappel que l'application a attaché au cycle, quand il y en avait un. |
| `userId` | string \| null | Le [membre du personnel](https://developers.backresto.com/fr/docs/facilities) qui l'a enregistré, quand l'application en a enregistré un. |

```json
{
  "id": "0e5d1a7c-9f24-4b13-8d60-c1a7f0b25e94",
  "collection": "cooling",
  "deleted": false,
  "capturedAt": "1789779612441",
  "receivedAt": "1789779910228",
  "sequence": "1",
  "data": {
    "timestamp": "1789772400000",
    "product": "Blanquette de veau",
    "beginDate": "1789772400000",
    "beginTemperature": 63.5,
    "endDate": "1789779600000",
    "endTemperature": 8.2,
    "userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
  }
}
```

**`product` est un nom, pas une référence.** Il n'y a pas de `productId` sur
ces enregistrements : vous ne pouvez pas les joindre à `products` sans comparer
des chaînes de caractères, et ces chaînes sont saisies pendant le service,
dans la langue du restaurant.

**`reminderId` est opaque.** Aucune collection de cette API ne le résout.
Transportez-le s'il vous aide à grouper des enregistrements ; n'espérez pas
pouvoir le consulter.

## `freezing`

La même forme, pour un produit descendu jusqu'à la congélation plutôt qu'à la
température du réfrigérateur. Champ pour champ, c'est `cooling`.

| Champ | Type | Signification |
| --- | --- | --- |
| `timestamp` | string, **obligatoire** | L'horodatage propre à l'enregistrement. |
| `product` | string, **obligatoire** | Ce qui a été congelé. |
| `beginDate` | string, **obligatoire** | Quand l'opération a commencé. |
| `beginTemperature` | number, **obligatoire** | La température de départ. |
| `endDate` | string \| null | Quand elle s'est terminée, ou `null`. |
| `endTemperature` | number \| null | La température d'arrivée, ou `null`. |
| `reminderId` | string \| null | Comme ci-dessus. |
| `userId` | string \| null | Qui l'a enregistrée. |

Rien dans l'enregistrement ne dit laquelle des trois c'est : c'est la
collection d'où il vient, et le champ `collection` de l'enveloppe la reprend.
Un pipeline qui fusionne les trois dans une seule table doit conserver cette
colonne.

## `reheating`

Un produit froid remonté. Encore la même forme, lue dans l'autre sens :
`beginTemperature` est la basse et `endTemperature` la haute.

| Champ | Type | Signification |
| --- | --- | --- |
| `timestamp` | string, **obligatoire** | L'horodatage propre à l'enregistrement. |
| `product` | string, **obligatoire** | Ce qui a été remis en température. |
| `beginDate` | string, **obligatoire** | Quand il est entré. |
| `beginTemperature` | number, **obligatoire** | La température de départ. |
| `endDate` | string \| null | Quand il est sorti, ou `null`. |
| `endTemperature` | number \| null | La température atteinte, ou `null`. |
| `reminderId` | string \| null | Comme ci-dessus. |
| `userId` | string \| null | Qui l'a enregistré. |

Une opération sans fin ressemble à ceci :

```json
{
  "id": "b4c8e21d-3a76-4f58-9c02-77ad5e1b8f30",
  "collection": "reheating",
  "deleted": false,
  "capturedAt": "1789783221907",
  "receivedAt": "1789783402115",
  "sequence": "2",
  "data": {
    "timestamp": "1789783200000",
    "product": "Gratin dauphinois",
    "beginDate": "1789783200000",
    "beginTemperature": 4.1,
    "endDate": null,
    "endTemperature": null,
    "userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
  }
}
```

## `transport`

La même transition, dans l'espace plutôt que dans le temps : d'où le produit
est parti et où il est arrivé, avec une température à chaque extrémité.

| Champ | Type | Signification |
| --- | --- | --- |
| `timestamp` | string, **obligatoire** | L'horodatage propre à l'enregistrement. |
| `product` | string, **obligatoire** | Ce qui a été transporté, sous forme de texte. |
| `departureLocation` | string, **obligatoire** | D'où il est parti, sous forme de texte. |
| `departureTime` | string, **obligatoire** | Quand il est parti, en millisecondes depuis l'epoch sous forme de chaîne de caractères. |
| `departureTemperature` | number, **obligatoire** | La température au départ. |
| `arrivalLocation` | string \| null | Où il est arrivé. `null` tant qu'il ne l'est pas. |
| `arrivalTime` | string \| null | Quand il est arrivé, ou `null`. |
| `arrivalTemperature` | number \| null | La température à l'arrivée, ou `null`. |
| `reminderId` | string \| null | Comme ci-dessus. |
| `userId` | string \| null | Qui l'a enregistré. |

```json
{
  "id": "f31b6a08-5c47-4de9-b1a5-6e0c9d247b82",
  "collection": "transport",
  "deleted": false,
  "capturedAt": "1789786944610",
  "receivedAt": "1789787101883",
  "sequence": "3",
  "data": {
    "timestamp": "1789785000000",
    "product": "Plats préparés, service traiteur",
    "departureLocation": "Cuisine centrale",
    "departureTime": "1789785000000",
    "departureTemperature": 3.4,
    "arrivalLocation": "Salle Montparnasse",
    "arrivalTime": "1789786800000",
    "arrivalTemperature": 5.9,
    "userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
  }
}
```

Les deux lieux sont du texte libre, ni des [zones](https://developers.backresto.com/fr/docs/facilities) ni des
adresses. C'est ce que la personne a saisi, et le côté arrivée est exactement
aussi facultatif que la température d'arrivée : un enregistrement peut ne
porter qu'un départ et rien d'autre.

## Travailler avec ces enregistrements

**Une fin à `null`, c'est deux choses différentes.** `endDate` et
`endTemperature` (ainsi qu'`arrivalTime`, `arrivalTemperature` et
`arrivalLocation` sur `transport`) acceptent `null`, et l'enregistrement ne
vous dit pas pourquoi. Ce peut être une opération encore en cours au moment où
vous la lisez, ou une opération que personne n'a jamais close. Les deux sont
identiques.

Cela compte dès l'instant où vous calculez une durée. Les dates sont des
chaînes de caractères : `endDate - beginDate` se soustrait très bien sur un
enregistrement clos et, sur un enregistrement ouvert, convertit `null` en zéro
et vous rend un grand nombre négatif plutôt qu'une erreur. Rien ne lève
d'exception ; votre moyenne est simplement fausse. Filtrez d'abord sur les
enregistrements clos, comptez les ouverts à part, et décidez vous-même à partir
de quel âge un enregistrement ouvert cesse d'être en cours pour devenir
abandonné : rien dans cette API ne tranche à votre place.

**Calculez sur `beginDate` et `endDate`, pas sur `timestamp`.** Les dates sont
l'horloge de l'opération. `timestamp` est celle de l'enregistrement, et ce
n'est pas avec elle qu'on fabrique une durée.

**Faites vos rapports sur `capturedAt`, synchronisez sur `receivedAt`.** Une
tablette dans une chambre froide téléverse quand elle trouve du réseau : un
cycle commencé à 09:00 peut arriver à 14:00.
[La page de l'enveloppe](https://developers.backresto.com/fr/docs/collections) raconte toute l'histoire.

**L'absence ne prouve rien.** Un produit sans enregistrement de refroidissement
peut n'avoir jamais été refroidi, ou dormir sur un appareil qui n'a pas encore
téléversé. Ne transformez pas un décompte en chiffre de conformité. Voyez
[la mise en garde](https://developers.backresto.com/fr/docs/support).
