dolibarr.idsconnect/CHANGELOG.md
data 81646f5ea4 feat(launch): Konfigurationsvalidierung & GlobalNotify-Integration v3.4
- Neue Funktion idsconnect_notify() für GlobalNotify-Benachrichtigungen
  mit Fallback auf dol_syslog falls Modul nicht installiert
- Neue Funktion idsconnect_validateSupplierConfig() prüft Supplier-
  Konfiguration vor dem Launch auf Vollständigkeit
- launch.php: Validierung vor Formular-Submit
  - Fehler (leeres Passwort): blockiert Launch, Redirect zur Konfiguration
  - Warnung (leerer Username): erlaubt Launch aber warnt User + Admin
- Löst Problem: Klux "Weiter"-Button fehlte weil ids_username leer war,
  Login schlug still fehl ohne jede Fehlermeldung

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 12:58:23 +01:00

360 lines
16 KiB
Markdown
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# IDS Connect Modul - Changelog
## Projektinfo
- **Modul**: IDS Connect für Dolibarr ERP
- **Modul-ID**: 500025
- **Zweck**: Punchout-Schnittstelle zu Elektrogroßhändlern (Sonepar, Kluxen)
- **Protokoll**: IDS Connect 2.0/2.5 (Browser-basiertes Punchout)
- **Entwickler**: Eduard Wisch / Claude AI
---
## v3.4 - Klux-Support & Konfigurationsvalidierung (17.03.2026)
### 🐛 BUGFIX: Klux "Weiter"-Button fehlte
- **Problem**: Beim WKS (Warenkorb senden) an Klux fehlte im Klux-Shop der "Weiter"-Button zur Bestellbestätigung.
- **Ursache**: `ids_username` (→ `name_kunde`) war leer in der Klux-Supplier-Konfiguration. Ohne gültigen Login-Namen zeigt der Klux-Shop den Katalog, aber keinen IDS-Checkout-Button.
- **Fix**: `ids_username = ids-1222925` (Kundennummer) in der Produktiv-DB gesetzt.
- **Hintergrund**: Bei deutschen Elektrogroßhändlern (Klux, Sonepar) wird die Kundennummer oft auch als Login-Username verwendet. Das `name_kunde`-Feld darf nicht leer sein.
### ✨ Feature: Konfigurationsvalidierung vor IDS-Launch
Neue Validierung in [launch.php](launch.php) bevor das Formular an den Großhändler-Shop gesendet wird:
- **Fehler (blockiert Launch)**: Kein Passwort konfiguriert → Redirect zur Supplier-Konfiguration
- **Warnung (erlaubt aber warnt)**: Kein Benutzername konfiguriert → `name_kunde` wäre leer → "Weiter"-Button fehlt im Shop
- Verhindert das stille Fehlschlagen ("Shop lädt, aber kein Bestell-Button, niemand weiß warum")
### ✨ Feature: GlobalNotify-Integration
- **Neue Funktion** `idsconnect_notify()` in [lib/idsconnect.lib.php](lib/idsconnect.lib.php)
- Sendet Benachrichtigungen an Admins über GlobalNotify (falls Modul aktiv)
- Fallback auf `dol_syslog()` wenn GlobalNotify nicht installiert
- Typen: `error`, `warning`, `info`, `action`
- **Neue Funktion** `idsconnect_validateSupplierConfig()` in [lib/idsconnect.lib.php](lib/idsconnect.lib.php)
- Prüft Supplier-Konfiguration vor dem Launch auf Vollständigkeit
- Gibt strukturierte `errors` und `warnings` zurück
- Bei unvollständiger Konfiguration: Admin erhält GlobalNotify-Meldung mit direktem Link zur Supplier-Konfigurationsseite
### 🔧 Technische Details
- **Validierte Felder**: `ids_password` (Error), `ids_username` (Warning)
- **GlobalNotify-Typ**: `action` für Fehler (Link zur Konfiguration), `warning` für Warnungen
- **Keine Code-Änderungen** an der Formular-Generierung (`idsconnect.class.php`) - nur Vorbedingung in `launch.php`
---
## v2.9 - Preis-Vergleich & Kritische Bugfixes (12.03.2026)
### 🐛 KRITISCHER BUGFIX: SQL-Spaltenname korrigiert
- **Problem**: `matchProducts()` hat SQL-Fehler produziert wegen falscher Spalte `pfp.qty` (existiert nicht!)
- **Fix**: Alle `pfp.qty``pfp.quantity` ersetzt in:
- [class/idsconnect.class.php:578](class/idsconnect.class.php#L578) - matchProducts() SELECT Query
- [class/idsconnect.class.php:589](class/idsconnect.class.php#L589) - $obj->qty → $obj->quantity
- [tab_supplierorder.php:154,177](tab_supplierorder.php#L154) - Preis-Vergleich Queries
- **Auswirkung**: Vorher wurden ALLE Artikel als Freitext importiert (kein Produkt-Matching!)
- **Test**: Artikel 0486597 wird jetzt korrekt als COK-WAG-2273-202-2F erkannt ✅
### 🐛 BUGFIX: Produkt-Matching erweitert
- **Fix**: `p.status = 1``p.tosell = 1` in matchProducts() und tab_supplierorder.php
- **Grund**: Dolibarr verwendet `tosell` für verkaufbare Produkte, nicht `status`
### ✨ Preis-Vergleich in Warenkorb-Import (cart_review.php)
- **Basis-Price-Vergleich**: Shop-Preis (z.B. 12,88€/100 Stk) vs. DB-Preis (12,88€/100 Stk)
- **Vorteil**: Keine Rundungsfehler mehr durch Stückpreis-Umrechnung
- **Berechnung**:
- `shop_basis_price = raw_netprice` (vom Shop empfangen)
- `db_basis_price = debug_price` (aus DB geladen)
- Deviation = ((shop - db) / db) × 100
- **DEBUG-Ausgabe** für Admins: Zeigt Shop-Preis, DB-Preis, Einzelpreis zur Kontrolle
- **Farbcodierung**:
- Grün: ≤2% Abweichung
- Gelb: 2-10% Abweichung
- Rot: >10% Abweichung
### ✨ Preis-Vergleich in Lieferantenbestellungen (tab_supplierorder.php)
- **Admin-Option**: `IDSCONNECT_PRICE_UPDATE_ENABLED` (Standard: AUS)
- **Schwellwert**: `IDSCONNECT_PRICE_UPDATE_THRESHOLD` (Standard: 5%)
- **Anzeige**: Tabelle mit allen Positionen, die vom Schwellwert abweichen
- **Freitext-Warnung**: Zeigt an, wenn Artikel nicht mit Dolibarr-Produkten verknüpft sind
- **Keine Auto-Updates**: Nur Anzeige, keine automatische Preis-Aktualisierung
### 🔧 Technische Details
- **matchProducts()**: Lädt jetzt `pfp.quantity`, `pfp.price` korrekt
- **Stückpreis-Berechnung**: `unit_price = price / quantity` (mit Fallback qty=1)
- **Debug-Felder**: `debug_price` und `debug_qty` für Basis-Price-Vergleich
- **Sprachdateien**: Neue Keys für Preis-Verwaltung (de_DE)
---
## v2.2 - Menü-Integration, ADL-Hooks & Admin-Erweiterung (19.02.2026)
### Menü unter Einkauf/Lieferantenbestellungen
- **Kein eigenes Top-Menü mehr**: IDS Connect ist jetzt unter Einkauf > Lieferantenbestellungen > IDS Connect eingegliedert
- Untermenüs (Großhändler, Transaktionslog) als Level 2/3 unter dem IDS Connect Eintrag
- Bessere Integration in den normalen Dolibarr-Workflow
### ADL-Buttons auf Produkt-Lieferantenpreisen (Hook)
- **Neue Datei**: `class/actions_idsconnect.class.php` - Hook-Klasse für `pricesuppliercard`
- Zeigt ADL-Button (externer Link) direkt in der Lieferantenpreis-Tabelle auf Produktkarten
- Button nur sichtbar wenn Lieferant mit IDS Connect Großhändler verknüpft UND Lieferanten-Artikelnummer vorhanden
- Spalte erscheint nur wenn mindestens ein aktiver IDS-Supplier existiert
- Gecachte DB-Abfragen für Performance (static-Cache pro Request)
### Admin Setup-Seite erweitert
- **Großhändler-Schnellübersicht**: Tabelle mit allen konfigurierten Großhändlern direkt auf der Einstellungsseite
- Zeigt Name, URL, IDS-Version, Status (Aktiv/Inaktiv, Testmodus) pro Großhändler
- **Version prüfen**: Button pro Großhändler für SV-Action (Schnittstellenversion)
### Dashboard
- **Shop öffnen**: Neuer LI-Button (Login-Info) neben dem WKE-Button auf der Übersichtsseite
### Sprachdateien
- `IdsconnectShowInShop` - Tooltip für ADL-Button in Preistabelle
- `IdsconnectCheckVersion` - Button für Schnittstellenversion prüfen
---
## v2.1 - WKS-Flow & URL-Handling (18.02.2026)
### WKS (Warenkorb senden) - Live getestet mit Sonepar
- **WKS End-to-End**: Lieferantenbestellung in Dolibarr anlegen → IDS Connect Tab → "Warenkorb senden" → Artikel im Sonepar-Shop vorausgefüllt → "Nur bestellen"
- **buildCartXml()**: Von altem `<Artikel>`-Format auf IDS Connect 2.0 umgestellt
- Namespace `xmlns="http://www.itek.de/Shop-Anbindung/Warenkorb/"`
- `<OrderItem>` statt `<Artikel>`, `<ArtNo>`, `<Kurztext>`, `<NetPrice>`
- `PriceBasis=1` (Dolibarr-Preise sind bereits Stückpreise)
- `VAT` aus MwSt-Satz der Bestellposition
- **launch.php**: MwSt-Satz (`tva_tx`) und Einheit `PCE` in Warenkorb-Positionen
- **Bestellstatus**: Setzt Lieferantenbestellung auf "Bestellt" nach WKS-Versand
### URL-Handling (Cross-Domain-Fix)
- **Problem**: `$dolibarr_main_url_root` war `192.168.155.1:8090` (intern, ohne Protokoll), User greift über `awl.data-it-solution.de` zu → Callback-Links waren kaputt
- **user_base_url-Tracking**: Speichert beim Launch die Domain des Users (`HTTP_HOST`) in den Log-Request-Daten, callback.php liest diese für Links zurück
- **Protokoll-Prefix**: Automatische `http://`-Ergänzung wenn `://` fehlt
- **URL-Priorität** für Callback-Links:
1. `user_base_url` aus Log (Domain des Users beim Launch)
2. `IDSCONNECT_PUBLIC_URL` aus Einstellungen
3. `$dolibarr_main_url_root` als Fallback
### Sonepar-Shop Buttons (Erkenntnisse)
- **"Warenkorb übergeben"** = WKE-Callback zurück an Dolibarr (für Shop-Einkauf)
- **"Nur bestellen"** = Bestellung direkt bei Sonepar auslösen (für WKS-Flow)
- **"Bestellen"** = beides (übergeben + bestellen)
- Bei WKS "Nur bestellen" verwenden, da Bestellung in Dolibarr bereits existiert
---
## v2.0 - Sonepar Live-Integration (18.02.2026)
### Kernproblem
Die erste Version konnte den Warenkorb von Sonepar nicht empfangen. Mehrere Probleme:
1. `enctype="multipart/form-data"` fehlte im Launch-Formular
2. XML-Parser erkannte das IDS Connect Format nicht
3. Redirect nach Callback führte zum Login (Session-Problem)
4. Preiseinheit (PriceBasis) wurde nicht berücksichtigt
### Sonepar XML-Format (Erkenntnisse)
Sonepar sendet IDS XML im POST-Feld `warenkorb` mit folgender Struktur:
```xml
<Warenkorb xmlns="http://www.itek.de/Shop-Anbindung/Warenkorb/">
<WarenkorbInfo>
<Date>2026-02-18+01:00</Date>
<Time>05:47:07+01:00</Time>
<RueckgabeKZ>Warenkorbrückgabe</RueckgabeKZ>
<Version>2.0</Version>
</WarenkorbInfo>
<Order>
<OrderInfo>
<ModeOfShipment>Lieferung</ModeOfShipment>
<Cur>EUR</Cur>
</OrderInfo>
<DeliveryPlaceInfo>
<Address>
<Name1>...</Name1><Name2>...</Name2>
<Street>...</Street><PCode>...</PCode>
<City>...</City><Country>DE</Country>
</Address>
</DeliveryPlaceInfo>
<OrderItem>
<ArtNo>0351817</ArtNo>
<Qty>100</Qty>
<QU>PCE</QU>
<Kurztext>Klauke Stossverbinder f.Massivleiter SV1525</Kurztext>
<OfferPrice>26.8</OfferPrice>
<NetPrice>16.88</NetPrice>
<PriceBasis>100</PriceBasis> <!-- Preis gilt für 100 Stück! -->
<VAT>19</VAT>
<TechnClarification>No</TechnClarification>
</OrderItem>
</Order>
</Warenkorb>
```
Wichtig: Namespace `xmlns="http://www.itek.de/Shop-Anbindung/Warenkorb/"` verhindert
direkten SimpleXML-Zugriff → Namespace-Stripping nötig.
### Änderungen
#### callback.php - Komplett-Rewrite auf v2.0
- **Versionskennung**: `IDSCONNECT_CALLBACK_VERSION = '2.0'` zur Identifikation
- **Kein Redirect mehr**: Zeigt Ergebnis direkt als HTML-Seite (NOLOGIN)
- Vorher: `header('Location: idsconnectindex.php?error=...')` → Login-Problem
- Nachher: `idsconnectCallbackPage()` rendert eigenständige HTML-Seite
- **7 Datenquellen**: Durchsucht POST[warenkorb], POST[cart], FILES[warenkorb],
FILES[cart], erste hochgeladene Datei, alle POST-Felder nach XML, php://input
- **OCI-Format**: Erkennt `NEW_ITEM-*` POST-Felder (SAP OCI Punchout)
- **Debug-Daten**: Speichert immer alle empfangenen Daten in DB (response_data JSON)
- **Roh-XML**: Speichert XML sofort in DB, auch bei Parse-Fehler
- **Interne Links**: Verwendet `$dolibarr_main_url_root` statt `DOL_URL_ROOT`
für korrekte Navigation nach Callback auf öffentlicher Domain
#### class/idsconnect.class.php - Parser erweitert
- **Format 5**: IDS Connect 2.5 `Warenkorb/Order/OrderItem` hinzugefügt
- **Format 6**: Namespace-Stripping - entfernt `xmlns` und Prefixe, versucht
alle Format-Checks erneut auf bereinigtem XML
- **PriceBasis**: Neues Feld `PriceBasis`/`PE`/`Preiseinheit` wird geparst
- Einzelpreis = NetPrice / PriceBasis (z.B. 16,88 / 100 = 0,1688€/Stk)
- Angebotspreis wird analog umgerechnet
- `preiseinheit` und `raw_netprice` werden im Item-Array mitgegeben
- **VAT/MwSt**: MwSt-Satz wird aus `VAT`/`MwSt` geparst und als `mwst_satz` gespeichert
- **Erweiterte Feldnamen**: ArtNo, Kurztext, Langtext, NetPrice, OfferPrice,
ManufacturerID, Hinweis in den Mapping-Arrays ergänzt
- **Debug-Logging**: Root-Element und Kinder werden via dol_syslog geloggt
- **Bessere Fehlermeldung**: Zeigt Root-Element und Kinder bei "Keine Artikel gefunden"
#### launch.php - Formular-Fix
- **enctype**: `enctype="multipart/form-data"` zum Launch-Formular hinzugefügt
(fehlte, dadurch kamen keine Callback-Daten an)
#### cart_review.php - Preisanzeige verbessert
- **PE-Info**: Zeigt unter dem Stückpreis die Preiseinheit-Info:
"16,88 / 100 Stk" wenn PriceBasis > 1
- **MwSt**: Verwendet `mwst_satz` aus XML bei Bestellerstellung (statt 0%)
#### mockserver.php - Realitätsnäher
- **IDS Connect 2.5 Format**: Generiert `Warenkorb/Order/OrderItem` statt altes
`<Artikel>` Format
- **Namespace**: Verwendet `xmlns="http://www.itek.de/Shop-Anbindung/Warenkorb/"`
wie Sonepar
- **Neue Felder**: PriceBasis, VAT im generierten XML
#### admin/setup.php - Erweiterte Konfiguration
- **Öffentliche URL**: Feld für IDSCONNECT_PUBLIC_URL (Reverse-Proxy)
- **Callback-URL**: Berechnete Anzeige (read-only, klickbar zum Kopieren)
- **Mock-Server URL**: Berechnete Anzeige (immer intern)
- **WKS-Warnschwellen**: Mengen- und Wertgrenzen konfigurierbar
- **WKS-PIN**: Verschlüsselte PIN für Warenkorb-Senden
---
## v1.0 - Initiale Entwicklung (17.02.2026)
### Grundgerüst
- Modul-Struktur nach Dolibarr-Standard (modIdsconnect.class.php)
- Berechtigungssystem: read, use, config, delete
- Mehrsprachig: de_DE, en_US
- SQL-Tabellen: `idsconnect_supplier`, `idsconnect_log`
### Dateien erstellt
| Datei | Zweck |
|-------|-------|
| `class/idsconnect.class.php` | Kern-Klasse: Formular-Builder, XML-Parser, XML-Generator |
| `class/idssupplier.class.php` | Großhändler-Konfiguration (URL, Login, Passwort) |
| `class/idslog.class.php` | Transaktionslog (CRUD, Status-Updates) |
| `callback.php` | HOOKURL-Empfänger, NOLOGIN-Seite |
| `launch.php` | Formular-Generator für Shop-Weiterleitung |
| `mockserver.php` | Test-Shop für lokale Entwicklung (NOLOGIN, nur Testmodus) |
| `idsconnectindex.php` | Hauptseite mit Großhändler-Liste und Log |
| `cart_review.php` | Empfangenen Warenkorb prüfen, Bestellung erstellen |
| `supplier_card.php` | Großhändler-Konfigurationsseite |
| `supplier_list.php` | Großhändler-Liste |
| `log_list.php` | Log-Übersicht mit Filter |
| `log_detail.php` | Log-Detailansicht (XML, Response, Fehler) |
| `tab_supplierorder.php` | Tab auf Lieferantenbestellungen |
| `test_connection.php` | AJAX-Verbindungstest |
| `admin/setup.php` | Modul-Einstellungen |
| `admin/about.php` | Über-Seite |
| `lib/idsconnect.lib.php` | Hilfsfunktionen, Tab-Builder |
| `build/buildzip.php` | Modul-ZIP erstellen |
### IDS Connect Actions
| Kürzel | Bedeutung | Richtung | Status |
|--------|-----------|----------|--------|
| WKE | Warenkorb empfangen | IN | Funktioniert (Sonepar getestet) |
| WKS | Warenkorb senden | OUT | Funktioniert (Sonepar getestet) |
| ADL | Artikel Deep-Link | OUT | Implementiert |
| LI | Login-Info | OUT | Implementiert |
| SV | Schnittstellenversion | OUT | Implementiert |
### Sicherheit
- CSRF-Tokens auf allen Formularen
- Callback-Tokens: HMAC-SHA256, 2h gültig, einmalig verwendbar
- XXE-Schutz: `LIBXML_NONET | LIBXML_NOENT`
- Passwörter verschlüsselt via `dolEncrypt`/`dolDecrypt`
- Testmodus als Standard (IDSCONNECT_TESTMODE=1)
- Live-Modus erfordert Bestätigung
### Sonepar-Schnittstellen
Sonepar unterstützt 3 Schnittstellen:
1. **IDS** - XML-basiertes Punchout (das was wir implementiert haben)
2. **OCI** - SAP Open Catalog Interface, POST-Felder `NEW_ITEM-*` (ebenfalls implementiert)
3. **UGL** - ÜberGabeSchnittstelleLang, FTP-basierter Dokumentenaustausch
(Angebote, Bestellungen, Lieferscheine, Rechnungen) - nicht implementiert
---
## Architektur
### WKE-Flow (Warenkorb empfangen)
```
1. User klickt "Zum Shop" in Dolibarr
2. launch.php generiert HTML-Formular mit:
- Shop-URL als action
- USERID, PASSWORT, AESSION (WKE)
- HOOKURL (callback.php?token=...)
3. Browser submitted Formular an Großhändler-Shop
4. User wählt Artikel im Shop
5. Shop sendet Warenkorb per POST an HOOKURL
6. callback.php empfängt, parst, speichert
7. Callback-Seite zeigt Ergebnis + Link zu cart_review.php
8. cart_review.php: Artikel prüfen → Lieferantenbestellung erstellen
```
### Preisberechnung mit PriceBasis
Im Elektrogroßhandel werden Preise oft pro Preiseinheit (PE) angegeben:
```
PriceBasis=100 → NetPrice gilt für 100 Stück
Einzelpreis pro Stück = NetPrice / PriceBasis
Gesamtpreis = Qty * (NetPrice / PriceBasis)
Beispiel: 100 Stk Stoßverbinder
NetPrice=16.88, PriceBasis=100
→ Stückpreis = 16.88 / 100 = 0.1688€
→ Gesamt = 100 * 0.1688 = 16.88€
```
### Datenbank-Tabellen
- `llx_idsconnect_supplier` - Großhändler mit URL, Login, Passwort (verschlüsselt)
- `llx_idsconnect_log` - Transaktionslog mit Status, XML, Response-JSON, Token
---
## Konfiguration
### Dolibarr-Einstellungen (Schlüssel)
| Schlüssel | Beschreibung | Standard |
|-----------|-------------|----------|
| IDSCONNECT_TESTMODE | Testmodus aktiv | 1 |
| IDSCONNECT_PUBLIC_URL | Öffentliche URL für Callback | (leer) |
| IDSCONNECT_LOG_ENABLED | Logging aktiviert | 1 |
| IDSCONNECT_WKS_WARN_QTY | WKS Warn-Menge | 0 |
| IDSCONNECT_WKS_WARN_VALUE | WKS Warn-Wert | 0 |
| IDSCONNECT_WKS_PIN | PIN für WKS (verschlüsselt) | (leer) |
### Sonepar-Konfiguration
- URL: `https://www.sonepar.de/punchout/v1/ids/setup?OrganizationId=12`
- Benutzer: ids
- Kundennummer: 100184
- IDS-Version: 2.0