Leistungen
Warum Qavlar Tech Stack Projekte Über uns
Produkte
Blog Kontakt
AUTOMATISIERUNG · SHOPIFY · LIVE IN PRODUKTION

Kassen-Dashboard: ein Kassensystem mit Shopify synchronisieren

Für einen Kosmetik-Einzelhändler habe ich den Bestandsabgleich zwischen Ladenkasse und Online-Shop automatisiert. Interessant ist weniger die Feature-Liste als das, was passierte, als das System auf den echten Katalog und den echten PC der Inhaberin traf.

~285Echte SKUs im Katalog
MinutenVom PDF zum Shopify-Bestand
2Erlaubte Adressen, kein Passwort
0Secrets auf dem Kunden-PC
Kassen-Dashboard
Die Übersicht: letzter Lauf, geänderte Produkte, Bestandswarnungen und PDF-Upload. Produktnamen und SKUs sind unkenntlich gemacht.

Zwei Systeme, die nicht miteinander sprachen

Ein Kosmetik-Einzelhändler betrieb bcassa, ein deutsches Kassensystem im Laden, und Shopify als Online-Shop. Jeder Verkauf im Laden veränderte den Bestand nur in bcassa. Um das in Shopify abzubilden, musste die Inhaberin einen PDF-Bestandsexport prüfen und Produkte von Hand aktualisieren.

Das vorhandene Tooling machte es schlimmer: Ein Skript las dieses PDF, indem es riet, welche Zahl welche Spalte ist. Enthielt ein Lieferantenname zufällig Ziffern, wurden Produktnamen stillschweigend beschädigt.

Was ich gebaut habe

Ein durchgängiges Sync-System und dazu ein Auslieferungsweg, der für eine nicht-technische Inhaberin gebaut ist statt für Entwickler.

Ein echter PDF-ParserDie Spaltenerkennung nutzt die tatsächlichen x/y-Koordinaten jedes Wortes und rekonstruiert die Spalten aus dem Layout der Kopfzeile. Unabhängig vom Inhalt korrekt, per Regressionstest gegen das echte Exportformat abgesichert.
Kostenbewusster Shopify-SyncBei ca. 285 SKUs, von denen sich täglich nur wenige bewegen, überspringt ein Cache die Shopify-API für alles Unveränderte. Vor jedem echten Schreibzugriff wird der Live-Bestand geholt und Shopifys Compare-and-Set genutzt, damit ein gleichzeitiger Online-Verkauf nie überschrieben wird.
Dashboard im Look des KundenDeutschsprachig und im Branding der Inhaberin: letzte Läufe, geänderte Produkte, Bestandswarnungen, Nachfragespitzen sowie Best- und Worst-Seller aus Shopifys Bestelldaten.
Login ohne PasswortEin einmaliger Link per E-Mail, auf zwei Adressen beschränkt, mit Rate-Limit und langlebigen Sessions.
Selbstinstallierender Windows-ClientStatt Google Drive (ein Security-Review, das keine Seite hätte bestehen können) beobachtet ein Hintergrundprogramm einen Ordner auf ihrem PC. Gebaut per CI ohne Windows-Rechner, Zugangsdaten beim Build aus einem GitHub-Actions-Secret, legt den Ordner selbst an und startet beim Login automatisch.
Ein Deployment ohne FehlerquelleEin Skript: git pull, Docker-Rebuild, Neustart, idempotente Datenbankmigration. Jede Schemaänderung rollt automatisch aus, nie manuelles SQL.

Was wirklich kaputtging

Das Besondere an diesem Projekt ist nicht die Feature-Liste, sondern das, was auftauchte, als es auf den echten Katalog und den echten PC traf, und wie jedes Problem eingekreist wurde.

Ein 504 beim ersten Upload des kompletten Katalogs. Kein Codefehler: Der Reverse-Proxy des VPS brach einen legitim langen Sync per Timeout ab. Zuerst musste ich feststellen, dass der Server gar kein Standard-nginx-Layout nutzt. Er läuft über aaPanel, und die echte Konfiguration liegt in einem vom Panel generierten Proxy-Verzeichnis, das ein grep über /etc/nginx nie gefunden hätte. Erst dann ließ sich der Fix anwenden: ein gezielter Timeout-Override in einer Datei, die die Panel-Oberfläche nicht überschreibt.

Drei Vertragsbrüche der Shopify Admin API in Folge. Ein Feld, das laut Doku richtig aussah (ignoreCompareQuantity), existierte am Input-Typ gar nicht. Das Feld, das es ersetzte (changeFromQuantity), ist eine verpflichtende Compare-and-Set-Vorbedingung, keine optionale Metainformation. Danach lehnte die Mutation jede Anfrage ab, weil eine neu vorgeschriebene @idempotent-Direktive fehlte. Jedes Problem diagnostizierte ich aus dem rohen GraphQL-Fehlerpayload und Shopifys Changelog. Der Fix für das zweite ist selbst defensives Design: Der Sync holt den Live-Bestand direkt vor dem Schreiben, statt einem lokalen Cache zu vertrauen, und schützt so nebenbei vor dem Überschreiben eines Verkaufs, der dazwischen passiert ist.

Ein Cache, der ein Produkt für immer scheitern lassen konnte. Ein Produkt wurde in Shopify gelöscht und neu angelegt, ein Routinevorgang, und bekommt dabei eine neue interne ID. Der Sync hatte keinen Weg zurück von „die gecachte ID existiert nicht mehr“, außer bei jedem künftigen Lauf für diese SKU zu scheitern. Jetzt heilt er sich selbst: Ein „nicht gefunden“ leert gezielt den veralteten Cache und fällt auf eine frische Suche per SKU zurück. Eine falsche ID wird so nicht zur dauerhaften, stillen Lücke im Katalog.

Erfolg melden für einen Schreibvorgang, der nie stattfand. Ein fehlgeschlagener Shopify-Write speicherte kurzzeitig trotzdem den beabsichtigten neuen Bestand lokal, bevor der Write bestätigt war. Ein vorübergehender Fehler konnte so dazu führen, dass der Cache-Check im nächsten Lauf einen falschen Treffer sah, den Wiederholungsversuch still übersprang und „Erfolg“ meldete für einen Bestand, den Shopify nie erhielt. Behoben: Lokaler Zustand wird nur noch nach einem bestätigten Remote-Write geschrieben.

Windows SmartScreen auf dem Rechner einer nicht-technischen Nutzerin. Bei der ersten Übergabe der .exe traf sie die Reibung, die ein unsigniertes Binary erwarten lässt: Sie sah einen blockierenden Sicherheitsdialog, las ihn als „der Computer lässt mich nicht“ und schloss ihn. Den Dialog blind zu diagnostizieren, anhand einer zweizeiligen Nachricht auf Ungarisch und ohne das Binary selbst ausführen zu können, hieß: die Anleitung in ihrer Sprache und für ihren konkreten Bildschirm zu schreiben, kein generisches Troubleshooting-Dokument.

Kassen-Dashboard
Protokoll der letzten Aktualisierungen mit Status, geänderten und übersprungenen Produkten.

Tech-Stack

FastAPIPostgreSQLSQLAlchemyDocker ComposeShopify Admin GraphQL APIMailjetPyInstallerGitHub Actionsnginx / aaPanelVanilla HTML, CSS, JS

Wo es gelandet ist

Es läuft unbeaufsichtigt in Produktion: Die Inhaberin legt ein PDF in einen Ordner, der Bestand in Shopify ist innerhalb von Minuten aktuell, und jeder Fehler, nicht nur die, die ihr auffallen, meldet sich automatisch per E-Mail beim Entwickler.

Die Teile, die beim Spezifizieren simpel aussahen, waren in der Praxis vor allem eine Frage der Teile, die nicht simpel sind: eine Proxy-Schicht, die keiner von uns von Hand konfiguriert hatte, eine API, die ihren Vertrag mitten im Bau dreimal änderte, und eine Software, der jemand vertrauen können muss, der nie einen Stacktrace lesen wird.

Zwei Systeme, die nicht miteinander reden?

Erzähl mir in einem kostenlosen 30-minütigen Gespräch, wo bei dir noch von Hand übertragen wird.

Ein Projekt von Qavlar.tech, entwickelt in Saarbrücken.