Fryers

The fryers and their oil quality checks — two collections, and why the measured value can be missing while the decision never is.

Frying oil degrades as it is used, and past a point it has to be filtered or thrown away. The restaurant tests it, decides what to do, and records both. Two collections: the fryers, and the checks made on them.

CollectionScopeWhat a record is
fryer-equipmentfryer-equipment:readA fryer.
fryer-checksfryer-checks:readAn oil quality check on one of them, and what was decided.

Both share the snapshot envelope: id, deleted, capturedAt, receivedAt, sequence and the rest sit beside the data described below.

fryer-equipment

The fryers themselves, as the restaurant lists them. A short record — a name, a position in the list, and where it stands.

FieldTypeMeaning
indexinteger, requiredThe fryer's position in the list, as the app orders it.
namestring, requiredWhat the restaurant calls it.
areaIdstring | nullThe area it stands in. null on a restaurant that does not split itself into areas.
isDeletedboolean, requiredThe app's own soft delete. Read the note below.
{
  "id": "6b1d0f54-8e27-4c93-a7b0-52fd3e1c9a86",
  "collection": "fryer-equipment",
  "deleted": false,
  "capturedAt": "1789598411206",
  "receivedAt": "1789598603471",
  "sequence": "1",
  "data": {
    "index": 1,
    "name": "Friteuse gauche",
    "areaId": "7d2c0b61-4e8a-49f3-9c27-5a1b8e60d3f4",
    "isDeleted": false
  }
}

There are no thresholds here. Unlike equipment, a fryer carries no min and no max: nothing on the record says what a good oil reading is. Whether a check was acceptable is the restaurant's judgement, and the only trace of it is the action they took.

isDeleted is not the envelope's deleted. A fryer the restaurant removed comes back with isDeleted: true and deleted: false: still a live record, describing a fryer that is no longer in service. Filter on both, and keep the removed ones — old checks still point at them.

fryer-checks

One check on one fryer: what the test showed, and what was done about it.

FieldTypeMeaning
timestampstring, requiredWhen the check was made, epoch milliseconds as a string.
testValuestring | nullWhat the test showed, as text. null when nothing was measured.
actionstring, requiredWhat was decided about the oil — filtering it, changing it, leaving it as it is.
fryerEquipmentIdstring, requiredThe fryer that was checked.
userIdstring | nullThe staff member who made the check, when the app recorded one.
{
  "id": "e07a5c92-4f61-48b8-9d3a-b18c60e5427f",
  "collection": "fryer-checks",
  "deleted": false,
  "capturedAt": "1789777348215",
  "receivedAt": "1789777510964",
  "sequence": "2",
  "data": {
    "timestamp": "1789777200000",
    "testValue": "18",
    "action": "Filtration de l'huile",
    "fryerEquipmentId": "6b1d0f54-8e27-4c93-a7b0-52fd3e1c9a86",
    "userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
  }
}

testValue is a string, not a number. It is what the strip or the probe showed, recorded as text, and the record carries no unit and no scale — a restaurant using test strips and one using a polar-compound meter both land here. Keep it as text. Parsing it into a float across restaurants gives you a column whose values do not compare.

action is free text in the language of the restaurant. The schema does not constrain it, so read the value rather than matching it against a list of your own — and do not build an enum from what you see in one restaurant's data.

What a check without a value means

testValue is nullable and action is required, and that asymmetry is the thing to design around. A check can be recorded as an action taken with no measured value behind it: someone changed the oil because it looked wrong, because it was the end of the week, because the fryer was being cleaned anyway.

So a check is not a measurement. It is a decision, sometimes with a measurement attached. If you are building a table of oil readings over time, expect a good share of rows with nothing in the value column, and count them rather than dropping them — an action with no value is still a check that happened.

Working with these

Join on fryerEquipmentId. A check names its fryer and nothing else; the fryer's name is on fryer-equipment, which you walk separately. Two walks, joined on your side.

Order on capturedAt, not receivedAt. A tablet uploads when it finds signal, so a check made at 09:00 can arrive at 14:00. The envelope page has the full story.

Absence proves nothing. A fryer with no check this week may not have been checked, or may be sitting on a device that has not uploaded. Do not turn a count of these into a compliance figure — see the caveat.

Last updated 2026-09-20.