Ga naar hoofdinhoud
Versie: 1.x

POS Discount Technical Reference

This page documents how WCPOS handles cashier-applied line-item price overrides — how they're stored, how they interact with WooCommerce coupons, and what filters are available. For user-facing documentation, see Cart Discounts and Coupons.

How POS Price Overrides Are Stored

When a cashier sets or changes a line item's price in the POS, the per-unit prices are stored in the _woocommerce_pos_data line item meta as JSON:

{
"price": "16.00",
"regular_price": "18.00",
"tax_status": "taxable"
}

For misc (custom) products, this meta can also carry virtual, downloadable, and categories fields used during coupon validation.

This meta is the authoritative source of per-unit prices for the line. WooCommerce's stored subtotal on the line item is derived from price * qty (with tax extraction and rounding applied). The stored total may be lower when coupon discounts are applied, so for per-unit precision you should always read _woocommerce_pos_data.price and regular_price.

Subtotal Model (v1.9.0+)

As of v1.9.0, WCPOS aligns with WooCommerce's native sale-price semantics:

  • line_item.subtotal = price * qty where price is the POS price (after any override).
  • line_item.total = subtotal - coupon_discount_for_line.
  • order.discount_total reflects coupon discounts only, not POS price overrides.

This matches how WooCommerce treats a product on sale: the sale_price becomes the subtotal, and discount_total is reserved for coupons. There is no separate "POS discount" line on the order.

Migration from pre-1.9.0

Earlier versions sent subtotal = regular_price * qty, which caused WooCommerce to compute discount_total = subtotal - total and include POS price overrides as a discount. This was changed because it conflicted with coupon math: recalculate_coupons() uses subtotal as the base price, so coupons would calculate against the original price and produce incorrect totals.

The previous server-side workaround — a woocommerce_order_item_get_subtotal filter active during recalculate_coupons() — was removed in v1.9.0. See the ADR for the full history.

Coupon Interaction Behaviour

Coupon support is a WCPOS Pro feature. When a coupon is applied to an order that contains POS-discounted items:

  1. The coupon calculates against the POS-discounted price (the value in _woocommerce_pos_data.price), not the original regular_price.
  2. When a coupon is removed, the line item returns to its POS-discounted price.

exclude_sale_items Behaviour

POS-discounted items are treated as "on sale" by WooCommerce when _woocommerce_pos_data.price < regular_price. Coupons with exclude_sale_items enabled will therefore skip them, consistent with how WooCommerce treats regular sale prices.

If you need different behaviour, see the woocommerce_pos_item_is_on_sale filter below.

Available Filters

woocommerce_pos_item_is_on_sale

Override whether a POS-discounted item is considered "on sale" for coupon validation purposes.

add_filter( 'woocommerce_pos_item_is_on_sale', function ( $is_on_sale, $product, $item, $pos_data ) {
// Allow coupons with exclude_sale_items to apply to POS-discounted items
return false;
}, 10, 4 );

Parameters:

ParameterTypeDescription
$is_on_saleboolWhether the item is considered on sale (default: price < regular_price)
$productWC_ProductThe product object
$itemWC_Order_Item_ProductThe order line item
$pos_dataarrayThe decoded _woocommerce_pos_data JSON

REST API Endpoint

The coupons REST endpoint lives in the free plugin at /wp-json/wcpos/v1/coupons so that the POS app never receives a 404 when querying coupons — even on sites without WCPOS Pro installed. The user-facing coupon feature itself, however, requires Pro.

The controller extends WC_REST_Coupons_Controller with POS-specific additions:

  • UUID handling for offline-first sync
  • access_woocommerce_pos capability for permission checks
  • Optimised bulk-ID query path when posts_per_page=-1 and fields=id (or fields=id,date_modified_gmt) are requested

Receipt Data Exposure

The receipt data builder (Receipt_Data_Builder) exposes:

  • lines[].discounts, lines[].discounts_incl, lines[].discounts_excl — per-line discount amount, computed as subtotal - total. From v1.9.0 this reflects coupon discounts only for POS price overrides, since subtotal === total when no coupon is applied.
  • totals.discount_total, totals.discount_total_incl, totals.discount_total_excl — order-level coupon discount total from WC_Order::get_discount_total().
  • discounts[] — array of coupon line items with label, code, and total.

The line item's underscore-prefixed meta (including _woocommerce_pos_data) is filtered out of lines[].meta by Receipt_Data_Builder::get_item_meta_pairs(), so regular_price is not directly available to templates. If you need to surface the difference between regular and POS price on receipts, request it via support.

  • Cart Discounts — user-facing guide to cashier-applied discounts
  • Coupons — user-facing guide to WooCommerce coupons in the POS (Pro)