@@ -18,7 +18,7 @@ und verlustfrei, ohne neue Plugin-Abhängigkeiten und ohne Wechsel der API-Versi
1818
1919### IN-Scope
2020
21- - Pagination-Loop beim Bestellabruf ( ` X-WP-TotalPages ` -Header auswerten)
21+ - Single-Order-Abruf mit ` after ` -Filter + ` exclude ` -Tupel-Cursor; Caller-Loop iteriert.
2222- ` after ` -Filter (ISO-8601) statt 800er-` include ` -Hack
2323- Persistenz "letzter erfolgreicher Import-Timestamp" pro Shop
2424- Fallback-Logik für Erstlauf (kein Timestamp vorhanden)
@@ -41,6 +41,7 @@ und verlustfrei, ohne neue Plugin-Abhängigkeiten und ohne Wechsel der API-Versi
4141| Persistenz-Feld | ` shopexport.einstellungen_json ` → ` felder.letzter_import_timestamp ` | Bestehende Struktur nutzen, keine DB-Migration. |
4242| Timestamp-Format | ISO-8601 ` Y-m-d\TH:i:s ` (UTC) | Direkt an ` after= ` durchreichbar. |
4343| Caller-Cap (pre-existing) | ` shopexport.maxmanuell ` , default 100 | Der Batch-Cap liegt beim shopimport.php-Caller, nicht im Importer. |
44+ | Cursor-Format | Tupel ` (letzter_import_timestamp, letzter_import_order_id) ` | Loest Same-Second-Kollisionen via after=ts-1 + exclude=[ id] . |
4445
4546## 4. Datei- und Funktions-Landkarte
4647
@@ -143,24 +144,17 @@ SELECT einstellungen_json FROM shopexport WHERE id = <shopid>
143144** Neu — Single-Order-Pseudocode:**
144145
145146```
146- response = client.get('orders', {
147- status[]: configuredStatuses,
148- after: this.lastImportTimestamp,
149- per_page: 1,
150- page: 1,
151- orderby: 'date',
152- order: 'asc',
153- })
147+ afterTs = ts-1s (falls ts gesetzt)
148+ query = orders?after=<afterTs>&per_page=1&orderby=date&order=asc
149+ (+ exclude=[last_id] wenn last_id bekannt)
154150
155151if response is empty:
156152 return []
157153
158154wcOrder = response[0]
159155order = parseOrder(wcOrder)
160156
161- # Cursor auf diese Order setzen, damit naechste Caller-Iteration
162- # die naechste Order holt.
163- this.persistLastImportTimestamp(wcOrder.date_created_gmt)
157+ persistLastImportCursor(order.date_created_gmt, order.id)
164158
165159return [{ id: order.auftrag, sessionid: '', logdatei: '', warenkorb: ... }]
166160```
@@ -186,6 +180,16 @@ Der after-Filter sorgt dafuer, dass jede Iteration die naechste Order
186180holt. Ein Crash zwischen ` RemoteGetAuftrag() ` und ` shopimport_auftraege ` -
187181Insert verliert max. diese eine Order.
188182
183+ ### Schritt 5b — Migration-Helper
184+
185+ Um die ab_nummer → timestamp Migration auch im Count-Pfad auszufuehren
186+ (shopimport.php ruft ImportGetAuftraegeAnzahl() BEFORE ImportGetAuftrag()):
187+
188+ - Extraktion in eine private Methode ` migrateAbNummerIfNeeded() ` .
189+ - Aufruf am Anfang von beiden ImportGetAuftraegeAnzahl() und ImportGetAuftrag().
190+ - Idempotent durch ` $lastImportTimestampIsFallback ` -Check — nach einmaliger Migration
191+ keine weiteren Reads.
192+
189193### Schritt 6 — Transitions-Kompatibilitaet ` ab_nummer `
190194
191195** Ist-Zustand:** ` CatchRemoteCommand('data') ` liefert u.a. ` ab_nummer ` — die naechste
@@ -299,6 +303,7 @@ Manuelle Integrationstests mit seeded Test-Orders.
299303| Andere Shopimporter erben Basisklasse-Struktur | niedrig | niedrig | Nur ` shopimporter_woocommerce.php ` anfassen, ` ShopimporterBase ` nicht anruehren |
300304| URL-Laengenlimits der WAF beim ` status[] ` -Array | sehr niedrig | niedrig | Typisch 2-3 Status-Werte, URL bleibt kurz |
301305| Caller-Kontrakt-Abweichung (1 vs. n Orders) | niedrig | hoch | Per Design Single-Order-Return; Caller-Loop fuer Volume-Handling |
306+ | Timestamp-Kollision bei identischem date_created_gmt | niedrig | mittel | Adressiert durch Tupel-Cursor (ts, id) + exclude-Parameter |
302307
303308## 9. Definition of Done
304309
0 commit comments