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.

CollectionBerechtigungWas ein Datensatz ist
cleaning-taskscleaning-tasks:readEine Zeile des Plans: was gereinigt werden muss, wo, wie oft, ob es ein Foto braucht.
cleaning-task-recordscleaning-task-records:readEine tatsächlich erledigte Aufgabe — wann und von wem.
cleaning-task-picturescleaning-task-pictures:readDas 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.

FeldTypBedeutung
indexinteger, PflichtDie Position der Aufgabe im Plan, so wie die App sie ordnet.
namestring, PflichtWas gereinigt werden muss, in den Worten des Restaurants.
descriptionstring | nullLängere Anweisungen, sofern jemand welche geschrieben hat.
areaIdstring | nullDer Bereich, zu dem sie gehört. null bei einem Restaurant, das sich nicht in Bereiche aufteilt.
recurrenceinteger, PflichtWie oft die Aufgabe wiederkehrt, als schlichte ganze Zahl.
requirePhotoProofboolean | nulltrue, wenn die Aufgabe ein Foto verlangt. null heißt, dass die App es nie gesetzt hat.
isDeletedboolean, PflichtDie 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.

FeldTypBedeutung
timestampstring, PflichtWann die Aufgabe erledigt wurde, als Zeichenkette mit Millisekunden seit der Epoche.
cleaningTaskIdstring, PflichtDer cleaning-tasks-Datensatz, den sie erfüllt.
userIdstring | nullDer 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.

FeldTypBedeutung
timestampstring, PflichtWann das Foto aufgenommen wurde, als Zeichenkette mit Millisekunden seit der Epoche.
cleaningTaskRecordIdstring | nullDer cleaning-task-records-Datensatz, den es belegt. Darf null sein — siehe unten.
partnumber | nullNicht 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.
groupIdstring | nullBindet diese Fotos zusammen. Datensätze mit derselben groupId gehören zum selben Beleg.
assetobject, PflichtBenennt 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.cleaningTaskIdcleaning-tasks.id
  • cleaning-task-pictures.cleaningTaskRecordIdcleaning-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.

Zuletzt aktualisiert am 2026-09-20.