Enfriamiento y procesos alimentarios

Enfriamiento, congelación, recalentamiento y transporte: cuatro colecciones que registran una transición de temperatura, y qué aspecto tiene una sin terminar.

Un plato caliente que se enfría despacio pasa horas en el rango en que las bacterias se multiplican, así que la regla es bajarlo rápido y poder demostrar que se hizo. Cuatro colecciones registran ese tipo de transición. Tres son una transición en el tiempo —bajar, bajar más, volver a subir— y la cuarta es la misma idea en el espacio.

ColecciónPermisoQué es un registro
coolingcooling:readUn producto caliente bajado a temperatura de conservación en frío.
freezingfreezing:readUn producto bajado hasta la congelación.
reheatingreheating:readUn producto frío subido de nuevo a temperatura de servicio.
transporttransport:readUn producto trasladado de un sitio a otro, con la temperatura en cada extremo.

Las cuatro comparten el sobre de instantánea: id, deleted, capturedAt, receivedAt, sequence y los demás campos acompañan al data que se describe más abajo.

cooling

Un ciclo: un producto, cuándo empezó y a qué temperatura estaba, cuándo terminó y a qué temperatura acabó.

CampoTipoSignificado
timestampstring, obligatorioLa marca propia del registro, en milisegundos desde la época Unix como cadena. No es el reloj del ciclo: eso son las dos fechas de abajo.
productstring, obligatorioQué se enfrió, como texto.
beginDatestring, obligatorioCuándo empezó el ciclo, en milisegundos desde la época Unix como cadena.
beginTemperaturenumber, obligatorioLa temperatura a la que empezó.
endDatestring | nullCuándo terminó. null mientras no lo haya hecho.
endTemperaturenumber | nullLa temperatura a la que terminó. null junto con endDate.
reminderIdstring | nullEl recordatorio que la aplicación asoció al ciclo, cuando hubo uno.
userIdstring | nullEl miembro del personal que lo registró, cuando la aplicación registró uno.
{
  "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 es un nombre, no una referencia. No hay ningún productId en estos registros, así que no puede unirlos a products sin comparar cadenas, y las cadenas se teclean durante el servicio, en el idioma del restaurante.

reminderId es opaco. Ninguna colección de esta API lo resuelve. Llévelo consigo si le ayuda a agrupar registros; no espere poder consultarlo.

freezing

La misma forma, para un producto bajado a congelación en lugar de a temperatura de nevera. Campo a campo, es cooling.

CampoTipoSignificado
timestampstring, obligatorioLa marca propia del registro.
productstring, obligatorioQué se congeló.
beginDatestring, obligatorioCuándo empezó la operación.
beginTemperaturenumber, obligatorioLa temperatura a la que empezó.
endDatestring | nullCuándo terminó, o null.
endTemperaturenumber | nullLa temperatura a la que terminó, o null.
reminderIdstring | nullComo arriba.
userIdstring | nullQuién lo registró.

Nada en el registro dice cuál de las tres es: eso lo dice la colección de la que viene, y el campo collection del sobre lo refleja. Una canalización que fusione las tres en una sola tabla tiene que conservar esa columna.

reheating

Un producto frío que se vuelve a subir. De nuevo la misma forma, leída en el otro sentido: beginTemperature es la baja y endTemperature la alta.

CampoTipoSignificado
timestampstring, obligatorioLa marca propia del registro.
productstring, obligatorioQué se recalentó.
beginDatestring, obligatorioCuándo entró.
beginTemperaturenumber, obligatorioLa temperatura a la que empezó.
endDatestring | nullCuándo salió, o null.
endTemperaturenumber | nullLa temperatura que alcanzó, o null.
reminderIdstring | nullComo arriba.
userIdstring | nullQuién lo registró.

Una operación sin final tiene este aspecto:

{
  "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 misma transición, en el espacio en lugar de en el tiempo: de dónde salió el producto y adónde llegó, con una temperatura en cada extremo.

CampoTipoSignificado
timestampstring, obligatorioLa marca propia del registro.
productstring, obligatorioQué se transportó, como texto.
departureLocationstring, obligatorioDe dónde salió, como texto.
departureTimestring, obligatorioCuándo salió, en milisegundos desde la época Unix como cadena.
departureTemperaturenumber, obligatorioLa temperatura a la salida.
arrivalLocationstring | nullAdónde llegó. null mientras no lo haya hecho.
arrivalTimestring | nullCuándo llegó, o null.
arrivalTemperaturenumber | nullLa temperatura a la llegada, o null.
reminderIdstring | nullComo arriba.
userIdstring | nullQuién lo registró.
{
  "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"
  }
}

Los dos lugares son texto libre, no zonas ni direcciones. Son lo que tecleó la persona, y el extremo de llegada es exactamente tan opcional como la temperatura de llegada: un registro puede llevar una salida y nada más.

Trabajar con estas colecciones

Un final nulo son dos cosas distintas. endDate y endTemperature —y arrivalTime, arrivalTemperature y arrivalLocation en transport— admiten null, y el registro no le dice por qué. Puede ser una operación que sigue en curso mientras la lee, o una que nadie cerró nunca. Las dos son idénticas.

Eso importa en cuanto calcule una duración. Las fechas son cadenas, así que endDate - beginDate resta sin problema en un registro cerrado y, en uno abierto, convierte null en cero y le devuelve un número negativo enorme en lugar de un error. No salta ninguna excepción; su media simplemente está mal. Filtre primero los registros cerrados, cuente los abiertos aparte y decida usted a partir de qué antigüedad un registro abierto deja de estar en curso y pasa a estar abandonado: nada en esta API toma esa decisión por usted.

Calcule sobre beginDate y endDate, no sobre timestamp. Las fechas son el reloj de la operación. timestamp es el del propio registro, y no es la materia de la que se hace una duración.

Ordene por capturedAt, no por receivedAt. Una tableta en una cámara frigorífica sube los datos cuando encuentra cobertura, así que un ciclo empezado a las 09:00 puede llegar a las 14:00. La página del sobre cuenta la historia completa.

La ausencia no prueba nada. Un producto sin registro de enfriamiento puede que nunca se enfriara, o puede estar en un dispositivo que todavía no ha subido nada. No convierta un recuento de estos en una cifra de cumplimiento: consulte la nota sobre exhaustividad.

Última actualización: 2026-09-20.