Reinigung
Der Reinigungsplan, die tatsächlich erledigten Aufgaben und die Fotos, die es belegen — drei Collections, und was ein fehlender Datensatz nicht beweist.
Reinigung ist in der App ein Plan und sein Beleg. Das Restaurant schreibt auf, was gereinigt werden muss, wo und wie oft; die Mitarbeiter haken die Aufgaben ab, während sie sie erledigen; manche dieser Aufgaben verlangen ein Foto. Drei Collections, eine pro Ebene, und die Verbindung zwischen ihnen ist der Teil mit der meisten Arbeit.
| Collection | Berechtigung | Was ein Datensatz ist |
|---|---|---|
cleaning-tasks | cleaning-tasks:read | Eine Zeile des Plans: was gereinigt werden muss, wo, wie oft, ob es ein Foto braucht. |
cleaning-task-records | cleaning-task-records:read | Eine tatsächlich erledigte Aufgabe — wann und von wem. |
cleaning-task-pictures | cleaning-task-pictures:read | Das Foto, das es belegt. Trägt eine Datei. |
Alle drei teilen sich den Snapshot-Umschlag: id,
deleted, capturedAt, receivedAt, sequence und der Rest stehen neben dem
data, das unten beschrieben ist.
cleaning-tasks
Der Plan selbst. Er ändert sich selten — ein Restaurant schreibt seinen Plan einmal und bearbeitet ihn, wenn sich die Küche ändert —, das ist also die Collection, die Sie am seltensten durchlaufen und am längsten zwischenspeichern.
| Feld | Typ | Bedeutung |
|---|---|---|
index | integer, Pflicht | Die Position der Aufgabe im Plan, so wie die App sie ordnet. |
name | string, Pflicht | Was gereinigt werden muss, in den Worten des Restaurants. |
description | string | null | Längere Anweisungen, sofern jemand welche geschrieben hat. |
areaId | string | null | Der Bereich, zu dem sie gehört. null bei einem Restaurant, das sich nicht in Bereiche aufteilt. |
recurrence | integer, Pflicht | Wie oft die Aufgabe wiederkehrt, als schlichte ganze Zahl. |
requirePhotoProof | boolean | null | true, wenn die Aufgabe ein Foto verlangt. null heißt, dass die App es nie gesetzt hat. |
isDeleted | boolean, Pflicht | Die eigene weiche Löschung der App. Lesen Sie die Anmerkung unten. |
{
"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 trägt keine Einheit. Es ist eine ganze Zahl, und der Datensatz
sagt nicht, was sie zählt. Zeigen Sie sie so an, wie die App sie anzeigt, statt
aus einer Vermutung „alle 1 Tage“ zu machen.
isDeleted ist nicht das deleted des Umschlags. Eine Aufgabe, die das
Restaurant aus seinem Plan entfernt hat, kommt mit isDeleted: true und
deleted: false zurück: Sie ist weiterhin ein lebender Datensatz und beschreibt
eine Zeile, die nicht mehr gilt. Filtern Sie auf beides und behalten Sie die
gelöschten — alte Datensätze zeigen weiterhin auf sie.
cleaning-task-records
Eine erledigte Aufgabe. Absichtlich klein: welche Aufgabe, wann, wer.
| Feld | Typ | Bedeutung |
|---|---|---|
timestamp | string, Pflicht | Wann die Aufgabe erledigt wurde, als Zeichenkette mit Millisekunden seit der Epoche. |
cleaningTaskId | string, Pflicht | Der cleaning-tasks-Datensatz, den sie erfüllt. |
userId | string | null | Der Mitarbeiter, der sie erledigt hat, sofern die App einen erfasst hat. |
{
"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 ist Pflicht, jeder Datensatz hat also eine Aufgabe. Umgekehrt
gilt das nicht: Eine Aufgabe kann wochenlang ohne einen einzigen Datensatz im
Plan stehen, und das ist der normale Zustand einer wöchentlichen Aufgabe an
einem Dienstag.
cleaning-task-pictures
Das Foto. Es trägt eine Datei, und damit ist es die eine Collection hier, die Sie nicht allein über den Datensatz-Endpunkt nutzen können.
| Feld | Typ | Bedeutung |
|---|---|---|
timestamp | string, Pflicht | Wann das Foto aufgenommen wurde, als Zeichenkette mit Millisekunden seit der Epoche. |
cleaningTaskRecordId | string | null | Der cleaning-task-records-Datensatz, den es belegt. Darf null sein — siehe unten. |
part | number | null | Nicht dokumentiert, und eine Dezimalzahl statt eines Zählers. Führen Sie es unverändert mit, statt es zu deuten — derselbe Vorbehalt wie bei den Rückverfolgbarkeitsetiketten. |
groupId | string | null | Bindet diese Fotos zusammen. Datensätze mit derselben groupId gehören zum selben Beleg. |
asset | object, Pflicht | Benennt die Datei: objectKey und status immer, dazu contentType, byteLength und sha256, sobald sie hochgeladen ist. |
{
"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…"
}
}
}
Die Bytes sind ein zweiter Aufruf, gegen die eigene Kennung des Datensatzes:
GET /v1/restaurants/{restaurantId}/collections/cleaning-task-pictures/records/{recordId}/assets
Er antwortet mit einer Liste signierter URLs, jede fünfzehn Minuten gültig,
jede mit ihrer eigenen Autorisierung, sodass der Download keinen Header braucht.
Holen Sie die Bytes jetzt, speichern Sie die Bytes statt des Links, und rufen
Sie den Endpunkt erneut auf, wenn Sie eine frische URL brauchen. Die Regeln —
was erscheint, wozu sha256 da ist, warum es sich lohnt, contentType zu lesen
— stehen auf der Collections-Seite, und hier gelten
dieselben.
asset.status sagt Ihnen, ob es Bytes zu holen gibt. Es ist pending oder
uploaded. Ein Asset mit pending ist ein Foto, das das Gerät angekündigt und
noch nicht fertig gesendet hat: Der Datensatz ist echt, die Datei nicht, und der
Assets-Endpunkt antwortet ohne sie. Lesen Sie den Status, bevor Sie eine leere
Asset-Liste als ein Foto behandeln, das nie aufgenommen wurde.
cleaningTaskRecordId darf null sein. Ein Foto kann existieren, ohne auf
einen Datensatz zu zeigen; ein Join, der es immer erwartet, lässt Fotos unter
den Tisch fallen. Zählen Sie sie getrennt, statt sie stillschweigend zu
verwerfen.
Die drei verbinden
Die Struktur, die Sie fast sicher wollen, ist eine Zeile pro erledigter Aufgabe, mit ihrem Planeintrag und ihren Fotos:
cleaning-task-records.cleaningTaskId→cleaning-tasks.idcleaning-task-pictures.cleaningTaskRecordId→cleaning-task-records.id
Drei Durchläufe, auf Ihrer Seite verbunden. Es gibt keinen Endpunkt, der das für Sie tut, und es gibt keinen Filter nach Aufgabe oder nach Datum — Sie durchlaufen jede Collection ganz und gleichen ab, so wie es die Seite zum Umschlag beschreibt.
Ordnen Sie nach capturedAt, nicht nach receivedAt: Ein Tablet in einem
Kühlraum lädt hoch, sobald es Empfang findet, und ein Foto von 09:00 Uhr kann um
14:00 Uhr ankommen — nach dem Datensatz, zu dem es gehört, oder davor.
Was ein fehlender Datensatz nicht heißt
Ein Planeintrag ohne passenden Datensatz ist kein Beweis, dass die Aufgabe
ausgefallen ist. Sie kann auf einem Gerät erledigt worden sein, das noch nicht
hochgeladen hat, oder erledigt worden sein, bevor die Erfassung für dieses
Restaurant eingeschaltet wurde. Dasselbe gilt für eine Aufgabe mit
requirePhotoProof: true ohne ein Foto dazu: Das Foto kann noch im Upload sein,
und ein Asset, dessen Upload nicht abgeschlossen ist, fehlt, statt kaputt zu
sein.
Drucken Sie also aus dieser API keine Erfüllungsquote für die Reinigung und nennen Sie sie Compliance. Ehrlich sagen können Sie, was angekommen ist und wann — lesen Sie die Anmerkung zur Vollständigkeit, bevor Sie irgendjemandem eine Zahl vorlegen.