Ga naar inhoud

Verzending

De transportkosten van een bestelling worden bepaald via Transsmart, dat in deze shop via de Odoo-middleware wordt aangesproken. De prijs komt primair uit Odoo; lukt dat niet, dan valt de berekening terug op de DB-tabel order_transport_price. Deze pagina beschrijft hoe de transportprijs tot stand komt, hoe een verzending wordt geboekt, en welk gedrag de huidige code feitelijk vertoont rond niet-leverbare landen.

Transportkeuze in de checkout

Elke klant heeft een standaard-Transport op zijn entiteit (customer->getTransport()). In de checkout toont de actie transportDelivery (_core/controller/classes/atx/customers/cart/CartController.php rond regel 780) twee keuzes: het standaardtransport van de klant of afhalen ('00'). De gekozen transport wordt bij createOrder gevalideerd tegen ['00', customer->getTransport()->getId()].

Of een transport via Transsmart loopt, hangt af van Transport::isTranssmart() (_core/model/classes/atx/orders/Transport.php:549), dat controleert of de OdooId gelijk is aan '-129'. Is dat het geval, dan loopt de prijsbepaling via de Transsmart-stack; anders rechtstreeks via de DB-tabel.

Transportprijs bepalen

Het startpunt is Cart::calculateTransportCost() (_core/model/classes/atx/customers/cart/Cart.php:644), dat delegeert naar ShippingRateCalculatorManager (src/Atraxion/Shipping/ShippingRateCalculatorManager.php). Die manager bevat een circuit breaker en routeert tussen Transsmart en de DB-fallback:

flowchart TD
    A["Cart::calculateTransportCost()"] --> M{ShippingRateCalculatorManager}
    M -->|transport niet Transsmart| DB[DefaultShippingRateCalculator]
    M -->|circuit open| DB
    M -->|anders| C[CachedShippingRateCalculator session-cache]
    C --> H["HasFreeShippingCalculator snelle check 2s"]
    H -->|prijs bekend| R1[ShippingRate = max colli/pallet]
    H -->|timeout of prijs onbekend| D["TranssmartShippingRateCalculator diepe check 15s"]
    D --> R2[ShippingRate uit NShift shipment lines]
    H -.->|prijs 0 en Never free shipping| EX[TransportNotAvailableException]
    D -.->|prijs 0 en warning_message| CB[circuit opent en DB-fallback]
    DB --> DBT[(order_transport_price)]

Snelle check (HasFreeShippingCalculator)

HasFreeShippingCalculator (src/Transsmart/Service/HasFreeShippingCalculator.php) bouwt een TransportSellingConfig-DTO uit de winkelwagen, het land, het channel en de transport-/betaalinfo, en roept Odoo aan via DynAppsAPI::getTransportSellingConfig() met een timeout van 2 seconden (endpoint /api/v1/get_transport_selling_config).

Slaagt de check en is transport_price_unknown == false (HasFreeShippingCalculator.php:97), dan is de prijs max(unit_price_colli, unit_price_pallet) (:98). Time-out de check, of geeft Odoo transport_price_unknown == true terug, dan valt de berekening door naar de diepe check (:112).

Diepe check (TranssmartShippingRateCalculator)

De fallback (src/Transsmart/Service/TranssmartShippingRateCalculator.php) bouwt een volledige OrderDTO en roept DynAppsAPI::getNShiftShipmentLines() aan met een timeout van 15 seconden (endpoint /api/v1/get_nshift_shipment_lines). Het antwoord wordt via NShiftShipmentLinesResponse::toShippingRate() omgezet naar een ShippingRate.

DB-fallback (order_transport_price)

Wanneer het transport niet via Transsmart loopt, of wanneer de circuit breaker open staat, gebruikt DefaultShippingRateCalculator (src/Atraxion/Shipping/ShippingRateCalculator.php) de DB-tabel via CountryColliPriceDAO::getByCountryIsoCodeAndTransport() (_core/model/dbclasses/atx/orders/CountryColliPriceDAO.php:197).

De tabel order_transport_price (entiteit _core/model/classes/atx/orders/CountryColliPrice.php) bevat onder meer countryIsoCode, franco (grens vanaf wanneer transport gratis is), buyPrice, sellPrice, vlaggen forCompany/sepa/vip, een transport-FK, weightBasedPrices (JSON) en een is_default-rij per land. De lookup gebeurt op (countryIsoCode, transport-id, sepa, forCompany) met terugval op de is_default-rij van het land.

Geen harde landen-gate

De huidige code blokkeert een land niet op verzendniveau, behalve in één precieze situatie. Het feitelijke gedrag:

  • Op het snelle-checkpad wordt warning_message uit TransportSellingConfigResult wél gedeserialiseerd (src/Transsmart/DataTransfer/TransportSellingConfigResult.php:15), maar nooit gelezen door enige aanroeper. Een niet-nul prijs uit Odoo wordt altijd geaccepteerd, ongeacht een eventuele waarschuwing.
  • Een prijs van €0 wordt geaccepteerd en als gratis verzending behandeld. De enige uitzondering is de tegenstrijdige combinatie prijs <= 0 én xx_free_shipping_comment === 'Never free shipping'; alleen dan wordt een TransportNotAvailableException gegooid (HasFreeShippingCalculator.php:103).
  • Op het diepe-checkpad gooit NShiftShipmentLinesResponse::toShippingRate() alleen een exception als drie condities tegelijk gelden: sales_price === 0, én warning_message is aanwezig (isset), én mb_strlen(warning_message) > 1 (NShiftShipmentLinesResponse.php:27). Een lege of éénkarakter-bericht triggert de exception dus niet. Die exception blokkeert de bestelling niet — het opent de circuit breaker en schakelt over op de DB-prijs.

Concreet: voor een land als Noorwegen kan Odoo €0 met een waarschuwing teruggeven, maar tenzij de comment exact 'Never free shipping' is, vat de code dit op als gratis verzending en gaat de bestelling door. Het Transsmart-pad vertrouwt dus de Odoo-prijs; het land zelf wordt niet als gate gebruikt.

Verzending boeken na de bestelling

Het boeken van de verzending verloopt synchroon binnen het webrequest, zonder queue:

  1. CartController::doFinishOrder() (CartController.php rond regel 1688) roept OrderService::sendOrderToErp() aan (_core/model/classes/atx/orders/OrderService.php:1125).
  2. sendOrderToErp() roept UploadOrders::uploadSingle() aan (OrderService.php:1149).
  3. UploadOrders (src/Odoo/Service/UploadOrders.php) bouwt een OrderDTO met onder meer xx_transsmart_costcenter_id en delivery_method, en voegt eventueel gecachete NShift-verzendregels uit Order::getNShiftShipmentLines() toe.
  4. De DTO wordt naar de Odoo-middleware gePОST; Odoo triggert de NShift/Transsmart-boeking aan zijn kant.

Het kostencentrum (xx_transsmart_costcenter_id) volgt een prioriteitsvolgorde: het transport van de channel-eigenaar → de verzendmethode van het platform → het transport van de klant → de harde fallback 22.

Zie ook