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 | Âmbito | O que é um registo |
|---|---|---|
cleaning-tasks | cleaning-tasks:read | Uma linha do plano: o que tem de ser limpo, onde, com que frequência, se exige uma fotografia. |
cleaning-task-records | cleaning-task-records:read | Uma tarefa efetivamente feita — quando, e por quem. |
cleaning-task-pictures | cleaning-task-pictures:read | A 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.
| Campo | Tipo | Significado |
|---|---|---|
index | integer, obrigatório | A posição da tarefa no plano, tal como a aplicação a ordena. |
name | string, obrigatório | O que tem de ser limpo, nas palavras do restaurante. |
description | string | null | Instruções mais longas, quando alguém as escreveu. |
areaId | string | null | A zona a que pertence. null num restaurante que não se divide em zonas. |
recurrence | integer, obrigatório | Com que frequência a tarefa volta, como um simples número inteiro. |
requirePhotoProof | boolean | null | true quando a tarefa exige uma fotografia. null significa que a aplicação nunca o definiu. |
isDeleted | boolean, obrigatório | A 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.
| Campo | Tipo | Significado |
|---|---|---|
timestamp | string, obrigatório | Quando a tarefa foi feita, em milissegundos desde a época Unix, em forma de cadeia de caracteres. |
cleaningTaskId | string, obrigatório | O registo de cleaning-tasks que ela cumpre. |
userId | string | null | O 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.
| Campo | Tipo | Significado |
|---|---|---|
timestamp | string, obrigatório | Quando a fotografia foi tirada, em milissegundos desde a época Unix, em forma de cadeia de caracteres. |
cleaningTaskRecordId | string | null | O registo de cleaning-task-records que ela prova. Admite null — veja abaixo. |
part | number | null | Nã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. |
groupId | string | null | Liga essas fotografias entre si. Os registos que partilham um groupId pertencem à mesma prova. |
asset | object, obrigatório | Identifica 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.cleaningTaskId→cleaning-tasks.idcleaning-task-pictures.cleaningTaskRecordId→cleaning-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.