Ga naar inhoud

Bestellingen & orderverwerking

Een bestelling in de Atraxion-webshop doorloopt een vaste lifecycle van aanmaak tot facturatie. De centrale klasse is Order (_core/model/classes/atx/orders/Order.php); de verwerking loopt via OrderService (_core/model/classes/atx/orders/OrderService.php) en de koppeling met het ERP via Odoo\Service\UploadOrders (src/Odoo/).


Orderstatussen

Status Constante Waarde Betekenis
In afwachting Order::STATUS_PENDING 99 Bestelling aangemaakt, wacht op betaling (Pay.nl)
Nieuw Order::STATUS_NEW 0 Bestelling bevestigd, klaar voor verwerking
Reservering Order::STATUS_RESERVATION 1 Artikel gereserveerd bij leverancier
Gefactureerd Order::STATUS_INVOICE 2 Verzonden en gefactureerd vanuit Odoo
Geannuleerd Order::STATUS_CANCELLED 3 Bestelling geannuleerd
Voltooid Order::STATUS_COMPLETE 10 Order volledig afgehandeld

Referentie: Order.php:44-55.

Stroomschema

flowchart LR
    A[Bestelling aangemaakt] --> B{Betaalmethode}
    B -->|Pay.nl| P[STATUS_PENDING\nbetaling via gateway]
    B -->|Op rekening / domiciliëring| N[STATUS_NEW]
    P -->|Betaling geslaagd\ncallback| N
    P -->|Betaling mislukt / verlopen| C[STATUS_CANCELLED]
    N --> R[STATUS_RESERVATION\nreservering leverancier]
    R --> I[STATUS_INVOICE\nverzonden + factuur Odoo]
    I --> D[STATUS_COMPLETE]
    N --> C

Order lifecycle

1. Aanmaak

Nadat de klant de checkout bevestigt, maakt CartController::doCreateOrder() (CartController.php:1460) een Order-object aan met:

  • Klantgegevens (Klantnr, Username)
  • Orderartikelen (OrderArticle[]) met prijs, leverancier, leveringsdagen
  • Leveringsadres(sen) (OrderAddress[])
  • Transportkosten, betalingskosten
  • Orderhash (deduplicatie via twee lagen, CartController.php:1302–1351):
  • Als $_SESSION['order_id'] aanwezig is: de bijbehorende order wordt opgehaald. Als de hash overeenkomt → hergebruikt. Als de hash niet overeenkomt → de bestaande order wordt geannuleerd en de sessie wordt gewist.
  • Als er geen sessie-order is: hash-gebaseerde lookup. Als er een STATUS_PENDING-order met dezelfde hash bestaat → hergebruikt in plaats van een nieuwe aan te maken.

De order en de bijbehorende artikelen en adressen worden via OrderDAO, OrderArticleDAO en OrderAddressDAO opgeslagen.

2. Bevestiging & e-mail

OrderService::sendOrderMail() (OrderService.php:227) verstuurt:

  • Interne bevestiging — e-mail naar Atraxion-medewerkers met inkoopprijs-referentie (createInternalOrderMail)
  • Klantbevestiging — e-mail naar de klant op basis van de orderconfirmation-template

3. Doorsturen naar Odoo ERP

OrderService::sendOrderToErp() (OrderService.php:1126) roept UploadOrders::uploadSingle() (OrderService.php:1150) aan om de order te synchroniseren met het Odoo ERP-systeem. Dit gebeurt alleen in productie en alleen als zowel de klant als de logingebruiker geaccepteerd zijn (OrderService.php:1149):

if ($customer->isAccepted() && (!$login || $login->isAccepted()))

Een geaccepteerde klant wiens logingebruiker nog niet geaccepteerd is, triggert dus geen ERP-upload.

Odoo ontvangt de order als verkooporder (SO) en verwerkt de leveranciersinkoop, de reservering bij de leverancier en de facturatie. Odoo-ID's worden teruggeschreven naar Order::OdooId en Order::SaleOrderName.

Zie Integraties voor de technische details van de Odoo-koppeling.

4. Facturatie

Wanneer een bestelling door Odoo wordt gefactureerd en verzonden, wordt de status bijgewerkt naar STATUS_INVOICE. De bijbehorende factuurgegevens (Invoice) en zendingstracking (ShipmentTracking) worden teruggekoppeld vanuit Odoo naar de webshop-database en zijn zichtbaar in de klantomgeving.


Gesplitste bestellingen

Splitsing per voertuigchassis ("te monteren")

Wanneer de winkelwagen artikelen bevat die te monteren zijn bij een specifiek voertuigchassis, splitst CartController::splitCart() de bestelling op per chassis-nummer. Elk deelorder wordt afzonderlijk opgeslagen, bevestigd en naar Odoo gestuurd. De klant ontvangt bevestigingen per deelorder.

Splitsing op productcategorie

Voor bepaalde klanten (gemarkeerd via CustomerTag) kan de bestelling worden gesplitst op productcategorie. OrderSplittingService::shouldSplitOrdersByCategory() controleert dit en stuurt zo nodig door naar splitCartByProductCategories(). Bij categorie-splitsing wordt de factuur op een andere facturatierelatie gelegd (InvoiceCustomer).


Orderdocumenten

Klanten kunnen PDF-bestanden koppelen aan een order via het document-portaal (DocumentPortalController, route /order-documents/{hash}/{token}/upload). De upload-aanroep binnen DocumentPortalController::uploadDocument() zet uploadedBy altijd op 'customer' hardgecodeerd (DocumentPortalController.php:88) — er is geen aparte admin-upload. Atraxion-medewerkers kunnen de portaallink vanuit de backoffice delen of bekijken, maar uploaden via diezelfde portal-URL als de klant. De verdere verwerking loopt via de OrderDocumentService (src/Atraxion/Module/OrderDocuments/Service/OrderDocumentService.php).

Upload & opslag

Bij upload (OrderDocumentService::uploadDocument(), OrderDocumentService.php:41):

  • Validatie: alleen PDF, maximaal 5 MB, extensie en MIME-type worden gecontroleerd
  • Bestand wordt opgeslagen in APP_STATIC_PATH/order_documents/{jaar}/{maand}/{orderId}/
  • Bestandsnaam is uniek ({orderId}_{random}_{timestamp}.pdf)
  • Een OrderDocument-entity (src/Atraxion/Module/OrderDocuments/Model/OrderDocument.php) wordt aangemaakt in de database

Virusscan (VirusTotal)

Na upload wordt het bestand asynchroon gescand via de VirusTotal-API (VirusScannerInterface, src/VirusTotal/). Het scanproces:

  1. virusScanner->scanFile($filePath) dient het bestand in en geeft een analysisId terug
  2. De OrderDocument krijgt status VIRUS_SCAN_STATUS_SCANNING
  3. Bij voltooiing wordt de status bijgewerkt naar VIRUS_SCAN_STATUS_CLEAN, VIRUS_SCAN_STATUS_INFECTED of VIRUS_SCAN_STATUS_ERROR
  4. Geïnfecteerde bestanden worden in quarantaine geplaatst (isQuarantined = true)

Een document is alleen toegankelijk als het de status clean heeft en niet in quarantaine zit (OrderDocument::isSafeToAccess(), OrderDocument.php:277).

De viruscan mislukt niet de upload: als de scan-API niet beschikbaar is, wordt het document toch opgeslagen maar blijft de status op pending (OrderDocumentService.php:105-111).

Odoo-upload

Na een geslaagde viruscan worden documenten asynchroon doorgestuurd naar Odoo — niet synchroon bij de upload zelf. Het CLI-command order-documents:check-virus-scans (CheckVirusScanResultsCommand.php) pollt de VirusTotal-resultaten en start de Odoo-upload bij een schone scan. Bij fouten herprobeert order-documents:retry-odoo-uploads (RetryOdooUploadsCommand.php) de upload. Beide commands draaien via cron. Zie ook Crons & queue.

Het OrderDocument houdt bij:

  • odooUploadStatus: pending, uploaded of failed
  • odooSaleOrderAttachmentId — attachment-ID bij de Odoo-verkooporder
  • odooStockPickingAttachmentId — attachment-ID bij de Odoo-pakbon

Via OdooDocumentUploadService (src/Atraxion/Module/OrderDocuments/Service/OdooDocumentUploadService.php) worden de PDF's als bijlage aan de relevante Odoo-objecten gekoppeld.

Orderstatussen van documenten

Status Constante Betekenis
In afwachting VIRUS_SCAN_STATUS_PENDING Nog niet gescand
Aan het scannen VIRUS_SCAN_STATUS_SCANNING Scan in uitvoering bij VirusTotal
Veilig VIRUS_SCAN_STATUS_CLEAN Geen malware gevonden
Besmet VIRUS_SCAN_STATUS_INFECTED Malware gedetecteerd, in quarantaine
Fout VIRUS_SCAN_STATUS_ERROR Scan kon niet worden voltooid

Referentie: OrderDocument.php:15-19.


Orderreferenties en deduplicatie

Elke order heeft een OrderHash die berekend wordt op basis van de winkelwageninhoud. Bij aanmaak gebruikt de checkout een twee-laagse deduplicatiecontrole (CartController.php:1302–1351):

  1. Sessie-laag: als $_SESSION['order_id'] bestaat, wordt die order opgehaald. Als de hash overeenkomt → hergebruikt. Als de hash niet overeenkomt → de bestaande pending order wordt geannuleerd en de sessie wordt gewist.
  2. Hash-laag: als er geen sessie-order is, wordt gezocht op OrderHash. Als er een STATUS_PENDING-order met dezelfde hash bestaat (bijv. bij dubbelklikken of pagina-verversing) → hergebruikt in plaats van een nieuwe aan te maken.

Leveringsopties

Bij aanmaak van een order kiest de klant een leveringsvoorkeur:

Optie Constante Waarde
Zo snel mogelijk Order::DELIVERY_ASAP 1
Alles samen Order::DELIVERY_TOGETHER 2
Vaste leveringsdatum Order::DELIVERY_FIXED_DELIVERYDAY 3
Neutraal (dropship) Order::DELIVERY_NEUTRAL 4

Referentie: Order.php:39-42.


Gerelateerde pagina's