Skip to content

SubmitOrder (Web → NAV)

Legacy system

This page documents the current (legacy) SubmitOrder integration in Zwerfkei Old (CodeIgniter), reverse-engineered directly from that codebase. It is reference material for building the equivalent flow on the new platform — see ZWE-392.

SubmitOrder is the SOAP operation on the NAV WSDL (config key wdslNav, called via Connection::requestCallWdslNav()) that pushes an order (or a return) from the webshop to NAV.

Not the LS Retail WSDL

This is not the LS Retail WSDL (wsdlLs) — that's a separate endpoint with its own operation list, and SubmitOrder does not appear if you query the LS WSDL. Order.php (requestCallWdsl()) explicitly returns 'requestCallWdslNav', which in Connection.php selects the wdslNav config, not wsdlLs.


Call flow

Order::sendOrderToNav() / Order::createNegativeOrderToNav()
  → creates a K3_synchronization record (model='Order', action=positive|negative, status=false)

K3_synchronization::sendToNav()  (cronjob)
  → GET_LOCK('k3_sync') + loops over all open syncs (status=false)
  → _makeK3retailCall()
       action=positive → new K3retail\Call\Order_positive($this)
       action=negative → new K3retail\Call\Order_negative($this)

Order_positive / Order_negative (extends K3retail\Call\Order)
  → _checkOrder()                     validation (customer/country/navCustomerId/orderproducts)
  → _createOrderRequestObject()       builds K3retail\Classes\Order (NavOrder + NavCustomer + NavAddress)
  → wraps in K3retail\Classes\SubmitOrder->order
  → callToNav()  →  Connection::requestCallWdslNav('SubmitOrder', $request)
  → response() stores sync->status=true, sync->date_send=time()

Files:

File Role
application/models/order/Order.php sendOrderToNav() / createNegativeOrderToNav() — creates the K3_synchronization job
application/models/K3_synchronization.php Queue/dispatcher (sendToNav(), _makeK3retailCall())
application/components/k3retail/Order.php Base class: validation + shared line/customer/address helpers
application/components/k3retail/Order_positive.php Builds a normal order (SubmitOrder, action=positive)
application/components/k3retail/Order_negative.php Builds a return order (SubmitOrder, action=negative)
application/components/k3retail/classes/SubmitOrder.php SOAP request wrapper ({ order: NavOrder-graph })
application/components/k3retail/classes/Order.php Container: NavOrder + NavCustomer + NavAddress
application/components/k3retail/classes/NavOrder.php Order header/totals + NavOrderLine[]
application/components/k3retail/classes/NavOrderLine.php Order line
application/components/k3retail/classes/NavCustomer.php Customer
application/components/k3retail/classes/NavAddress.php Shipping address
application/components/k3retail/classes/NavOrderStatus.php Status constants (OrderStatus/ShippingStatus/PaymentStatus)

Request envelope

K3retail\Classes\SubmitOrder {
    order: K3retail\Classes\Order {
        NavOrder:    NavOrder      // header + lines
        NavCustomer: NavCustomer
        NavAddress:  NavAddress    // = shipping address (always the fixed Zwerfkei warehouse address)
    }
}

_createOrderRequestObject() (Order.php:111) throws an error if NavOrder, NavCustomer, NavAddress or NavOrder->Id are empty — the call is not sent in that case.


Field Type Origin / notes
Id int K3_synchronization->nav_id — own incrementing counter per model (K3_synchronization.php:182), not the website order number
PurchaseOrderNumber string positive: Ymd of sync->date_created + ordernr. Return: same + "R" + ordernr + "_" + serial_number
StoreId int constant Order::StoreId = 1
CustomerId int country->navCustomerId (set per country on Customer_country)
BillingAddressId int constant Order::BillingAddressId = 1
ShippingAddressId int constant Order::ShippingAddressId = 1
CustomerTaxDisplayType int always 1 (incl. VAT)
OrderStatusId int always NavOrderStatus::orderStatus_complete (30)
ShippingStatusId int always NavOrderStatus::shipmentStatus_shipped (40)
PaymentStatusId int always NavOrderStatus::paymentStatus_paid (30)
PaymentMethodName string order->payment_provider (code comment: "nothing happens when you change this value")
ShippingMethod string always 'POSTNL' (hardcoded)
OrderSubtotalInclTax / ExclTax decimal sum of order lines (_countOrderSubInclFromOrderline / _countOrderSubExFromOrderline)
OrderSubtotalDiscInclTax / ExclTax decimal subtotal minus line discount, or 0 if no discount
OrderShippingInclTax / ExclTax decimal always 0 (shipping cost goes as a separate order line, see below)
PaymentFeeInclTax / ExclTax decimal always 0 (same, as an order line)
OrderTax decimal difference between discount incl/excl, or subtotal incl−excl if no discount
OrderDiscount decimal always 0 ("We don't need these")
OrderTotal decimal positive: order->totaal minus paid giftcard/voucher amounts. Return: subtotal incl − discount incl
RefundedAmount decimal always 0
RewardPointsWereAdded bool always false
CustomerLanguageId int always 0
CreatedOnUtc datetime positive: order->datum as ISO-8601 (DateTime::ATOM). Return: sync->date_created as Y-m-d\TH:i:s
NavOrderLine NavOrderLine[] see below
NavOrderAttribute — defined on the class, not populated in the current flow

Status constants (NavOrderStatus.php)

Group Value Meaning
OrderStatus 10/20/30/40 Pending / Processing / Complete / Cancelled
ShippingStatus 10/20/30/40/50 Not Required / Not Yet Shipped / Partially Shipped / Shipped / Delivered
PaymentStatus 10/20/30/40/50/60 Pending / Authorized / Paid / Partially Refunded / Refunded / Voided

The current implementation always sends Complete/Shipped/Paid, regardless of the actual order status in the webshop.


Field Type Origin
Id int/string orderproduct->id (return: + sync->serial_number appended)
OrderId int sync->nav_id (same as NavOrder->Id)
ProductId int defined on the class, not set by the builders
ItemNo string K3 item number (orderproduct->Article)
VariantCode string k3_article_variant->Code
Quantity decimal orderproduct->aantal (return/negative lines with - prefix)
UnitPriceInclTax decimal orderproduct->prijs
UnitPriceExclTax decimal prijs / VATCounted, 2 decimals
PriceInclTax decimal aantal * prijs (always returned positive; return with - prefix)
PriceExclTax decimal aantal * (prijs / VATCounted) (same)
DiscountAmountInclTax decimal bundle discount, or aantal*prijs − aantal*aanbieding, else 0
DiscountAmountExclTax decimal same, excl. VAT
ItemWeight decimal always 0

Special order lines

Beyond regular product lines, extra NavOrderLine entries are conditionally added to NavOrder->NavOrderLine:

Line Trigger Item
Shipping/payment cost order->verzendkosten and/or order->betaalkosten > 0 Order::shippingArticleNo (223000001) / GTIN 2000000443508
Giftcard discount (return) Payment::getTotalDiscountGiftByReference() variant on onlinekadobon2_gtin (config)
Voucher (return) Payment::getTotalDiscountByReference() variant on goedmaakbon (config)
Negative line (aantal_gepakt < 0) in _createOrderLinesObject() own negative-line calculation

For Order_negative (return flow), return costs, giftcard refunds and voucher refunds also come in as separate lines via Order_service records (type = shipment/payment/retour/giftcard/discount, refund=1).


Field Origin
Id country->navCustomerId
CustomerNo country->navCustomerNo
ContactNo country->navContactNo
Password / PasswordFormat / PasswordSalt defined on the class, not populated for SubmitOrder

Source: Customer_country of the customer on the order (order->customer->country). Without navCustomerId, _checkOrder() fails.


Hardcoded to the Zwerfkei warehouse, not the customer's address:

Id              = settings.shippingAddressId   // constant 1
Code            = 'VERZ-0001'
Company         = 'Zwerfkei Buitensport b.v.'
FirstName       = 'Webshop'
LastName        = 'orders'
Address         = 'Jaap Bijzerweg 10'
ZipPostalCode   = '3446CR'
City            = 'WOERDEN'
CountryCode     = 'NL'
CountryId       = 0
StateProvinceId = 0

(Email, Address2, PhoneNumber, FaxNumber exist on the class but are not populated.)


Precondition checks (_checkOrder())

The call is not sent (and the sync gets status K3_synchronization::error + an admin email) if:

  • sync->RelatedModel is not Order, the sync already has a status, or POS is not "started" (POSStartStatus::getStatus())
  • the order has no customer
  • the customer has no country
  • country->navCustomerId is empty
  • settings (derived from the country) could not be built
  • the order has no orderproducts

Response

SubmitOrder_Result is a genuinely empty struct — confirmed against the live WSDL (see zwerfkei-api PR #14, ZWE-425). NAV returns no confirmation id, order number, or any field at all on success. This is why the new backend generates and stores its own order id as the reference rather than relying on anything NAV sends back — see ZWE-503 and Order Push Flow Demo.

Error scenarios

A rejected/invalid submission (e.g. a VariantCode exceeding NAV's 10-character limit) returns a SOAP fault, not a populated SubmitOrder_Result — there is no NAV-side "rejected" response shape to document beyond the fault itself. Confirmed live in ZWE-425, which catches this fault and logs it against the correlating backend order.


What you need to build a SubmitOrder call yourself

  1. Connection: wsdlLs endpoint, basic auth (K3retail_gateway.username/password), via Connection::requestCallWdslNav('SubmitOrder', $payload).
  2. nav_id: own incrementing counter (per model) from k3_synchronization.nav_id — not the website order number. Must be unique towards NAV.
  3. Customer mapping: navCustomerId / navCustomerNo / navContactNo per country (Customer_country) — without this mapping nothing can be sent.
  4. Lines: each Orderproduct → ItemNo (K3 item number) + VariantCode (from K3_article_variant) + incl./excl. VAT prices + incl./excl. discount. Note: PriceInclTax/PriceExclTax are always sent positive for a regular line and with a "-" string prefix for return/negative lines (not a real negative number, but a minus-prefixed string — that's apparently how NAV expects it).
  5. Fixed values not coming from the order itself: StoreId=1, BillingAddressId=1, ShippingAddressId=1, CustomerTaxDisplayType=1, ShippingMethod='POSTNL', and the fixed shipping address above.
  6. Status fields are always sent as "complete" (Complete/Shipped/Paid) — there is no mapping to intermediate webshop order statuses.
  7. Shipping/payment cost and giftcards/vouchers are not fields on the order, but separate synthetic order lines with a fixed ItemNo (223000001) or the giftcard/voucher config items.

Legacy Integration K3 Data Models