# 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](/pos/cart/discounts.md) and [Coupons](/coupons/.md).

## How POS Price Overrides Are Stored[​](#how-pos-price-overrides-are-stored "Direct link to 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+)[​](#subtotal-model-v190 "Direct link to 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](https://github.com/wcpos/wiki/blob/main/architecture/decisions/2026-04-08-subtotal-parity.md) for the full history.

## Coupon Interaction Behaviour[​](#coupon-interaction-behaviour "Direct link to Coupon Interaction Behaviour")

Coupon support is a [WCPOS Pro](https://wcpos.com/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[​](#exclude_sale_items-behaviour "Direct link to 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[​](#available-filters "Direct link to Available Filters")

### `woocommerce_pos_item_is_on_sale`[​](#woocommerce_pos_item_is_on_sale "Direct link to 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:**

| Parameter     | Type                    | Description                                                               |
| ------------- | ----------------------- | ------------------------------------------------------------------------- |
| `$is_on_sale` | `bool`                  | Whether the item is considered on sale (default: `price < regular_price`) |
| `$product`    | `WC_Product`            | The product object                                                        |
| `$item`       | `WC_Order_Item_Product` | The order line item                                                       |
| `$pos_data`   | `array`                 | The decoded `_woocommerce_pos_data` JSON                                  |

## REST API Endpoint[​](#rest-api-endpoint "Direct link to 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[​](#receipt-data-exposure "Direct link to 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](https://wcpos.com/support).

## Related[​](#related "Direct link to Related")

* [Cart Discounts](/pos/cart/discounts.md) — user-facing guide to cashier-applied discounts
* [Coupons](/coupons/.md) — user-facing guide to WooCommerce coupons in the POS (Pro)
