Bestellsoftware für Restaurants scheitert meist an zwei Stellen: dort, wo eine fremde Person auf einen Tisch bestellen kann, der ihr nicht gehört, und dort, wo die Summe auf dem Gästebildschirm der Summe auf der Kasse widerspricht. Korats Essensmodul ist von beidem rückwärts entworfen. Die Tischsitzung wird vom Personal erzeugt und trägt ein einmal gültiges Token; die Rechnung besteht aus denselben Zeilen, die die Kasse liest; und place_dining_order bepreist jede Position auf dem Server.
Der Tisch-QR ist ein einmalig gültiges Token, keine Tischnummer
Es gibt bewusst keinen Weg, eine Tischnummer einzutippen. Das wäre die naheliegende Funktion — und der Grund, warum sich so viele Systeme vom Parkplatz aus missbrauchen lassen: Ist das einzige Merkmal eine auf den Tisch gedruckte Zahl, kann jeder, der dort je gegessen hat, für immer auf jeden Tisch bestellen. Stattdessen öffnet das Personal den Tisch in der Konsole, und dieses Öffnen erzeugt ein frisches, einmal gültiges Token im gezeigten QR-Code. Ein abfotografierter Code, ein weitergeleiteter Screenshot oder der Ausdruck von letztem Dienstag trägt ein Token, das nicht mehr aktuell ist, und funktioniert nicht.
Wer an den Tisch tritt, ist weder Mitglied des Betriebs noch Gastgeber einer Sitzung, die es noch nicht gibt — unter Row-Level-Security kann diese Person die Tabelle dining_sessions also gar nicht lesen, weder die eigene Zeile noch irgendeine. RLS kann beurteilen, wer anfragt; nicht, ob eine Anfrage ein gültiges Token trägt. Der Scan läuft deshalb über join_session_by_token, eine SECURITY DEFINER-Funktion, die das Token serverseitig abgleicht, den ersten Scanner zum Gastgeber macht und bei Nichtübereinstimmung null zurückgibt, ohne zu sagen warum. Eine Fehlermeldung, die „falsches Token“ von „abgelaufenes Token“ unterscheidet, ist ein Rateorakel.
Ist man drin, lädt der Gastgeber den Rest des Tisches per Link oder Chatkarte ein — wer später kommt, tritt derselben Sitzung bei, und sein Essen landet auf derselben Rechnung.
Bestellen: Optionen, Notizen und eine Karte, die weiß, dass gleich Schluss ist
Die Karte ist nach Kategorien gegliedert, und jede Position kann Optionsgruppen tragen — der zusätzliche Espresso, die Schärfe, die Nudelsorte — mit Preisaufschlägen, Pflichtkennzeichen und einer Höchstzahl an Auswahlen. Der Aufschlag hängt an der Option und wird nicht in eine Notiz getippt, weshalb Küchenbon und Rechnungszeile über dasselbe reden. Positionen nehmen zusätzlich eine freie Notiz und eine Menge, und der Warenkorb bleibt bis zum Absenden änderbar.
Öffnungszeiten, letzte Bestellung und die Alterskontrolle beim Alkohol
Läden setzen Öffnungszeiten mit einem Vorlauf für die letzte Bestellung. Kurz davor warnt die Karte, dass die Küche schließt; danach wird das Bestellen blockiert, statt es anzunehmen und später still zu stornieren. Nicht verfügbare oder ausverkaufte Positionen sagen das auf der Karte, vor dem Tippen. Alkohol trägt ein eigenes Kennzeichen: eine Altersprüfung gegen das Geburtsdatum, und das Personal kann eine ganze Sitzung auf einmal altersfreigeben.
Die Bestätigungsschlange: nichts erreicht die Küche, bevor ein Mensch zustimmt
Abgeschickte Bestellungen gehen nicht direkt an den Pass. Sie landen auf der Küchenanzeige in einer Warteschlange zur Bestätigung, und das Personal bestätigt oder lehnt ab, bevor daraus Arbeit wird. Ein System, das ein Telefon direkt Bons in die Küche schieben lässt, hat einer fremden Person die Möglichkeit gegeben, dem Laden Ware zu kosten — und nimmt den Moment weg, in dem ein Mensch bemerkt, dass Tisch sechs versehentlich vier gleiche Menüs bestellt hat.
Von der Küchenanzeige aus schiebt das Personal Bestellungen durch ihre Zustände, und die Konsole zeigt Live-Zähler im Sekundentakt — offene Bestellungen, rufende Tische, wartende Reservierungen, neue Lieferungen — dazu ein rotes Band, sobald ein Tisch nach Personal ruft. Gäste haben die andere Hälfte: Personal rufen, Rechnung erbitten, Tisch verlassen.
Die laufende Einzelrechnung und das Teilen per PromptPay-QR
Die laufende Rechnung zeigt genau die Zeilen, die die Kasse liest, aktualisiert mit jeder bestätigten Position — keine Enthüllung am Ende, keine Rekonstruktion aus der Erinnerung. Schließt der Tisch, erzeugt die App eine Abschlussübersicht, samt Vorschlägen, die Mitessenden als Freunde hinzuzufügen, und später eine Erinnerung an die Bewertung. Belege bleiben in einer persönlichen Rechnungshistorie.
Rechnung teilen: Was schulde ich?
Die Teilungsansicht beantwortet die Frage, über die tatsächlich gestritten wird: Was schulde ich? Jede Person sieht ihre eigene À-la-carte-Summe plus einen gleichen Anteil an einem Buffetpaket am Tisch. Für den Betrag lässt sich ein PromptPay-QR erzeugen, im thailändischen Standardformat mit CRC-16-Prüfsumme.
Die geteilte Rechnung ist eine Aufschlüsselung, keine getrennte Zahlung. Sie berechnet, was jede Person schuldet; sie belastet nicht vier Karten mit vier Anteilen. Das Geld wird weiterhin zwischen den Leuten am Tisch und dem Lokal geregelt.
Und es gibt bewusst keinen Rückfallweg über eine eingetippte Tischnummer. Lässt sich der QR-Code eines Gastes nicht scannen, öffnet das Personal den Tisch neu in der Konsole. Ein manueller Weg gäbe genau das Loch zurück, für dessen Schließung das Token erzeugt wird.
Buffet: Pauschale pro Kopf, Timer und Reste-Abrechnung
Das Buffet hat ein eigenes Modell, statt als Menüposition mit Preis pro Kopf nachgebaut zu werden. Ein Paket hat einen Preis pro Kopf, einen Countdown mit vom Personal gewährten Verlängerungen und getrennte Kopfzahlen für Erwachsene und Kinder. Menüpositionen tragen eine Buffetrolle — enthalten, nur à la carte oder enthalten mit Aufschlag —, sodass eine Karte beide Gästearten bedient. Die Abrechnung übrig gelassener Speisen wird mit Mengeneinheiten unterstützt: die Regel, die in Thailand ohnehin auf jedem Buffettisch steht und die die meiste Software ignoriert.
Lieferung, Abholung und Tischreservierungen
Dieselbe Karte trägt Lieferung und Abholung. Eine Bestellung führt Liefergebühr, Anbieter, geplante Abholzeit und Adresse und durchläuft neu, in Zubereitung, unterwegs und erledigt, wobei beide Seiten bei jedem Übergang benachrichtigt werden. Tischreservierungen nehmen Datum, Uhrzeit, Personenzahl und eine Notiz und warten auf die Bestätigung des Lokals — die Zahl der offenen steht als Live-Zähler auf der Übersicht. Wer daneben abgepackte Ware verkauft, nutzt dafür das Shop-Modul, das auf derselben serverseitigen Preisregel steht.
Zwei häufige Abwandlungen. Ein Betrieb, der am Tresen bedient und nach Nummer aufruft, nimmt statt der Tische die Warteschlange. Und wer zuerst wissen will, was das Eröffnen eines Betriebs überhaupt verlangt, findet es vollständig auf der Seite für Betriebe.
Jeder Preis wird auf dem Server berechnet
Die Regel, die den Rest trägt: Der Client setzt nie einen Preis. place_dining_order und place_shop_order lesen Positionspreise, Optionsaufschläge und die Liefergebühr aus der Datenbank und ignorieren, was das Telefon gesendet hat. Ein Client-Feld, dem man traut, ist ein Rabatt zum Selberbedienen. Dasselbe Prinzip gilt für Bewertungen — schreiben darf nur, wessen Besuch oder Lieferung die Datenbank belegen kann, und die Sternebewertung wird von einem Trigger neu berechnet, nie eingetippt.