# Umsetzungsplan: Korrekte Käufer-GLN + Käufer-Adresse in der AB (ORDRSP)

Stand: 2026-07-06 · **Status: UMGESETZT in Branch `edi-iwofurn-fixes`** (Buyer-GLN = original_gln_customer, Adresse via D365-Lookup, sonst GLN-only; Carrier-NAD nur mit GLN).
**Scope-Erweiterung 2026-07-06:** dieselbe Buyer-Logik jetzt auch in **InvoiceResponse** (sendete vorher Liefer-GLN/Kunden-GLN/ZR mit Lieferanschrift) und **DesadvResponse** (sendete vorher stumpf `getCustomerGLN` mit Lieferanschrift) — AB, Rechnung und Lieferavis melden damit denselben Käufer. Abschnitt 4.5 („nur OrderResponse") ist damit überholt.
**buyerAddress-Speicherung (4.1-4.3) VERWORFEN (Entscheidung 2026-07-03):** laut iwofurn ist nur FlagOfParty+AdressGLN Pflicht; Klarschrift deckt der D365-Lookup (86%) ab, Rest GLN-only — DDL/Import-Aufwand lohnt nicht.

## 1. Problem (Meldung Lena, 2026-07-02)

Die Käufer-GLN (`FlagOfParty = Buyer`) auf den ABs ist "sehr sehr häufig falsch". Lutz sendet in
der Bestellung 3 GLNs (Rechnungsempfänger, Lieferadresse, Käufer); im iwofurn-Portal sind alle 3
sichtbar, in D365 nur Rechnungsempfänger + Lieferadresse — die Käufer-GLN wird beim Import auf
eine Debitoren-GLN umgebogen und ist in D365 nicht mehr sichtbar.

## 2. Ursache (Code)

`OrderResponse.php:311-344` leitet die Buyer-GLN aus D365-Stammdaten ab:
- Besteller == Rechnungskunde && Liefer-GLN vorhanden → **Buyer-GLN = Liefer-GLN (Filiale!)**
- sonst Kunden-GLN, ersatzweise ZR-Nummer.
- Buyer-**Adresse** = Lieferadresse.

Bei Lutz greift praktisch immer der erste Fall → AB meldet die Filial-GLN als Käufer.

## 3. Verifizierte Fakten (DB-Analyse Prod, letzte 6 Monate)

- 19.891 EDI-Aufträge; `IncomingOrderStgAdditionalHeader.original_gln_customer`
  (= originale Käufer-GLN aus der Bestellung, gespeichert VOR dem Debitor-Umbiegen,
  `OrdersClass.php:3002`) ist zu **100% gefüllt**.
- Bei **9.766 Aufträgen (49%)** weicht sie von der umgebogenen Debitoren-GLN ab
  (Top-GLNs: Lutz-Gruppe `90070190…`) → so oft ist die heutige Buyer-GLN potenziell falsch.
- **Alle** abweichenden Käufer-GLNs sind in D365 bekannt (`GLNPartyCodeStaging_XLNEDI`);
  86% davon mit Adressdatensatz (`LOCATIONID` → auflösbar via `Rep_Dynamics::getCustomerByGLN`).
- `buyerAddress` in der eingehenden Order (Lobster-JSON) enthält die **komplette Adresse**:
  `gln, name, street, zip, city, country` (+ Kontaktfelder). Lutz: in 20/20 geprüften
  Bestellungen vollständig. Es gibt aber Sender, die NUR die GLN schicken (z.B. `4260129840000`)
  → Fallback-Kette nötig.

## 4. Umsetzung

### 4.1 Neue Spalten `IncomingOrderStgAdditionalHeader` (Azure D365-DB `byodbprod`)

Wir schreiben diese Tabelle selbst (kein Xalution-Import nötig). DDL:

```sql
ALTER TABLE IncomingOrderStgAdditionalHeader ADD
    original_buyer_name     NVARCHAR(255) NULL,
    original_buyer_street   NVARCHAR(255) NULL,
    original_buyer_zip      NVARCHAR(20)  NULL,
    original_buyer_city     NVARCHAR(100) NULL,
    original_buyer_country  NVARCHAR(5)   NULL;
```

(Klären: DDL-Rechte des `qwellcode_admin`-Users; sonst DB-Admin.)

### 4.2 Entity `src/Entity/Incoming_order_stg_additional_header.php`

Properties + Getter/Setter für die 5 neuen Felder (Muster: bestehende
`original_gln_customer`-Felder).

### 4.3 Eingang: `OrdersClass::queue()` (Adresse wegspeichern)

An der Stelle, wo `$buyerAddress` ermittelt wird (`OrdersClass.php:2379-2404`):
- **Wichtig:** Adresse aus derselben Quelle nehmen wie die GLN (buyerAddress, Fallback
  billingAddress — exakt die bestehende Fallback-Reihenfolge Z. 2380-2389).
- Beim AdditionalHeader-Insert (`OrdersClass.php:3000-3019`) zusätzlich setzen:
  `original_buyer_name/street/zip/city/country` aus `$buyerAddress['name'|'street'|'zip'|'city'|'country']`
  (leer lassen, wenn der Sender sie nicht mitschickt).

### 4.4 Ausgang: Buyer-NAD in `OrderResponse::generateJSON()` (Z. 311-344)

**GLN und Adresse fallen GETRENNT zurück** — die (immer vorhandene) korrekte GLN darf nie
mit der Adresse zusammen auf die alte Ableitung kippen:

**GLN** (das identifizierende Element, das der Empfänger validiert):
| Prio | Quelle | wann |
|---|---|---|
| 1 | `original_gln_customer` | EDI-Auftrag (100% gefüllt, DB-verifiziert) |
| 2 | heutige Ableitung (deliveryGLN/customerGLN/ZR) | nur Nicht-EDI-Aufträge |

**Adresse** (Beiwerk, best effort — im Schema KOMPLETT OPTIONAL, siehe unten):
| Prio | Quelle | wann |
|---|---|---|
| 1 | gespeicherte `original_buyer_*`-Felder | Sender schickt Adresse mit (Lutz: 20/20 geprüft) |
| 2 | D365-Adressdatensatz via `getCustomerByGLN(original_gln_customer)` | Sender schickt nur GLN |
| 3 | **GLN-only NAD** (keine Anschrift) — bevorzugt, mit Lena/iwofurn bestätigen; alternativ Lieferadresse (heutiges Verhalten) | weder mitgeschickt noch in D365 |

**GEKLÄRT — iwofurn-Antwort (2026-07-03):** Im XML.Einrichten-Format sind bei Adressen
`FlagOfParty` UND `AdressGLN` Pflicht; die Klarschrift-Anschrift ist optional. Ausnahme:
Lieferadressen DP/UC — dort reicht auch Name + `FlagOfParty` (relevant für unsere
Dropshipping-Endkunden-NADs, die keine GLN tragen — das ist damit abgedeckt).
→ **Buyer-NAD nur mit GLN ist zulässig.** Die strenge Anschrift-Pflicht
(Firma+Straße+PLZ+Ort+Land) kommt aus UNSEREM `checkIsAddressComplete`, nicht aus dem
Format → für den Buyer-NAD lockern, sobald eine valide GLN vorhanden ist.
⚠️ Umkehrschluss beachten: `AdressGLN` ist (außer DP/UC) PFLICHT — der Carrier-NAD
(`carrier_info_gln` ist nullable) sollte daher nur gesendet werden, wenn eine GLN vorliegt.

Restrisiko Prio 3 ist minimal (DB-verifiziert): Von 1.390 "GLN ohne D365-Adresse"-Aufträgen
sind 1.344 Otto-**Dropshipping** — dort wird der Buyer-NAD gar nicht gebaut. Es bleiben
**47 Aufträge eines einzigen Kunden** (`4260129840000`) — GLN-only ist dort sauberer als die
heutige (faktisch falsche) Filial-Anschrift.

- `checkIsAddressComplete(ADDRESSTYPE_BUYER, …)` je Adress-Stufe anwenden; unvollständig →
  nächste Stufe (kein Fehler werfen).
- `BusinessCustomerNumber` bleibt `ORDERINGCUSTOMERACCOUNTNUMBER` (unverändert).
- `Street1` ggf. durch `OpenAI::getStreet` normalisieren wie bei den anderen NADs.
- Bestandsaufträge (vor DDL importiert) haben keine gespeicherte Adresse → greifen automatisch
  auf Adress-Prio 2/3; die GLN ist trotzdem korrekt (keine Migration nötig).

**Zur Frage "speichern wir das nicht in D365?":** Der D365-*Verkaufsauftrag* kennt die
Käufer-GLN/-Adresse tatsächlich nicht (wird beim Import auf den Debitor umgebogen) — muss er
auch nicht. Die AB liest aus UNSEREN Staging-Tabellen (`IncomingOrderStgAdditionalHeader`),
exakt der Mechanismus, mit dem heute schon `original_technical_sender` und
`original_gln_invoice` in die AB fließen. D365/Xalution bleiben unberührt. Die GLN-*Stammdaten*
(`GLNPartyCodeStaging_XLNEDI`) kennen ohnehin alle betroffenen Käufer-GLNs (100%, DB-verifiziert).

### 4.5 Scope

- Nur **OrderResponse** (dort ist der Buyer-NAD falsch). Invoice sendet keinen Buyer-NAD.
- Vorab-AB und normale AB nutzen denselben NAD-Codepfad → beide profitieren automatisch.

## 5. Tests

1. Lutz-Auftrag (Besteller == Rechnungskunde): Buyer-GLN = von Lutz gesendete Käufer-GLN
   (nicht Filial-/Liefer-GLN), Adresse = von Lutz gesendete Adresse.
2. EDI-Auftrag mit nur-GLN-buyerAddress: Adresse kommt aus D365 (`getCustomerByGLN`).
3. Manueller Auftrag (kein `IncomingOrderStg`): Verhalten unverändert wie heute.
4. Alt-Auftrag (importiert vor Spalten-DDL): kein Fehler, Fallback auf Prio 2/3.
5. Dropshipping-Auftrag: Buyer-NAD-Zweig wird dort nicht durchlaufen (Endkunden-Adressen) —
   sicherstellen, dass nichts kollidiert.

## 6. Offene Punkte

- [x] GO erteilt, umgesetzt in Branch `edi-iwofurn-fixes` (2026-07-03).
- [x] ~~DDL auf `IncomingOrderStgAdditionalHeader`~~ **entfällt — buyerAddress-Speicherung verworfen** (GLN reicht laut iwofurn, D365-Lookup liefert die Kür-Anschrift).
- [x] ~~Mit Lena/iwofurn bestätigen: Buyer-NAD nur mit GLN akzeptiert?~~ **JA — iwofurn
      2026-07-03: FlagOfParty + AdressGLN Pflicht, Anschrift optional (Ausnahme DP/UC:
      Name reicht).** → Adress-Prio 3 = GLN-only (statt Lieferadresse); der D365-Lookup
      (Prio 2) bleibt als nice-to-have, kann aber im ersten Schnitt entfallen.
