Neu JTL-Wawi 1.11.5: Sequenzieller Kundenexport mit Workflow und SQL-Queue

SHAAN

Sehr aktives Mitglied
26. August 2020
692
214
Hallo zusammen,

ich hoffe, ihr seid gut ins neue Jahr 2026 gestartet und wünsche euch ein gesundes und erfolgreiches neues Jahr.

Ich beschäftige mich aktuell intensiv mit der technischen Frage, wie sich in JTL-Wawi ein strikt sequenzieller Export umsetzen lässt, und stoße dabei an konzeptionelle Grenzen des Workflow-Systems.

Zielsetzung
  1. Alle 2 Minuten soll genau ein Kunde verarbeitet bzw. exportiert werden.
  2. Die Verarbeitung muss strikt aufsteigend erfolgen, z. B. nach tKunde.dErstellt.
  3. Es dürfen keine Doppel-Exporte auftreten, auch nicht bei Fehlern oder Wiederholungen.
  4. Der Ablauf soll deterministisch, wartbar und supportfähig sein.

Bekannte Rahmenbedingungen
  1. JTL-Workflows lassen sich nicht direkt per SQL triggern.
    Änderungen per INSERT oder UPDATE in der Datenbank lösen keinen Workflow aus.
  2. Direkte Updates auf dbo.tKunde sind bei uns per Trigger blockiert.
    Änderungen sind nur über Kunde.spKundeInsert, Kunde.spKundeUpdate und Kunde.spKundeDelete erlaubt.
  3. Lesen per DirectQuery innerhalb von Workflows funktioniert zuverlässig.
    Schreiben in andere Tabellen außerhalb von tKunde ist möglich.

Aktueller technischer Ansatz

Ich nutze einen zeitgesteuerten Workflow (alle 2 Minuten), der seinen Zustand vollständig aus der Datenbank liest.
Der Verarbeitungszustand wird in einer kundenspezifischen Tabelle gespeichert, konkret in:

[Kunde].[tKundeEigenesFeld]
Beispiel zum Lesen des Zustands:

SQL:
SELECT cWertVarchar
FROM [Kunde].[tKundeEigenesFeld]
WHERE kKunde = 1936
AND kAttribut = 315;

Der Wert enthält einen Cursor in folgendem Format:

CURSOR=2025-01-01 12:34:56.789|K=123456

Dabei dient dErstellt als primärer Cursor und kKunde als Tie-Breaker bei identischem Zeitstempel.

Der Workflow führt dann folgende Schritte aus:
  1. Cursor aus der Tabelle lesen
  2. Nächsten Kunden bestimmen
SQL:
SELECT TOP 1 kKunde, dErstellt
FROM dbo.tKunde
WHERE
dErstellt > @lastDate
OR (dErstellt = @lastDate AND kKunde > @lastK)
ORDER BY dErstellt ASC, kKunde ASC;

  1. Genau diesen Kunden exportieren
  2. Cursor nach erfolgreichem Export fortschreiben

SQL:
UPDATE [Kunde].[tKundeEigenesFeld]
SET cWertVarchar = 'CURSOR=2025-01-01 12:36:00.000|K=123789'
WHERE kKunde = 1936
AND kAttribut = 315;



Damit erreiche ich aktuell:
  1. Exakt einen Kunden pro Workflow-Lauf
  2. Eine stabile, deterministische Reihenfolge
  3. Keine direkten Änderungen an dbo.tKunde

Technisch funktioniert das zuverlässig, fühlt sich aber eher wie ein Workaround als wie ein „offizieller“ Lösungsweg an.

Zentrale Frage

Gibt es in JTL-Wawi eine saubere, empfohlene Möglichkeit, Workflows datenbank- oder statusgetrieben auszulösen, oder ist ein zeitgesteuerter Workflow mit SQL-gesteuertem Zustand tatsächlich der vorgesehene Weg?

Konkret interessieren mich folgende Punkte:

  1. Gibt es eine Möglichkeit, Workflows indirekt per SQL oder DB-Zustand zu triggern.
  2. Gibt es offizielle oder etablierte Best Practices für streng sequenzielle Exporte.
  3. Setzt jemand produktiv eine Queue-Tabelle ein, die von einem Workflow abgearbeitet wird.
  4. Gibt es bekannte Fallstricke bei Cursor- oder Queue-basierten Ansätzen in JTL.

Alternative Überlegung

Statt eines Cursors wäre auch eine explizite Queue denkbar, zum Beispiel:


SQL:
CREATE TABLE tExportQueue (
kID INT IDENTITY PRIMARY KEY,
kKunde INT,
Status TINYINT,
dCreated DATETIME
);

Workflow-seitig dann:

SQL:
SELECT TOP 1 kKunde
FROM tExportQueue
WHERE Status = 0
ORDER BY dCreated;


Wird ein solches Muster in JTL-Umgebungen produktiv eingesetzt?


Zusammenfassung

Mir ist bewusst, dass JTL-Workflows nicht datenbank-event-getrieben sind. Ich suche daher keine Umgehung, sondern eine architektonisch saubere, langfristig wartbare Lösung für eine strikt sequenzielle Verarbeitung.
Erfahrungen, Hinweise oder offizielle Empfehlungen wären sehr hilfreich.
 

frankell

Sehr aktives Mitglied
9. September 2019
2.358
712
Flensburg
Hallo @SHAAN,

muss es denn zwingend in der Wawi mit Workflows ablaufen? Wenn nicht, mach es mit dem Server-Agent und einer Queue-Tabelle.

So richtig nachzuvollziehen ist es nicht, warum Du überhaupt Wawi-Workflows dazu nutzt (dazu fehlt aber auch die Begründung :D). Du bewegst Dich mit dem UPDATE doch eh in einem CustomWorkflow, also rein auf Datenbankebene. Das widerspricht irgendwie der Wartbarkeit innerhalb der Wawi. Oder lässt Du ein externes SQL-Skript sowohl durch den Workflow schreiben als auch starten?
 
  • Gefällt mir
Reaktionen: SHAAN

SHAAN

Sehr aktives Mitglied
26. August 2020
692
214
Das ist ein spannendes Thema, und du hast völlig recht. Ich bin einen anderen Weg gegangen, um die Aktualisierung mit dem CRM umzusetzen, und lasse mir den Export einfach per Script direkt aus der SQL-Datenbank erstellen, was gut funktioniert. Import in JTL ist kein Problem. Ich bin noch nicht ganz fertig, aber hier ein Beispiel. Das reicht für mich.

SQL:
SELECT
    -- Kunden
    k.kKunde                AS [CustomerId_Internal]
  , k.cKundenNr             AS [CustomerNumber_External]
  , k.nZahlungsziel         AS [PaymentTerms_Days]
  , k.cHRNr                 AS [CompanyRegisterNumber]

  -- Steuernummer: keine Leerzeichen
  , REPLACE(ISNULL(k.cSteuerNr, ''), ' ', '')              AS [TaxNumber]

  , k.cWWW                  AS [WebsiteUrl]
  , k.dErstellt             AS [CustomerCreatedAt]

    -- Eigene Felder (global pro Kunde)
  , ef.EF_247               AS [Custom_DeliveryInstructions]   -- formerly CF.Anlieferhinweis
  , ef.EF_315               AS [Custom_Notes]                  -- formerly Notes
  , ef.EF_326               AS [Custom_CC_Email]
  , ef.EF_328               AS [Custom_InvoiceEmail]
  , ef.EF_329               AS [Custom_CreditNoteEmail]
  , ef.EF_345               AS [Custom_Accessible]
  , ef.EF_347               AS [Custom_Floor]
  , ef.EF_386               AS [Custom_CostCenter]
  , ef.EF_567               AS [Custom_DunningEmail]

    -- Billing = Standard-Rechnungsadresse (nStandard=1 AND nTyp=1)
  , aBill.cFirma            AS [Billing_Company]
  , aBill.cAnrede           AS [Billing_Salutation]
  , aBill.cTitel            AS [Billing_Title]
  , aBill.cVorname          AS [Billing_FirstName]
  , aBill.cName             AS [Billing_LastName]

  , aBill.cStrasse          AS [Billing_Street1]
  , aBill.cPLZ              AS [Billing_PostalCode]
  , aBill.cOrt              AS [Billing_City]
  , aBill.cBundesland       AS [Billing_State]
  , aBill.cLand             AS [Billing_Country]

  -- Telefon / Mobil / Fax: nur Ziffern (keine Leerzeichen, keine Sonderzeichen)
  , REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        ISNULL(aBill.cTel,''), ' ', ''), '+',''), '-',''), '/',''), '(',''), ')',''), '.',''), ',',''), CHAR(9),''), CHAR(13),'')
        AS [Billing_Phone]

  , REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        ISNULL(aBill.cMobil,''), ' ', ''), '+',''), '-',''), '/',''), '(',''), ')',''), '.',''), ',',''), CHAR(9),''), CHAR(13),'')
        AS [Billing_Mobile]

  , aBill.cMail             AS [Billing_Email_Primary]

  , REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        ISNULL(aBill.cFax,''), ' ', ''), '+',''), '-',''), '/',''), '(',''), ')',''), '.',''), ',',''), CHAR(9),''), CHAR(13),'')
        AS [Billing_Fax]

  , aBill.cAdressZusatz     AS [Billing_Attention]
  , aBill.cZusatz           AS [Billing_Street2]

  -- USt-IdNr: keine Leerzeichen
  , REPLACE(ISNULL(aBill.cUSTID, ''), ' ', '')             AS [VATId]

    -- Kundengruppe / Zahlungsart
  , kg.cName                AS [CustomerGroup_Name]
  , za.cName                AS [PaymentMethod_Name]

    -- interne/abgeleitete Felder
  , CASE WHEN k.cSperre = 'Y' THEN 'True' ELSE 'False' END AS [Customer_IsBlocked]

  , CASE
        WHEN NULLIF(LTRIM(RTRIM(aBill.cFirma)), '') IS NOT NULL
             AND NULLIF(LTRIM(RTRIM(COALESCE(aBill.cVorname, '') + COALESCE(aBill.cName, ''))), '') IS NOT NULL
        THEN LTRIM(RTRIM(aBill.cFirma)) + ' – ' +
             LTRIM(RTRIM(
                 COALESCE(aBill.cVorname, '') +
                 CASE WHEN aBill.cVorname IS NOT NULL AND aBill.cName IS NOT NULL THEN ' ' ELSE '' END +
                 COALESCE(aBill.cName, '')
             ))
        WHEN NULLIF(LTRIM(RTRIM(aBill.cFirma)), '') IS NOT NULL
        THEN LTRIM(RTRIM(aBill.cFirma))
        ELSE LTRIM(RTRIM(
                 COALESCE(aBill.cVorname, '') +
                 CASE WHEN aBill.cVorname IS NOT NULL AND aBill.cName IS NOT NULL THEN ' ' ELSE '' END +
                 COALESCE(aBill.cName, '')
             ))
    END AS [Customer_DisplayLabel]

    -------------------------------------------------------------------------
    -- Lieferadresse (Standard-Lieferadresse: nStandard=1 AND nTyp=0)
    -------------------------------------------------------------------------

  , aShip.cAdressZusatz     AS [Shipping_Attention]
  , aShip.cStrasse          AS [Shipping_Street1]
  , aShip.cZusatz           AS [Shipping_Street2]

  , aShip.cOrt              AS [Shipping_City]
  , aShip.cBundesland       AS [Shipping_State]
  , aShip.cLand             AS [Shipping_Country]
  , aShip.cPLZ              AS [Shipping_PostalCode]

  -- Telefon / Fax: nur Ziffern
  , REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        ISNULL(aShip.cTel,''), ' ', ''), '+',''), '-',''), '/',''), '(',''), ')',''), '.',''), ',',''), CHAR(9),''), CHAR(13),'')
        AS [Shipping_Phone]

  , REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        ISNULL(aShip.cFax,''), ' ', ''), '+',''), '-',''), '/',''), '(',''), ')',''), '.',''), ',',''), CHAR(9),''), CHAR(13),'')
        AS [Shipping_Fax]

    -- OPTIONAL: Shipping-Kontaktinfos als Custom Felder (neutral)
  , aShip.cFirma            AS [Shipping_Company]
  , aShip.cVorname          AS [Shipping_FirstName]
  , aShip.cName             AS [Shipping_LastName]
  , aShip.cMail             AS [Shipping_Email]

  , REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
        ISNULL(aShip.cMobil,''), ' ', ''), '+',''), '-',''), '/',''), '(',''), ')',''), '.',''), ',',''), CHAR(9),''), CHAR(13),'')
        AS [Shipping_Mobile]

FROM tkunde k

OUTER APPLY (
    SELECT TOP (1) a.*
    FROM tAdresse a
    WHERE a.kKunde = k.kKunde
      AND a.nStandard = 1
      AND a.nTyp = 1
    ORDER BY a.kAdresse DESC
) aBill

OUTER APPLY (
    SELECT TOP (1) a.*
    FROM tAdresse a
    WHERE a.kKunde = k.kKunde
      AND a.nStandard = 1
      AND a.nTyp = 0
    ORDER BY a.kAdresse DESC
) aShip

LEFT JOIN tKundenGruppe kg
    ON kg.kKundenGruppe = k.kKundenGruppe

LEFT JOIN tZahlungsart za
    ON za.kZahlungsart = k.kZahlungsart

LEFT JOIN (
    SELECT
        kKunde,
        MAX(CASE WHEN kAttribut = 247 THEN COALESCE(cWertVarchar, CAST(nWertInt AS varchar(50)), CAST(fWertDecimal AS varchar(50)), CONVERT(varchar(19), dWertDateTime, 120)) END) AS EF_247,
        MAX(CASE WHEN kAttribut = 315 THEN COALESCE(cWertVarchar, CAST(nWertInt AS varchar(50)), CAST(fWertDecimal AS varchar(50)), CONVERT(varchar(19), dWertDateTime, 120)) END) AS EF_315,
        MAX(CASE WHEN kAttribut = 326 THEN COALESCE(cWertVarchar, CAST(nWertInt AS varchar(50)), CAST(fWertDecimal AS varchar(50)), CONVERT(varchar(19), dWertDateTime, 120)) END) AS EF_326,
        MAX(CASE WHEN kAttribut = 328 THEN COALESCE(cWertVarchar, CAST(nWertInt AS varchar(50)), CAST(fWertDecimal AS varchar(50)), CONVERT(varchar(19), dWertDateTime, 120)) END) AS EF_328,
        MAX(CASE WHEN kAttribut = 329 THEN COALESCE(cWertVarchar, CAST(nWertInt AS varchar(50)), CAST(fWertDecimal AS varchar(50)), CONVERT(varchar(19), dWertDateTime, 120)) END) AS EF_329,
        MAX(CASE WHEN kAttribut = 345 THEN COALESCE(cWertVarchar, CAST(nWertInt AS varchar(50)), CAST(fWertDecimal AS varchar(50)), CONVERT(varchar(19), dWertDateTime, 120)) END) AS EF_345,
        MAX(CASE WHEN kAttribut = 347 THEN COALESCE(cWertVarchar, CAST(nWertInt AS varchar(50)), CAST(fWertDecimal AS varchar(50)), CONVERT(varchar(19), dWertDateTime, 120)) END) AS EF_347,
        MAX(CASE WHEN kAttribut = 386 THEN COALESCE(cWertVarchar, CAST(nWertInt AS varchar(50)), CAST(fWertDecimal AS varchar(50)), CONVERT(varchar(19), dWertDateTime, 120)) END) AS EF_386,
        MAX(CASE WHEN kAttribut = 567 THEN COALESCE(cWertVarchar, CAST(nWertInt AS varchar(50)), CAST(fWertDecimal AS varchar(50)), CONVERT(varchar(19), dWertDateTime, 120)) END) AS EF_567
    FROM Kunde.tKundeEigenesFeld
    GROUP BY kKunde
) ef
    ON ef.kKunde = k.kKunde

WHERE k.kKunde = 1936;
 
Zuletzt bearbeitet:
Ähnliche Themen
Titel Forum Antworten Datum
Neu askJan | Neuer KI-Assistent für JTL-Wawi - schnelle, effiziente und transparente KI-Datenanalyse - ohne SQL! Dienstleistung, Jobs und Ähnliches 12
Neu Download JTL Wawi 1.5.54.0 User helfen Usern - Fragen zu JTL-Wawi 4
JTL WAWI 1.11.4 Neues Passwort generieren ist ausgegraut JTL-Wawi 1.11 0
Der ganze Betrieb hat keinen Zugriff auf JTL Wawi 1.11.4 meh JTL-Wawi 1.11 10
Artikelnummern-Konflikt bei JTL-WaWi-Import aus Shopify, SEO-Ranking erhalten JTL-Wawi 1.10 1
Lizenzschlüssel in JTL Wawi hinterlegen JTL-Wawi 1.10 1
JTL-Wawi startet nach Neustart nicht / hängt auf neu eingerichtetem PC (Remote-Datenbank) JTL-Wawi 1.11 14
Seit dem Update auf JTL Wawi 1.11.4 funktioniert der Workflow "Datei Schreiben" nicht JTL-Wawi 1.11 1
JTL Wawi 1.11.4 "Dashboard übernehmen" funktioniert nicht JTL-Wawi 1.11 1
JTL-Wawi App (1.11.x) – Lizenz angeblich belegt nach Löschen aller App-Registrierungen / kein Reset möglich JTL-Wawi App 3
Smart App Control blockiert start von JTL-Wawi JTL-Wawi 1.11 1
Neu JTL Wawi auf Windows Server 2025 mit SQL 2025? Installation von JTL-Wawi 10
Erfahrungen zur JTL Wawi 1.11.5 – Tipps, Bugs und Praxisberichte JTL-Wawi 1.11 19
Neu JTL-Wawi 1.11.4 – Vaterartikel lässt sich nach Entfernen eines Kindartikels nicht mehr speichern JTL-Wawi - Fehler und Bugs 3
Neu Neues E-Commerce Business mit JTL Wawi - Jtl Shop - Lexware Office (online) - Fragen Starten mit JTL: Projektabwicklung & Migration 2
Neu Bestellung aus JTL-Shop wird nicht in die Wawi übernommen Allgemeine Fragen zu JTL-Shop 1
Neu Amazon VCS - JTL Wawi > 1.10 - Lexware: Suche Best Practice Amazon-Anbindung - Ideen, Lob und Kritik 1
JTL-WAWI teilweise extrem lahm JTL-Wawi 1.10 8
Anfrage zur Einrichtung des Dashboards (Gewinnanzeige) in JTL-Wawi – Remote-Support über AnyDesk JTL-Wawi 1.10 6
Neu Bestehende POS an WAWI anbinden (JTL Administrator) Einrichtung / Updates von JTL-POS 7
JTL Wawi 1.8.11.2 zum Download JTL-Wawi 1.8 1
JTL-WaWi Konfigurator Bestandteile in WMS zusammenfassen JTL-Wawi 1.11 3
Neu BMEcat Schnittstelle JTL-Wawi [DEV] Schnittstellen Import / Export 3
Neu 0,1% an der Kasse erstellte Kunden nicht synchronisiert mit JTL Wawi Allgemeine Fragen zu JTL-POS 0
Neu SUCHE Freelancer für JTL WAWI Anbindung an WooCommerce und Einrichtung Dienstleistung, Jobs und Ähnliches 2
Neu JTL Wawi sendet keine aufzuschaltenden Artikel an Amzon Amazon-Anbindung - Fehler und Bugs 2
Fehler beim Verknüpfen von JTL-FFN mit Wawi – „Anmeldung nicht möglich“ JTL-Wawi 1.11 1
Neu Dokumentation: Kundenverknüpfung JTL-Wawi (Version 1.10.15.0) zu JTL-Shop JTL-Shop 5.2 Onlineshop-Anbindung 0
Einzelartikel als Kindartikel zu einem neuen Vaterartikel zusammenführen (JTL-Wawi + Shopware Connector) JTL-Wawi 1.8 0
Neu Handhabung JTL Wawi - zu Datev Unternehmen Online User helfen Usern - Fragen zu JTL-Wawi 1
Neu JTL-Wawi Aufträge die mit JTL-POS bezahlt wurde tauchen im Tagenabschluss auf JTL-POS - Fehler und Bugs 7
Neu Bitte legen Sie eine Retoure in JTL-Wawi an, damit eine korrekte Zuordnung zu den Stücklistenartikeln möglich ist. - WMS Retoure JTL-WMS / JTL-Packtisch+ - Fehler und Bugs 0
Neu Kapazitäten frei für Routineaufgaben JTL Wawi Dienstleistung, Jobs und Ähnliches 0
Datenabgleich von WooCommerce auf JTL Wawi 1.9.7.0 JTL-Wawi 1.9 0
JTL Wawi to ShopApotheke Artikelname eigenesfeld JTL-Wawi 1.11 16
Neu Wer hat 2025 mit Xentral Erfahrungen gesammelt? Wechsel von JTL‑Wawi in Sicht Smalltalk 17
JTL Wawi und JTL-POS gleichen nicht mehr ab JTL-Wawi 1.11 2
Neu Übergabe Versandlaber an Fulfiller möglich über FFN standallone aber nicht über FFN mit angebundeener JTL-WAWI Arbeitsabläufe in JTL-Wawi 0
JTL Wawi API beendet Registrierung nicht 1.11.1 JTL-Wawi 1.11 11
Keine Anmeldung mehr möglich in JTL Wawi seit Update? JTL-Wawi 1.11 5
JTL-Wawi stürzt beim Speichern des Shopify-Connectors jedes Mal ab JTL-Wawi 1.11 0
JTL Lob - gebuchte Wawi Edition in Fenster-Kopfleiste angezeigt JTL-Wawi 1.11 4
Issue angelegt [WAWI-71085] JTL Dashboard fehlerhafte Anzeige JTL-Wawi - Fehler und Bugs 11
1.11 mit 2 Mandanten - Dashboard bei Advanced Wawi zerschossen - JTL Start funktioniert JTL-Wawi 1.11 1
Neu PC-Umzug mit Datenbanksicherung von JTL-Wawi 1.5.48 User helfen Usern - Fragen zu JTL-Wawi 5
Neu JTL-Wawi 1.11.5 Stable Release Releaseforum 5
Neu JTL Wawi 1.10.14.3 / Unbehandelte Ausnahme im WaWi-Error-Log / Die Sammlung wurde geändert. Der Enumerationsvorgang kann möglicherweise .... JTL-Wawi - Fehler und Bugs 1
Neu JTL Shop Update von 5.3.3 auf 5.6 mit der Community Free Edition - WaWi funktion weiter gegeben? User helfen Usern - Fragen zu JTL-Wawi 1
Probleme mit dem Einstellen von Artikeln auf Amazon über JTL Wawi mit dem Lister 2.0 Amazon-Lister - Fehler und Bugs 6
Neu Update von JTL-Wawi von 1.6 auf 1.9 Installation von JTL-Wawi 12

Ähnliche Themen