EDI / B2B¶
B2B-klanten (importeurs, grotere garageketens) kunnen bestellingen rechtstreeks vanuit hun eigen ERP-systemen plaatsen via een XML-koppeling op basis van het reifen.net / ADC-protocol — de industrie-standaard in de Europese bandenhandel. De EDI-API is een afzonderlijke Slim 4-applicatie die naast de webshop draait.
Architectuur¶
sequenceDiagram
participant Partner as B2B-partner (ERP)
participant API as _api/index.php (Slim 4)
participant Auth as ClientMiddleware
participant Body as BodyParsingMiddleware
participant Svc as AnyDocumentService
participant DB as atx_www / orders
Partner->>API: POST /edi (XML, Basic Auth)
API->>Auth: HTTP Basic Auth check (edi_clients)
Auth-->>Partner: 401 bij mislukking
API->>Body: application/xml → DTO
API->>Svc: Route op documenttype (inquiry/order/…)
Svc->>DB: Artikel & klant opzoeken, order aanmaken
Svc-->>API: XML-antwoord
API-->>Partner: <order_response> / <quote> / …
Entry points (_api/index.php:66–74):
| URL | Controller | Omschrijving |
|---|---|---|
POST /edi |
EdiController::index |
Primair EDI-eindpunt |
POST /tyrestream |
EdiController::index |
Legacy-alias (achterwaartse compatibiliteit) |
Beide routes landen op dezelfde handler. AnyDocumentService kiest op basis van het XML-root-element het juiste service-object.
Authenticatie¶
src/Atraxion/Edi/Middleware/ClientMiddleware.php
- HTTP Basic Auth (
Authorization: Basic base64(username:password)). - Gebruikersnamen en bcrypt-gehashte wachtwoorden staan in de
edi_clients-tabel. - Bij succesvolle auth:
Client-entiteit wordt opgeslagen als request-attribuut;Image::$forceIsAuth = true. - Bij mislukking: HTTP 401 + XML
<error><ErrorCode>99</ErrorCode><ErrorText>Not authorized</ErrorText></error>. - Wachtwoorden worden automatisch geherhasht wanneer
password_needs_rehash()dat aangeeft — de rehash-logica zit inClientRepository::authenticate()(src/Atraxion/Edi/Repository/ClientRepository.php:22–27), niet in de middleware zelf.
XML-formaat (reifen.net / ADC)¶
De sabre/xml-library verzorgt het parsen. Vóór het parsen worden XML-namespace-prefixen weggehaald via preg_replace('/(<\/?)[\w-]+:/', '$1', $xml) — partners zijn vrij in hun namespace-gebruik (src/Atraxion/Edi/Serializer/AbstractXmlSerializer.php:55, methode parseXml()). BodyParsingMiddleware delegeert enkel naar de serializer en bevat de stripping-logica zelf niet.
Inkomende documenttypen (partner → Atraxion)¶
| XML-root | DTO-klasse | Doel |
|---|---|---|
<inquiry> |
InquiryRequest |
Beschikbaarheids- en prijsopvraag |
<order> |
OrderRequest |
Bestelplaatsing |
<order_status> |
OrderStatusResponse |
Opvraag orders in een datumbereik |
<desadv_request> |
DespatchAdviceRequest |
Opvraag verzendinformatie |
Uitgaande antwoordtypen (Atraxion → partner)¶
| XML-root | Doel |
|---|---|
<quote> |
Reactie op inquiry: voorraad, prijs, beschikbaarheidsdatum |
<order_response> |
Bevestiging geplaatste order |
<order_status> |
Orders in het gevraagde datumbereik met ETD's |
<desadv_list> |
Verzendbonnen met trackingnummers |
Artikel-identificatie in de XML¶
Elke orderregel bevat een <ArticleIdentification>-blok. Er kunnen meerdere ID-typen naast elkaar staan:
| XML-tag | Inhoud | AgencyCode |
|---|---|---|
<BuyersArticleID> |
article.Artnr (Atraxion-artikelnummer) |
91 (seller) |
<ManufacturersArticleID> |
Odoo-interne ID | — |
<EANUCCArticleID> |
article.Eancode (EAN-barcode) |
9 |
AgencyCode=92 (assigned by buyer) laat toe dat een partner zijn eigen dealer_code als partij-ID gebruikt.
Verwerkingsflow per documenttype¶
Inquiry (<inquiry> → <quote>)¶
src/Atraxion/Edi/Service/InquiryService.php
- Valideer
BuyerParty+Consignee. - Los per orderregel het artikel op (zie artikel-opzoekvolgorde hieronder).
- Controleer
canBeSold()enisAvailableOnWebshop(). - Optioneel Odoo-relay: als
client->isUseOdooEdiRelay()actief is én de sourcePLATFORM_EDI_INQUIRYbevat, wordt realtime voorraad viaJsonRpcClient::purchaseInquiry()bij Odoo opgevraagd. - Stel beschikbaarheidscode +
ScheduleDetailin (hoeveelheid, leveringsdatum min. +1 dag, netto-eenheidsprijs). - Serialiseer antwoord als
<quote>viaQuoteToXmlSerializer.
Beschikbaarheidscodes:
| Code | Betekenis |
|---|---|
0 |
Geen voorraad |
1 |
Direct leverbaar |
2 |
Buiten assortiment |
3 |
Niet beschikbaar |
Order (<order> → <order_response>)¶
src/Atraxion/Edi/Service/OrderService.php
- Valideer
BuyerParty(facturatiegericht) en optioneelOrderingParty(afleveradres). - Deduplicatie: bestaat er al een order met hetzelfde
reference-veld → foutcode901, geen nieuwe order. - Maak
atx\orders\Order-entiteit aan: reference=OrderReference::create($CustomerReference, orderNumberPrefix).channelafgeleid van$client->getChannel().- Betaalwijze-prioriteit: EDI
PaymentTerms.PaymentMethod→ platform-standaard → klant-standaard. - Leveradres: neutraal/anoniem als
Consigneewel een adres maar geenPartyIDheeft. - Per orderregel: artikel opzoeken, voorraadcontrole (te weinig → fout
974),OrderArticleaanmaken met leverancier, inkoopprijs en ETD. - Order persisteren via
orderManager->store()+ bestelmail. ?debug=dry-runslaat persisteren en versturen over (voor integratietesten).
Order Status (<order_status>)¶
src/Atraxion/Edi/Service/OrderStatusService.php
- Filtert op
BuyerParty, optioneelOrderDateFrom/OrderDateTo. - Geeft maximaal 120 orders terug als
<ReferencedOrder>-elementen met artikelgegevens en bevestigde hoeveelheden/ETD's — geen statusfilter: ook afgehandelde, verzonden en gefactureerde orders worden meegenomen (src/Atraxion/Edi/Service/OrderStatusService.php:50–60).
Despatch Advice (<desadv_request> → <desadv_list>)¶
src/Atraxion/Edi/Service/DespatchAdviceService.php
- Vereist
DespatchFromDate;DespatchToDateoptioneel (maximaal 5 dagen range). - Zoekt orders op channel + datumrange (maximaal 100 orders).
- Retourneert
<DespatchAdvice>-blokken met trackingnummers uitdeliveryNote.getTrackingNumbers().
Artikel-opzoekvolgorde¶
src/Atraxion/Edi/Service/ListingFinderTrait.php lost een artikel op uit ExportListingRepository in de volgende volgorde:
BuyersArticleID→findOneByExportArticle()— matcht oparticle.Artnr; strips_N-leverancierssuffix; vereist exact 5 tekens of een negatief Odoo-ID (begint met-). Een ID van 6 of meer tekens wordt stilzwijgend afgewezen (src/Atraxion/Model/Product/Repository/ExportListingRepository.php:83).ManufacturersArticleID→ zelfde methode (ook Odoo-IDs die beginnen met-).EANUCCArticleID→findOneByExportEan()— matcht oparticle.Eancode.
Uitgehende factuur-XML¶
src/Atraxion/Edi/Serializer/InvoiceToXmlSerializer.php
Los van de inkomende flow. Serialiseert facturen naar XML via het reifen.net-namespace ({http://www.reifen.net}, prefix ew). Documenttype-code 380, functiecode 9.
Datamodel¶
Tabel edi_clients — ORM-mapping: src/Atraxion/Edi/Resources/doctrine/Client.orm.xml
| Kolom | Type | Toelichting |
|---|---|---|
id |
INT AUTO_INCREMENT | PK |
username |
VARCHAR | Basic Auth-gebruikersnaam |
password |
VARCHAR | bcrypt-hash |
useOdooEdiRelay |
BOOL | Schakel realtime Odoo-relay in |
apiKey |
VARCHAR(255) nullable | Optionele X-Api-Key-header |
sources |
JSON | Toegestane bronnen (PLATFORM_EDI_INQUIRY, PLATFORM_EDI_ORDER) |
attachedExport_id |
FK | → article_stock_export.ExportId |
customer_id |
FK | → customer.Klantnr (facturatiegericht) |
channel_id |
FK | → channel.ChannelId |
Foutcodes (reifen.net ADC-protocol)¶
| Code | Betekenis |
|---|---|
0 |
Geen fout |
1 |
Ongeldige orderregel |
99 |
Niet geautoriseerd |
901 |
Dubbel documentnummer |
903 |
Artikel-ID onbekend |
907 |
Consignee-ID onbekend |
910 |
Interne fout |
914 |
Buyer-ID ontbreekt |
915 |
Buyer-ID onbekend |
974 |
Hoeveelheid ongeldig / onvoldoende voorraad |
997 |
Bestelstop (cut-off verstreken) |
CLI-beheer¶
src/Atraxion/Edi/Command/CreateEdiClientCommand.php
./cli.php edi:client:create <username> [--password|-p] [--random|-r] [--api-key|-k]
Maakt een nieuwe EDI-klant aan met gehashed wachtwoord. Klant, channel en exportconfiguratie worden daarna direct in de database toegewezen.
Kruislinks¶
- Integraties (technisch) — EDI ServiceProvider en API-applicatie
- Crons & queue — achtergrondverwerking
- Bestellingen & orderverwerking — wat er na order-aanmaak met een EDI-order gebeurt
- Externe platformen — order-import vanuit verkoopplatformen (ander kanaal, geen EDI)