Limpeza

O plano de limpeza, as tarefas efetivamente feitas e as fotografias que o provam — três coleções, e o que um registo em falta não prova.

A limpeza, na aplicação, é um plano e a sua prova. O restaurante escreve o que tem de ser limpo, onde e com que frequência; o pessoal vai marcando as tarefas à medida que as faz; algumas dessas tarefas exigem uma fotografia. Três coleções, uma por camada, e a junção entre elas é onde está quase todo o trabalho.

ColeçãoÂmbitoO que é um registo
cleaning-taskscleaning-tasks:readUma linha do plano: o que tem de ser limpo, onde, com que frequência, se exige uma fotografia.
cleaning-task-recordscleaning-task-records:readUma tarefa efetivamente feita — quando, e por quem.
cleaning-task-picturescleaning-task-pictures:readA fotografia que o prova. Traz um ficheiro.

As três partilham o envelope de instantâneo: id, deleted, capturedAt, receivedAt, sequence e os restantes ficam ao lado do data descrito abaixo.

cleaning-tasks

O plano em si. Estes mudam raramente — um restaurante escreve o seu plano uma vez e edita-o quando a cozinha muda — por isso é a coleção que percorre com menos frequência e que mantém em cache durante mais tempo.

CampoTipoSignificado
indexinteger, obrigatórioA posição da tarefa no plano, tal como a aplicação a ordena.
namestring, obrigatórioO que tem de ser limpo, nas palavras do restaurante.
descriptionstring | nullInstruções mais longas, quando alguém as escreveu.
areaIdstring | nullA zona a que pertence. null num restaurante que não se divide em zonas.
recurrenceinteger, obrigatórioCom que frequência a tarefa volta, como um simples número inteiro.
requirePhotoProofboolean | nulltrue quando a tarefa exige uma fotografia. null significa que a aplicação nunca o definiu.
isDeletedboolean, obrigatórioA eliminação lógica da própria aplicação. Leia a nota abaixo.
{
  "id": "c9a4f7e2-1b83-4d05-a6f1-90e3b7c4d218",
  "collection": "cleaning-tasks",
  "deleted": false,
  "capturedAt": "1789601204773",
  "receivedAt": "1789601399042",
  "sequence": "1",
  "data": {
    "index": 3,
    "name": "Nettoyage de la trancheuse",
    "description": "Démonter la lame, dégraisser, désinfecter.",
    "areaId": "7d2c0b61-4e8a-49f3-9c27-5a1b8e60d3f4",
    "recurrence": 1,
    "requirePhotoProof": true,
    "isDeleted": false
  }
}

recurrence não traz unidade nenhuma. É um número inteiro e o registo não diz o que é que ele conta. Mostre-o como a aplicação o mostra, em vez de apresentar «de 1 em 1 dias» a partir de um palpite.

isDeleted não é o deleted do envelope. Uma tarefa que o restaurante retirou do seu plano volta com isDeleted: true e deleted: false: continua a ser um registo vivo, que descreve uma linha que já não se aplica. Filtre pelos dois, e guarde as eliminadas — os registos antigos ainda apontam para elas.

cleaning-task-records

Uma tarefa feita. Pequeno de propósito: que tarefa, quando, quem.

CampoTipoSignificado
timestampstring, obrigatórioQuando a tarefa foi feita, em milissegundos desde a época Unix, em forma de cadeia de caracteres.
cleaningTaskIdstring, obrigatórioO registo de cleaning-tasks que ela cumpre.
userIdstring | nullO funcionário que a fez, quando a aplicação registou um.
{
  "id": "5f8b3d16-2c90-4a77-b4e8-31d0a9c65f7b",
  "collection": "cleaning-task-records",
  "deleted": false,
  "capturedAt": "1789775012558",
  "receivedAt": "1789775204910",
  "sequence": "2",
  "data": {
    "timestamp": "1789774800000",
    "cleaningTaskId": "c9a4f7e2-1b83-4d05-a6f1-90e3b7c4d218",
    "userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
  }
}

cleaningTaskId é obrigatório, por isso todos os registos têm uma tarefa. O contrário não se verifica: uma tarefa pode estar no plano sem qualquer registo contra ela durante semanas, e esse é o estado normal de uma tarefa semanal numa terça-feira.

cleaning-task-pictures

A fotografia. Traz um ficheiro, o que faz dela a única coleção desta página que não consegue consumir só a partir do endpoint dos registos.

CampoTipoSignificado
timestampstring, obrigatórioQuando a fotografia foi tirada, em milissegundos desde a época Unix, em forma de cadeia de caracteres.
cleaningTaskRecordIdstring | nullO registo de cleaning-task-records que ela prova. Admite null — veja abaixo.
partnumber | nullNão documentado, e um decimal em vez de um contador. Leve-o consigo em vez de o interpretar — a mesma ressalva que nas etiquetas de rastreabilidade.
groupIdstring | nullLiga essas fotografias entre si. Os registos que partilham um groupId pertencem à mesma prova.
assetobject, obrigatórioIdentifica o ficheiro: objectKey e status sempre, mais contentType, byteLength e sha256 assim que ele estiver carregado.
{
  "id": "2d71c8a9-6e34-4b0f-8517-ac93e25d0b46",
  "collection": "cleaning-task-pictures",
  "deleted": false,
  "capturedAt": "1789775118330",
  "receivedAt": "1789775301774",
  "sequence": "3",
  "data": {
    "timestamp": "1789774860000",
    "cleaningTaskRecordId": "5f8b3d16-2c90-4a77-b4e8-31d0a9c65f7b",
    "part": 1,
    "groupId": "a03e5c88-71bd-4f92-b6d4-2e8f10c73a5b",
    "asset": {
      "objectKey": "cleaning-task-pictures/2d71c8a9/1.jpg",
      "status": "uploaded",
      "contentType": "image/jpeg",
      "byteLength": 812043,
      "sha256": "9f2a…"
    }
  }
}

Os bytes são uma segunda chamada, contra o identificador do próprio registo:

GET /v1/restaurants/{restaurantId}/collections/cleaning-task-pictures/records/{recordId}/assets

Responde com uma lista de URL assinados, cada um válido durante quinze minutos, cada um a levar a sua própria autorização, por isso a transferência não precisa de qualquer cabeçalho. Vá buscar os bytes agora, guarde os bytes e não a ligação, e volte a chamar o endpoint quando precisar de um URL novo. As regras — o que aparece, para que serve o sha256, porque vale a pena ler o contentType — estão na página das coleções, e aqui são as mesmas.

O asset.status diz-lhe se há bytes para ir buscar. É pending ou uploaded. Um asset a pending é uma fotografia que o dispositivo anunciou e ainda não acabou de enviar: o registo é real, o ficheiro não é, e o endpoint dos ficheiros responde sem ele. Leia o estado antes de tratar uma lista de ficheiros vazia como uma fotografia que nunca foi tirada.

cleaningTaskRecordId admite null. Uma fotografia pode existir sem apontar para um registo, por isso uma junção que assuma que ele está sempre lá deixa fotografias pelo caminho. Conte-as à parte em vez de as descartar em silêncio.

Unir as três

O formato que quase de certeza quer é uma linha por tarefa feita, com a sua entrada no plano e as suas fotografias:

  • cleaning-task-records.cleaningTaskIdcleaning-tasks.id
  • cleaning-task-pictures.cleaningTaskRecordIdcleaning-task-records.id

Três percursos, unidos do seu lado. Não há endpoint que o faça por si, e não há filtro por tarefa nem por data — percorre cada coleção inteira e reconcilia, como descreve a página do envelope.

Ordene por capturedAt, não por receivedAt: um tablet numa câmara frigorífica carrega quando encontra sinal, e uma fotografia tirada às 09:00 pode chegar às 14:00 — depois do registo a que pertence, ou antes dele.

O que um registo em falta não significa

Uma entrada do plano sem registo correspondente não é prova de que a tarefa foi saltada. Pode ter sido feita num dispositivo que ainda não carregou os dados, ou feita antes de a captura ter sido ativada para esse restaurante. O mesmo vale para uma tarefa marcada com requirePhotoProof: true sem fotografia contra ela: a fotografia pode estar a meio do carregamento, e um ficheiro que não acabou de carregar está ausente, não avariado.

Por isso, não produza uma taxa de execução da limpeza a partir desta API e lhe chame conformidade. O que pode dizer com honestidade é o que chegou, e quando — veja a nota sobre a completude antes de pôr um número à frente de quem quer que seja.

Última atualização em 2026-09-20.