Skip to main content

Order handling by client type (Retail, Business, Wholesale)

This document explains how orders are created, paid, processed, and synchronized for different client types. It complements ORDER_PURCHASE_FLOW.md by focusing specifically on client-type nuances.

Contents

  • Client type classifiers and account routing
  • Retail (customers/guests) order handling
  • Business clients (company) order handling
  • Wholesale clients order handling
  • NAV posting and synchronization differences
  • Fulfillment and notifications
  • Troubleshooting and operational notes

Client type classifiers and account routing

  • Client types are determined via ClientAccount::getTypeManager() using classifier CLIENT_TYPE.
    • Retail: guests and logged-in retail users
    • Company: business and wholesale accounts (with discount group type and codes)
  • Pricing is selected via ProductPriceCalculationManager which picks the calculator by client type. The resulting price affects order totals, VAT basis, and discount display.
  • Checkout and post-payment processing are identical at the pipeline level (see ORDER_PURCHASE_FLOW.md), but the inputs (pricing, VAT flags, and identifiers) differ by client type.

Retail (customers/guests) order handling

  • Who: Guests or logged-in retail customers.
  • Price source and tax:
    • Prices are VAT-included (shop price) with optional VAT-free flag in special cases.
    • Discounts follow isPriceGroupDiscountRegular() and include default web-store guest/logged-in discounts and optional assigned discount.
    • Campaign prices for guests/retail are considered and can override the computed price; original reference becomes NAV current price if present.
  • Order creation (CheckoutController::actionIndex → OrderCheckoutService::checkout):
    • Customer identity: for guests, a lightweight customer entry may be created or associated with the order; for logged-in, the ClientAccount is linked.
    • Totals: computed from the retail calculator; order lines contain VAT-included unit prices.
  • Payment (BankController):
    • Most orders go through bank/PSP payment with status transitions NEW → PENDING_PAYMENT → PENDING_PROCESSING.
    • Zero/complimentary orders bypass external bank forms and proceed directly.
  • Post-payment processing (console OrderController::actionProcess):
    • Stock reservation/issuance and digital entitlements are executed normally.
    • Discount display is shown in customer communications (CalculatedPrice includes original reference).
  • NAV identifiers:
    • Customer codes usually map to website-generated retail customer codes (W%-prefixed) where applicable.
    • Orders are posted as web orders with web store NAV code.

Business clients (company) order handling

  • Who: Company accounts using business terms but not necessarily wholesale price lists.
  • Price source and tax:
    • Base price is max(original, shop). If product’s default discount (shop vs original) is better than assigned/custom, shop price is used; otherwise the highest applicable discount is applied when regular discounts are allowed.
    • Optional VAT-free mode can be applied depending on account settings.
    • Company campaign prices (CLIENT_TYPE=COMPANY) can override final price; original reference becomes NAV current/shop price for display.
  • Order creation:
    • Order is linked to the company ClientAccount; discount group type id and product discount group codes may affect applied prices and later NAV mapping.
    • Lines can still be VAT-included or VAT-free based on the account (the calculators support both). Ensure tax flags on the order match what was used for pricing.
  • Payment:
    • Can be bank/PSP like retail, or alternative terms if configured (e.g., invoice terms). In bank mode, status transitions mirror retail.
  • Post-payment processing:
    • Same operational flow as retail (stock, entitlements, emails) but discount visuals are typically suppressed for company/wholesale contexts.
  • NAV identifiers:
    • Company customer codes typically start with W% (website-originated) but map to the company master in NAV.
    • Discount group type id (e.g., OMA) influences discounts and may be relevant for NAV posting/validation.

Wholesale clients order handling

  • Who: Company accounts using wholesale price lists and/or custom per-product wholesale prices.
  • Price source and tax:
    • Primary source is wholesalePrice (VAT-excluded) or a per-product custom wholesale price from WholesaleClientPriceRepository based on the company group code.
    • If wholesale/custom price is 0/missing, falls back to shop price.
    • VAT handling:
      • Wholesale/custom prices are VAT-excluded. If order/pricing is VAT-inclusive, VAT is added (price * (1+VAT)). If VAT-free pricing is required, VAT is kept excluded. When falling back to shop or campaign (VAT-included) but an order must be VAT-free, the VAT part is removed.
    • Discounts: Highest of wholesale custom discount (by discount group) or company assigned percent, only if regular discounts apply.
    • Campaign: COMPANY campaign price can override and is then normalized for VAT if needed.
  • Order creation:
    • Order is linked with company account and company_price_group_code (used for custom wholesale prices).
    • Order lines reflect VAT-excluded unit prices if VAT-free mode is active, otherwise VAT-included (depending on configuration at checkout).
  • Payment:
    • Wholesale often uses invoice/terms; however, the bank flow is supported too. Status transitions are the same.
  • Post-payment processing:
    • Same operational flow (stock, entitlements, postbacks). For wholesale/business, CalculatedPrice’s original price is set to 0 in wholesale to suppress discount/strike-through UI.
  • NAV identifiers:
    • Orders are posted to NAV as website-originated company orders. Company discount group type and codes (e.g., OMA, group code strings) may control pricing/validation within NAV.

NAV posting and synchronization differences

  • Posting after processing: console order/process invokes common\synchronizations\nav\post\order\OrderPost to retry/ensure posting to NAV for processed orders.
  • Periodic reconciliation: common\synchronizations\nav\sync\OrderSync merges NAV’s posted and open orders into local temp tables and updates RR orders:
    • Keeps totals and line amounts in sync for all client types.
    • Filters by website-originated codes (WT%, WN%, W%).
    • Optional scoping by webStoreNavCode.
  • Expect that business/wholesale amounts may be VAT-excluded at line level depending on the configuration; NAV becomes the source of truth after posting.

Fulfillment and notifications

  • Regardless of client type, after payment authorization/capture:
    • OrderPurchaseManager::process() allocates inventory and issues entitlements.
    • EmailNotificationHandler sends confirmations. For company/wholesale, templates typically omit consumer-oriented discount visuals.
    • PostBackHandler triggers external system hooks where configured.

Troubleshooting and operational notes

  • If prices/discounts look wrong on a company/wholesale order:
    • Verify discount group type id and product group_discount_code.
    • Check assigned discount percent via ClientAccount->getDiscountManager()->getAssignedDiscountPercent().
    • For wholesale: verify company_price_group_code and custom prices via WholesaleClientPriceRepository.
    • Confirm isPriceGroupDiscountRegular() is true; otherwise discounts are not applied.
    • Inspect whether a campaign price (CLIENT_TYPE null/RETAIL/COMPANY) is overriding the computation.
  • VAT mismatches between UI and NAV:
    • Ensure the calculator’s VAT-free flag matched the order’s tax settings at checkout.
    • Remember that wholesale/custom prices start VAT-excluded; falling back to shop/campaign may require VAT normalization.
  • Payment edge cases:
    • Duplicate payment attempts are blocked if the order is no longer NEW.
    • Zero-amount orders bypass bank/PSP and go straight to processing.

Related documents and classes

  • ORDER_PURCHASE_FLOW.md — end-to-end checkout and processing timeline.
  • PRICING.md — pricing calculators and rules per client type.
  • console/controllers/OrderController.php — cron processing entry point.
  • common/synchronizations/nav/sync/OrderSync.php — reconciliation with NAV.
  • api/prices/WholesaleClientPriceRepository.php — wholesale custom prices.
  • api/prices/ProductCampaignRepository.php — web campaign participation checks.