Stockly anbinden
Stockly ist darauf gebaut, von außen gesteuert zu werden. Ein Versandmodul, ein ERP, dem die Gewichte gehören, ein Desktop-Etikettenwerkzeug, ein Programm, das einen für den Server unsichtbaren Drucker bedient, ein Logistikdienstleister mit eigener Kartonisierung, ein System, das Ware auf Regale bucht. Keines davon braucht eine Änderung in Stockly, um damit zu arbeiten.
Es gibt vier Wege hinein. Den richtigen zu wählen bestimmt die Gestalt der ganzen Integration.
Vier Wege hinein
Abschnitt betitelt „Vier Wege hinein“| Methode | Verwenden, wenn | Läuft |
|---|---|---|
| Benannte Events | Ihr Code in derselben Shopware-Installation lebt | im Prozess |
| HTTP-Endpunkte | Ihr Code anderswo lebt | über die Admin-API |
| DAL-Entitäten | Sie Stammdaten schieben oder für einen Bericht lesen | über die Admin-API oder im Prozess |
| Erweiterungspunkte | Sie eine Fähigkeit ergänzen: einen Dienstleister, eine Aufteilungsstrategie | im Prozess |
Wo zu beginnen ist
Abschnitt betitelt „Wo zu beginnen ist“| Ihre Aufgabe | Beginnen bei |
|---|---|
| Ware in einen Lagerplatz legen, herausnehmen, bewegen | Bestand in einen Lagerplatz legen |
| fragen, was in den Regalen liegt | Bestand lesen |
| zählen, ein Lager befüllen, eines in ein anderes entleeren | Korrekturen und Massenaufträge |
| benachrichtigt werden, wenn sich etwas ändert | Events |
| etwas anstelle von Stockly entscheiden | Policy Hooks |
| Sie besitzen die Bestandszahlen bereits anderswo | Externe Änderungen |
| versenden, packen, drucken | diese Seite und Packing API |
Was auch immer die Aufgabe ist, beginnen Sie mit dem Bestandsmodell: Fast jeder Integrationsfehler in der Geschichte dieses Moduls war ein Missverständnis darüber, welche Zahl aus welcher abgeleitet ist.
Benannte Events
Abschnitt betitelt „Benannte Events“Über Zeichenketten benannt, sodass ein Listener nie eine Klasse von uns importieren muss, und das ist der Zweck dieser Benennung: Ein Modul, das kaputtgeht, wenn Stockly fehlt, ist eine Abhängigkeit, keine Integration.
| Event | Bedeutung |
|---|---|
p2lab.stockly.parcels.plan | fragt nach einem Vorschlag; schreibt nichts |
p2lab.stockly.parcels.commit | speichert eine Aufteilung |
p2lab.stockly.parcel.tracking | meldet eine Sendungsnummer für ein Paket |
p2lab.stockly.parcel.registered | ein Paket hat eine Sendungsnummer erhalten |
p2lab.stockly.parcel.failed | eine Buchung ist gescheitert, mit dem Grund |
p2lab.stockly.label.print | übergibt ein fertiges Etikett an die Druckwarteschlange; antwortet mit einer jobId |
p2lab.stockly.label.preferred_format | fragt, welches Format beim Dienstleister zu bestellen ist |
p2lab.stockly.print.job.printed | ein Auftrag wurde gedruckt; Argumente jobId, printerId |
p2lab.stockly.print.job.failed | ein Auftrag ist gescheitert; Argumente jobId, error |
Die letzten beiden sind Meldungen und keine Fragen: Sie tragen ihre Daten wie der Rest dieser Liste in den arguments des Events, es wird aber nichts daraus zurückgelesen.
Tatsachen, die das Modul im Nachhinein veröffentlicht, sind andersherum benannt, mit einem Unterstrich nach dem
Anbieter (p2lab_stockly.…), denn sie sind Shopware-Business-Events und keine Hooks, die zu beantworten sind. Sie
umspannen Bedarf, Rückstände, Ware, Bestand, Arbeit, Versand, Retouren und Integrität und haben einen
eigenen Katalog.
Die meisten dieser Tatsachen sind auch im Flow Builder verfügbar, ein Händler kann also eine Mail, ein Tag oder einen Statuswechsel daran hängen, ohne dass jemand Code schreibt.
HTTP-Endpunkte
Abschnitt betitelt „HTTP-Endpunkte“Alle unter /api/_action/p2lab-stockly/, wie jeder Admin-API-Aufruf authentifiziert und durch dieselben
ACL-Berechtigungen begrenzt wie die Bildschirme.
Versand — shipping/…
| Methode | Pfad | Was er tut |
|---|---|---|
POST | shipping/parcels/plan | ein Vorschlag, schreibt nichts |
POST | shipping/parcels/commit | speichert eine Aufteilung |
POST | shipping/parcels/request | fragt den Dienstleister nach Sendungen |
POST | shipping/parcels/packed | meldet, dass der Packbildschirm fertig ist |
POST | shipping/parcels/of-order | die gespeicherten Pakete einer Bestellung |
POST | shipping/parcels/{parcelId}/tracking | meldet eine Sendungsnummer |
POST | shipping/parcels/{parcelId}/cancel | storniert ein Paket |
POST | shipping/parcels/close | für diese Bestellung geht nichts mehr hinaus |
GET | shipping/profiles | die installierten Dienstleister und Aufteilungsstrategien |
GET | shipping/batches/eligible | Pakete, die auf die Übergabe an einen Fahrer warten |
POST | shipping/batches/close | schließt eine Übergabe ab; liefert eine Job-Id |
GET | shipping/batches/{batchId}/list | das Blatt, das ein Fahrer unterschreibt, als PDF |
Verpackungsdaten — packaging/…
| Methode | Pfad | Was er tut |
|---|---|---|
GET | packaging/resolve | was das System für diese Artikel verwenden wird, samt Herkunft |
GET | packaging/coverage | wie vollständig der Katalog ist und was zuerst zu berichtigen ist |
GET | packaging/collisions | Artikel, die zwei Regeln gleichen Rangs beide beanspruchen |
GET | packaging/variance | wo Vorhersage und Waage auseinandergehen |
Druck — printing/…
| Methode | Pfad | Was er tut |
|---|---|---|
POST | print-job | nimmt ein fertiges Etikett von außerhalb Shopware entgegen; liefert eine jobId |
POST | printing/print-jobs/next | gibt einem Platz seinen nächsten Auftrag; null, wenn es keinen gibt |
POST | printing/print-jobs/{jobId}/ack | bestätigt einen Druck oder meldet einen Fehlschlag |
POST | printing/jobs/{jobId}/retry | wiederholt einen Auftrag |
POST | printing/jobs/{jobId}/cancel | gibt einen Auftrag auf |
GET | printing/station-devices | ob diese Installation browsergesteuerte Drucker hat |
GET | printing/profiles | die bekannten Druckertypen und ihre Formate |
DAL-Entitäten
Abschnitt betitelt „DAL-Entitäten“Gewöhnliche Shopware-Entitäten, /api/search/… und /api/… funktionieren also wie überall sonst.
| Entität | Hält |
|---|---|
p2lab_stockly_parcel | ein Paket: Gewicht, Sendungsnummer, Platz, Karton, Notizen |
p2lab_stockly_parcel_line | was im Paket ist |
p2lab_stockly_parcel_batch | eine Übergabe an einen Fahrer |
p2lab_stockly_packaging_material | den Kartonkatalog |
p2lab_stockly_packaging_profile | die Verpackungsregeln in großer Zahl |
p2lab_stockly_product_packaging | Überschreibungen je Artikel |
p2lab_stockly_packaging_rule | eine Regel je Versandart |
p2lab_stockly_printer | ein Gerät: wie es sich anbindet, sein Format, seine Etikettengröße |
p2lab_stockly_print_job | einen Druckauftrag: Nutzdaten, Status, Versuche |
p2lab_stockly_print_document_rule | welches Shopware-Dokument abgefangen wird und wohin es geht |
p2lab_stockly_print_rule | welche Arbeit was druckt, für wen und mit welcher Priorität |
p2lab_stockly_printer_default | das Gerät, auf das ein Platz, ein Mitarbeiter oder ein Lager voreingestellt ist |
p2lab_stockly_print_media | die Materialarten, mit denen ein Gerät bestückt sein kann |
Die Lagerentitäten — Regale, Lagerplätze, Bewegungen, Lots, Ladungsträger — sind im Bestandsmodell aufgeführt. Lesen Sie sie frei; schreiben Sie sie über die Endpunkte.
Erweiterungspunkte
Abschnitt betitelt „Erweiterungspunkte“Zu implementierende Schnittstellen, über ein Service-Tag registriert.
| Schnittstelle | Ergänzt |
|---|---|
ParcelSplitStrategyInterface | eine Art, Ware auf Pakete zu legen |
CarrierProfileInterface | einen Dienstleister: seine Grenzen, Produkte und Prüfungen |
CarrierManifestGatewayInterface | einem Dienstleister mitzuteilen, dass eine Übergabe abgeschlossen ist |
ExtractionEngineInterface | eine Lese-Engine für den Dokumentimport |
Die Druckwarteschlange ist ein eigener Weg, getrennt vom Versand, und sie nimmt drei verschiedene Arten von Programmen an. Die Bildschirme behandelt das Druckkapitel; was folgt, ist das, was der Code wissen muss.
Ein Etikett aus einem Plugin derselben Installation
Abschnitt betitelt „Ein Etikett aus einem Plugin derselben Installation“p2lab.stockly.label.print nimmt ein fertiges Etikett entgegen. Die Daten reisen in den arguments des
Events, nicht in seinem Subject, denn die Antwort kommt auf demselben Weg zurück:
$event = new GenericEvent(null, [ 'payload' => $labelBytes, 'format' => 'zpl', 'source' => 'AcmeCarrier', 'externalId' => $parcelNumber, 'orderId' => $orderId, 'context' => $context,]);
$dispatcher->dispatch($event, 'p2lab.stockly.label.print');
// Von der Warteschlange zurückgeschrieben — für einen Nachweis oder um den Auftrag später abzubrechen.$jobId = $event->getArgument('jobId');Vor der Bestellung des Etiketts beim Dienstleister ist p2lab.stockly.label.preferred_format zu fragen, welches
Format zu bestellen ist: Argumente role, format (die eigene Voreinstellung des Aufrufers) und context,
Antwort unter format. Es ist das Format des Geräts, an das der Auftrag geleitet würde, ein Drucker, der ZPL
verwendet, bekommt also ZPL statt eines unterwegs gerasterten PDF.
Das Event lässt nie eine Exception entkommen. Der Aufrufer ist mitten dabei, eine Sendung bei einem Dienstleister zu erzeugen, wo bereits eine Paketnummer vergeben wurde; ein unerreichbarer Drucker darf aus einem Druckproblem kein Versandproblem machen.
Ein Etikett aus einem Programm außerhalb Shopware
Abschnitt betitelt „Ein Etikett aus einem Programm außerhalb Shopware“POST /api/_action/p2lab-stockly/print-job tut genau das, was das Event tut, über die API:
{ "source": "acme-desktop", "externalId": "1Z999AA10123456784", "format": "zpl", "payload": "<base64>", "copies": 1, "orderId": "0189…", "printerId": "0189…" }Die Antwort ist {"jobId": "…"}. Eine mediaId darf für payload einstehen. Ohne eine printerId leitet die
Warteschlange den Auftrag selbst, genau wie bei einem Etikett aus dem Versand.
Ein Programm, das den Druck übernimmt
Abschnitt betitelt „Ein Programm, das den Druck übernimmt“Ein über den lokalen Agenten angebundener oder vom Browser gesteuerter Drucker ist vom Server nicht erreichbar, der Platz fragt also stattdessen nach Arbeit:
POST /api/_action/p2lab-stockly/printing/print-jobs/nextAuthorization: Bearer <token>Content-Type: application/json
{"stationId": "0189…", "drivers": ["agent"]}Die Antwort trägt job und counts. job ist oft null, und das ist der Normalzustand: Ein Packplatz hat
öfter nichts zu drucken als etwas. Ein Auftrag trägt format (zpl, epl, pdf), copies, driver, so viel
über das Gerät, wie es braucht, um es wiederzufinden (host, deviceUid, usbVendorId, usbProductId), und
payload base64-kodiert: dekodieren und die Bytes unverändert durchschreiben.
Danach:
POST /api/_action/p2lab-stockly/printing/print-jobs/<jobId>/ackContent-Type: application/json
{"printed": true}{"printed": false, "error": "Printer offline"}Drei Dinge kosten hier die meiste Zeit:
Das Abholen ist das Beanspruchen. Ohne ein ack sitzt der Auftrag auf Wird gesendet, bis das Zeitlimit
aus den Druckeinstellungen ihn freigibt, standardmäßig fünf Minuten.
Ein gemeldeter Fehlschlag ist endgültig. Niemand kann einen an einen Platz gebundenen Auftrag übernehmen, die Warteschlange wiederholt ihn also nicht von selbst; das Wiederholen ist eine Entscheidung für einen Menschen oder für das aufrufende Programm.
source zusammen mit externalId ist der Idempotenzschlüssel. Dasselbe Paar zweimal anzunehmen liefert die
bestehende jobId zurück statt eines zweiten Etiketts, ein Wiederholen nach einem Zeitlimit ist also sicher. Der
Aufrufer merkt den Unterschied nicht, und das ist beabsichtigt.
Berechtigungen: p2lab_stockly_print_job:create, um ein Etikett zu übergeben, p2lab_stockly_print_job:update
für die Agentenschleife, p2lab_stockly_printer:read für printing/station-devices.
Drei durchgehend geltende Regeln
Abschnitt betitelt „Drei durchgehend geltende Regeln“Null ist nicht null. Überall in den Verpackungsdaten heißt eine fehlende Zahl „die nächste Ebene fragen”, und
eine Null ist eine Behauptung. Eine 0 als Verpackungsgewicht zu schieben besagt, der Artikel werde unverpackt
versandt, und nichts weiter unten berichtigt das.
Die Herkunft reist mit den Zahlen. packaging/resolve nennt nicht nur, was ein Wert ist, sondern woher
er kam: product, parent, profile, global oder none. Integrationen, die das letzte davon ihren eigenen
Nutzern zeigen, finden schlechte Daten in Tagen statt in Quartalen.
Das Bestandsjournal wird beschrieben, nie korrigiert. Eine Bewegungszeile wird nie bearbeitet; eine Korrektur ist eine neue Zeile mit eigenem Grund. Alles, was das Lager darüber festhält, warum ein Lagerplatz vier hält, besteht aus Zeilen, und deshalb darf nichts eine davon stillschweigend ändern.
- Das Bestandsmodell — woran eine Menge hängt
- Bestand in einen Lagerplatz legen — der Schreibweg ins Lager
- Events — der vollständige Katalog
- Packing API im Detail — die Formen von Anfrage und Antwort
- Druckwarteschlange — was aus dem übergebenen Druckauftrag wird
- Beispiele — durchgerechnete Integrationen, von Anfang bis Ende