Pulizie

Il piano di pulizia, i compiti effettivamente svolti e le fotografie che lo dimostrano — tre collezioni, e cosa non prova un record mancante.

La pulizia nell'app è un piano e la sua prova. Il ristorante mette per iscritto cosa deve essere pulito, dove e con quale frequenza; il personale spunta i compiti man mano che li svolge; alcuni di quei compiti richiedono una fotografia. Tre collezioni, una per livello, e la join fra loro è dove sta la maggior parte del lavoro.

CollezioneAmbitoChe cos'è un record
cleaning-taskscleaning-tasks:readUna riga del piano: cosa deve essere pulito, dove, ogni quanto, se serve una fotografia.
cleaning-task-recordscleaning-task-records:readUn compito effettivamente svolto — quando, e da chi.
cleaning-task-picturescleaning-task-pictures:readLa fotografia che lo dimostra. Porta con sé un file.

Tutte e tre condividono l'involucro di istantanea: id, deleted, capturedAt, receivedAt, sequence e gli altri stanno accanto al data descritto qui sotto.

cleaning-tasks

Il piano stesso. Cambia di rado — un ristorante scrive il suo piano una volta e lo modifica quando cambia la cucina — quindi è la collezione che percorrerà meno spesso e che terrà in cache più a lungo.

CampoTipoSignificato
indexintero, obbligatorioLa posizione del compito nel piano, nell'ordine in cui li dispone l'app.
namestringa, obbligatorioCosa deve essere pulito, nelle parole del ristorante.
descriptionstringa | nullIstruzioni più estese, quando qualcuno le ha scritte.
areaIdstringa | nullL'area a cui appartiene. null in un ristorante che non si suddivide in aree.
recurrenceintero, obbligatorioOgni quanto il compito si ripresenta, come semplice numero intero.
requirePhotoProofbooleano | nulltrue quando il compito richiede una fotografia. null significa che l'app non l'ha mai impostato.
isDeletedbooleano, obbligatorioLa cancellazione logica propria dell'app. Legga la nota qui sotto.
{
  "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 non porta alcuna unità. È un intero e il record non dice cosa conti. Lo mostri come lo mostra l'app anziché rendere «ogni 1 giorni» a partire da una supposizione.

isDeleted non è il deleted dell'involucro. Un compito che il ristorante ha rimosso dal suo piano torna con isDeleted: true e deleted: false: è ancora un record vivo, che descrive una riga che non si applica più. Filtri su entrambi, e conservi quelli cancellati — i vecchi record li indicano ancora.

cleaning-task-records

Un compito svolto. Piccolo di proposito: quale compito, quando, chi.

CampoTipoSignificato
timestampstringa, obbligatorioQuando il compito è stato svolto, millisecondi dall'epoch come stringa.
cleaningTaskIdstringa, obbligatorioIl record cleaning-tasks che soddisfa.
userIdstringa | nullIl membro del personale che l'ha svolto, quando l'app ne ha registrato uno.
{
  "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 è obbligatorio, quindi ogni record ha un compito. Il contrario non vale: un compito può restare nel piano senza alcun record a suo carico per settimane, ed è lo stato normale di un compito settimanale di martedì.

cleaning-task-pictures

La fotografia. Porta con sé un file, il che ne fa l'unica collezione di questa pagina che non può consumare dal solo endpoint dei record.

CampoTipoSignificato
timestampstringa, obbligatorioQuando è stata scattata l'immagine, millisecondi dall'epoch come stringa.
cleaningTaskRecordIdstringa | nullIl record cleaning-task-records che dimostra. Ammette null — veda qui sotto.
partnumero | nullNon documentato, ed è un decimale anziché un contatore. Lo porti avanti così com'è anziché interpretarlo — la stessa avvertenza delle etichette di tracciabilità.
groupIdstringa | nullLega insieme quelle immagini. I record che condividono un groupId appartengono alla stessa prova.
assetoggetto, obbligatorioIndica il file: sempre objectKey e status, più contentType, byteLength e sha256 una volta caricato.
{
  "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…"
    }
  }
}

I byte sono una seconda chiamata, sull'id del record stesso:

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

Risponde con un elenco di URL firmati, ciascuno valido per quindici minuti, ciascuno con la propria autorizzazione, così il download non richiede alcun header. Scarichi i byte adesso, conservi i byte anziché il collegamento, e richiami l'endpoint quando le serve un URL nuovo. Le regole — cosa compare, a cosa serve sha256, perché vale la pena leggere contentType — sono nella pagina sulle collezioni, e qui sono le stesse.

asset.status le dice se ci sono byte da scaricare. È pending oppure uploaded. Un asset pending è una fotografia che il dispositivo ha annunciato e non ha ancora finito di inviare: il record è reale, il file no, e l'endpoint degli asset risponde senza di esso. Legga lo stato prima di trattare un elenco di asset vuoto come una fotografia che non è mai stata scattata.

cleaningTaskRecordId ammette null. Un'immagine può esistere senza puntare a un record, quindi una join che dà per scontato che ci sia sempre butta via delle fotografie. Le conti a parte anziché scartarle in silenzio.

Unire le tre

La forma che quasi sicuramente le serve è una riga per compito svolto, con la sua voce di piano e le sue fotografie:

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

Tre percorsi, uniti dalla sua parte. Non esiste un endpoint che lo faccia per lei, e non esiste alcun filtro per compito o per data — percorre ogni collezione per intero e riconcilia, come descrive la pagina sull'involucro.

Ordini su capturedAt, non su receivedAt: un tablet in una cella frigorifera carica i dati quando trova segnale, e un'immagine scattata alle 09:00 può arrivare alle 14:00 — dopo il record a cui appartiene, oppure prima.

Cosa non significa un record mancante

Una voce di piano senza un record corrispondente non è la prova che il compito sia stato saltato. Può essere stato svolto su un dispositivo che non ha ancora caricato i dati, oppure svolto prima che l'acquisizione venisse attivata per quel ristorante. Lo stesso vale per un compito contrassegnato requirePhotoProof: true senza alcuna immagine a suo carico: la fotografia può essere a metà del caricamento, e un asset che non ha finito di caricarsi è assente anziché rotto.

Quindi non stampi un tasso di completamento delle pulizie da questa API chiamandolo conformità. Ciò che può dire onestamente è cosa è arrivato, e quando — veda la nota sulla completezza prima di mettere un numero davanti a chiunque.

Ultimo aggiornamento 2026-09-20.