Das Onlineshop-System gibt es, weil ein lokaler Verkäufer, der seinen Laden über einen Chatverlauf führt, irgendwann eine Bestellung verliert. Was den Verlauf ersetzt, ist bewusst unspektakulär: ein Katalog mit Suche und Kategoriefilter, eine Produktseite, die den Bestand vor dem Tippen zeigt, ein Warenkorb, bei dem nie unklar ist, zu welchem Laden er gehört, und eine Bestellung, die fünf benannte Zustände durchläuft — mit einer Benachrichtigung bei jedem Übergang.
Die Produktseite: Varianten, Bestand und Rabatt, vor dem Tippen
Jedes Produkt trägt Fotos, eine Beschreibung, den Bestand und einen Preis, der reduziert sein kann — der Aktionspreis steht neben dem durchgestrichenen Original mit einem Prozentabzeichen, damit die Höhe des Nachlasses lesbar ist statt angedeutet. Ist etwas ausverkauft, steht das über dem Foto und wird nicht erst an der Kasse entdeckt. Das folgt einer Regel, auf der die ganze App steht: Was verändert, wohin ein Tipp führt, muss vor dem Tipp sichtbar sein.
Varianten entstehen aus Attributen, statt einzeln eingetragen zu werden. Der Verkäufer erklärt die Achsen — Größe, Farbe, woran das Produkt sich eben unterscheidet — und die Kombinationen werden Varianten mit je eigenem Bestand. Das hält den üblichen Fehler aus den Daten: drei getrennt eingetippte Anzeigen für dasselbe Hemd, über die sich nie gemeinsam berichten lässt, und ein Bestand, der für eine davon stimmt.
Der Warenkorb gehört einem Laden
Der Warenkorb führt einen Laden. Wer etwas von einem anderen Verkäufer hinzufügt, wird vor dem Leeren gefragt, statt still einen Korb über vier Betriebe zu bauen. Das ist eine gestalterische Weigerung und keine fehlende Funktion: Ein Korb über mehrere Verkäufer verspricht eine Kasse, eine Versandberechnung und eine Stelle, die verantwortlich ist, wenn etwas nicht ankommt — und nichts davon existiert, wenn dahinter vier unabhängige Betriebe mit vier Lieferwegen stehen.
Ein Laden je Warenkorb, mit Absicht. Der Wechsel des Ladens fragt, ob der Korb geleert werden darf. Ein Korb über mehrere Verkäufer bräuchte eine gemeinsame Kasse, um ehrlich zu sein, und eine marktplatzweite Kasse gibt es hier nicht — jede Bestellung läuft zwischen einer Käuferin und genau einem Betrieb.
Die Versandgebühr ist kein Feld, das die App schickt. Sie wird beim Aufgeben der Bestellung serverseitig aus dem Datensatz des Ladens gelesen und gilt nur bei Lieferung.
Kasse: Lieferung oder Abholung, und eine Gebühr, die der Client nicht setzt
Die Kasse fragt, was eine Bestellung wirklich braucht: Lieferung oder Abholung, Name, Telefon, Adresse, eine Notiz und die Zahlungsart. Abholung entfernt die Versandgebühr ganz, statt sie als Null zu zeigen, denn das sind zwei verschiedene Zustände und eine Null liest sich wie ein Fehler. Adressen kommen aus einem Adressbuch mit Standardeintrag — und es ist ein Adressbuch, geteilt mit dem Profil, weil eine Profiladresse und eine Lieferadresse dieselbe Tatsache sind und zwei Kopien am Ende garantiert auseinanderlaufen.
Die Gebühr selbst liegt auf dem Server. place_shop_order liest businesses.delivery_fee aus der Datenbank und ignoriert, was der Client übermittelt hat — genau wie der Weg im Lokal jede Zeile selbst bepreist. Das ist keine Richtlinie, die man in den Einstellungen abschalten könnte, sondern schlicht die Stelle, von der die Zahl kommt.
Fünf Bestellzustände, Sendungsnummer, beide Seiten informiert
Eine Bestellung läuft durch offen, bestätigt, in Verpackung, versandt und zugestellt, dazu storniert und abgelehnt als Endpunkte. Die Käuferin sieht eine Fortschrittsleiste statt eines Statusworts für sich allein, sodass „in Verpackung“ als Position in einer Folge lesbar ist, auf die noch etwas folgt. Beim Versand kommen eine Sendungsnummer und ein Versanddienst dazu, gewählt aus einer von der Verwaltung gepflegten Liste, die für jeden Dienst eine Tracking-URL-Vorlage mitbringt — die Nummer, die der Laden eintippt, wird so zu einem Link, den die Käuferin öffnen kann. Eine Sendungsnummer ohne Versanddienst lässt sich nicht anhängen, denn eine Nummer, von der niemand weiß, zu wem sie gehört, kann nie ein Link werden.
Beide Enden verlangen einen Grund. Wer storniert, muss sagen warum, und wer ablehnt, ebenso; der Grund reist mit der Bestellung, statt eine Nachricht zu sein, die im Chat weggescrollt wird. Jeder Übergang benachrichtigt beide Seiten, denn ein Bestellablauf ohne Benachrichtigungen lässt Läden Bestellungen verpassen und Käufer raten.
- offen — aufgegeben, wartet auf den Laden
- bestätigt — angenommen, der Laden hat zugesagt
- in Verpackung — wird vorbereitet
- versandt — unterwegs, mit Sendungsnummer und Versanddienst
- zugestellt — abgeschlossen, und die Käuferin darf bewerten
Die Seite des Verkäufers: Kasse, Onlinebestellungen und Bestand
Ein Handelsbetrieb bekommt drei Kacheln: eine Kasse für den Verkauf vor Ort, eine Konsole für Onlinebestellungen und Produkte samt Bestand mit Warnungen bei knappen Beständen. Die Übersicht trägt Live-Zähler im Sekundentakt, eine neue Bestellung meldet sich also selbst, statt gefunden werden zu müssen. Verkauf im Laden und Verkauf über die App schreiben in dasselbe Journal.
Weil beide Kanäle an einer Stelle landen, decken die Berichte den ganzen Betrieb statt nur den Onlineanteil: Umsatz, Anzahl der Bestellungen, durchschnittlicher Bon, die Aufteilung auf Bar, PromptPay und Sonstiges, die fünf meistverkauften Positionen, ein Umsatzdiagramm über sieben Tage und die Zeiträume heute, sieben Tage, dreißig Tage oder gesamt. Belege lassen sich erneut drucken oder als teilbarer Text bzw. Steuerbeleg erzeugen. Auch die Kundenliste entsteht aus diesem Journal — mit Notizen und Tags je Kunde, Kaufhistorie und Punkten, die zu einer vom Betrieb in der eigenen Währung festgelegten Rate anfallen, gegen einen einstellbaren Mindestumsatz samt Prämie.
Der Zugriff des Teams läuft über eine Rechtematrix mit Rollenvorlagen und Ausnahmen je Person, sodass eine Aushilfe Produkte und Bestellungen bekommen kann, aber weder Lohn noch Berichte. Dieselbe Matrix existiert in der Datenbank als has_capability(business_id, capability) und wird für die Row-Level-Security-Regeln serverseitig aufgelöst — der Server glaubt dem Client nie, welche Rolle jemand hat.
Zwei benachbarte Register folgen derselben Bestandslogik. Was ein Betrieb besitzt, ohne es zu verkaufen — Werkzeug, Maschinen, Mobiliar — steht im Anlagenregister, und was er gegen Kaution ausleiht, läuft über die Vermietung.
Produktbewertungen nur von echten Käufern
Produktbewertungen sind von der Betriebsbewertung getrennt, und beide sind gleich geschützt: Schreiben darf nur, wessen Geschäft die Plattform belegen kann. Die Berechtigung ist eine Datenbanksicht, und Kaufen zählt neben Essen im Lokal, Lieferung, Übernachten und Servicenutzung zu den anerkannten Arten. Alle anderen sehen eine Schlosskarte, die erklärt, warum es keinen Schreibknopf gibt, statt eines Knopfes, der scheitert.
Die Sternebewertung ist keine gespeicherte Zahl, die jemand bearbeitet. Ein Datenbank-Trigger berechnet sie aus den Bewertungszeilen neu — derselbe Mechanismus, den die Seite Vertrauen ausführlich beschreibt —, sodass die Bewertung auf der Ladenkarte und die Bewertungen auf der Seite nicht auseinanderlaufen können. Eine Käuferin kann ihre letzte Bewertung ändern oder löschen oder beim nächsten Kauf eine neue schreiben, während die alte bleibt; der Laden kann öffentlich antworten.