メインコンテンツにスキップ
バージョン: 2.x

Discount Records

To take money off a sale, pick one of the three ways below. Cart Discounts is the cashier's main page for all three. Developers and report readers can go straight to Where each discount is recorded.

Take money off at the till​

Whether they combine on one order depends on the coupon's rules. Add Discount can't be combined with a coupon marked Individual use only.

Change one item's price​

  1. Tap the price on a line. A keypad opens.
  2. Press a percentage key (−5%, −10%, −15%), or type the new price.
  3. Press the tick (✓) to finish, or Done on a phone. The line total changes to the quantity × the new price.
The price keypad open on a $45.00 line with −10% pressed: the new price $40.50 with $45.00 −10% above it, the three percentage keys, the digit keys, the delete key and the done key
  1. New price — what the item will cost, and how it was worked out from the price before.
  2. Percentage keys — each takes its percentage off this line's price. The lit one (here −10%) is in use; press it again to put the old price back. You choose the keys under Quick Discounts in Cart Settings.
  3. Digits — type a new price instead.
  4. Delete — removes the last digit.
  5. ✓ — sets the new price.

If you slip:

  • Wrong percentage key — press another before ✓; it replaces the first.
  • Want the old price back — before ✓, press the lit key again. After ✓, tap the price again and type the price you want.
  • Closing the keypad — tapping outside keeps the price you typed. Escape on a keyboard keeps the old price.

Other fixes: If something goes wrong. More: Take money off one item.

Take money off the whole order​

  1. Press + at the top of the cart and choose Add Discount.
  2. On Quick discount, type the amount and choose $ or %.
  3. Press Add Discount.
The Add Discount dialog on the Quick discount tab over a cart holding one $45.00 Aim Analog Watch: the Quick discount and Coupon tabs, the amount 10, the dollar and percent switch set to percent, and the Add Discount button
  1. Quick discount — an amount or a percentage off this whole order, not one line.
  2. Coupon — one of your store's coupons instead. See Apply a coupon.
  3. Amount — tap it and type on the keypad.
  4. $ or % — a fixed amount off, or a percentage of the order.
  5. Add Discount — puts the discount on the order.

The discount appears with the totals:

The cart after a 10% discount on a $45.00 watch: the line total $45.00 struck through with $40.50 under it, the subtotal $45.00, a Discount (10%) row taking off $4.50 with its remove button, and Checkout $40.50
  1. Line total — the price before the discount, struck through, and what the customer pays now.
  2. Subtotal — the order before the discount.
  3. Discount (10%) — the discount and how much it takes off. Discount alone means a fixed amount. Press ✕ to remove it.
  4. Checkout — the new total.

To undo it, press ✕ on its row. The line totals go back, and prices changed on lines stay as they were set. Other fixes: If something goes wrong. More: Take money off the whole order.

Apply a coupon​

  1. Press + at the top of the cart and choose Add Discount (coupons are in there too).
  2. Switch to Coupon, press Select coupon, and type part of the code to find it.
  3. Tap the coupon and press Apply.
The Add Discount dialog on the Coupon tab, with the coupon list open: the Quick discount and Coupon tabs, the Select coupon field, the search box, a coupon with its type and amount, the New coupon button, and the Apply button
  1. Quick discount — an amount or a percentage off the order, without a coupon. See Take money off the whole order.
  2. Coupon — your store's coupons.
  3. Select coupon — opens the list. Once you pick one, its code shows here.
  4. Search coupons — type any part of the code or the description.
  5. A coupon — its code, its description or type, and how much it takes off.
  6. New coupon (WCPOS Pro) — make a coupon now and apply it to this sale.
  7. Apply — checks the coupon's rules against this order and puts it on.

The coupon appears in the totals, or the POS says why it can't be used:

The cart with two coupons applied: the add menu, the subtotal, a row for each coupon with its code, a remove button and the amount it takes off, the item's total with the old amount struck through, and the Checkout button
  1. + — open Add Discount again to add another coupon.
  2. Subtotal — the order before coupons. The coupon rows sit under it.
  3. A coupon — its code, the ✕ that removes it, and the amount it takes off.
  4. The next coupon — each coupon gets its own row, in the order you applied them.
  5. Checkout — take payment for the order. See Checkout.

Picked the wrong one? Press ✕ on its row; the amount it took off goes back on. Refused? See If the coupon is refused. More: Apply a coupon and Applying Coupons.

If something goes wrong​

  • Wrong price on a line — tap the price again and type the right one. Before ✓, pressing the lit percentage key again puts the old price back.
  • Closed the keypad by mistake — tapping outside keeps the price you typed; Escape keeps the old price.
  • Wrong discount on the order — press ✕ on its row under the totals. The line totals go back; prices changed on lines stay.
  • Wrong coupon — press ✕ on its row; the amount it took off goes back on.
  • The coupon is refused — see If the coupon is refused.

If the coupon is refused​

The dialog stays open and says why in red. Two causes come from the other discounts on the order:

  • … can't be combined with other coupons or … is already on the order, and it can't be combined with other coupons — a coupon marked Individual use only must go on alone. Remove the coupon in the way, or keep it and skip the new one.
  • Exclude sale items — an item whose price was lowered at the till counts as on sale. The coupon skips it, and a coupon for a fixed amount off the order can't be used at all while that item is in the cart. Take that item off, or choose another coupon. Why: what counts as on sale.

Every other message, with what to do: When the till refuses a coupon.

Where each discount is recorded​

For developers and reports

Nothing below is needed at the till. It is for developers and for anyone reading WCPOS orders in a report, an export or another plugin.

Each way lands in a different place on the order:

  • A changed price is the line's price: the _woocommerce_pos_data meta and the line subtotal. It is not in discount_total, and no coupon rule applies to it.
  • Add Discount is a coupon line with the code pos-discount and _wcpos_quick_discount meta. It is in discount_total. It applies to every product line, sale items included, but can't be combined with a coupon marked Individual use only.
  • A store coupon is a coupon line with the coupon's code. It is in discount_total, and the coupon's own rules apply.

Here a $45.00 watch had its price typed as $40.00 at the till, then a 10% Add Discount went on the order:

The cart with one Aim Analog Watch: the price lowered at the till from $45.00 to $40.00, the line total $40.00 struck through with $36.00 under it, the subtotal $40.00, a Discount (10%) row taking off $4.00, and Checkout $36.00
  1. Price — a price changed at the till. Stored per unit in the line's _woocommerce_pos_data meta; the line's subtotal is this price × quantity. See How POS price overrides are stored.
  2. Line total — the line's total: its subtotal less its share of any coupon or Add Discount.
  3. Subtotal — the sum of the line subtotals. It already includes the till price, so the order holds no separate figure for it.
  4. Discount (10%) — an Add Discount line: a coupon line with the code pos-discount and _wcpos_quick_discount meta. It counts in the order's discount_total. See Add Discount lines.
  5. Checkout — the order total.

In this shot prices include tax: the US and US AL rows under the totals show the tax inside them. The cart shows amounts with tax, while each line's subtotal and total and the order's discount_total hold them with tax taken out. The order total is the grand total, with tax included.

How POS price overrides are stored​

Every product line the POS creates carries the per-unit prices in the _woocommerce_pos_data line item meta:

{
"price": "40.00",
"regular_price": "45.00",
"tax_status": "taxable"
}
  • price is what the line is sold at, after any change at the till.
  • regular_price is the product's regular price when it was added. Changing the price at the till never changes it, so a price raised at the till sits above it.
  • A misc product also carries virtual, downloadable and categories, so coupon rules can check them.

This meta is the authoritative per-unit price. The line's subtotal is worked out from price × quantity, with tax taken out and rounding applied, and total drops below it when a coupon or Add Discount applies. For per-unit precision, read price and regular_price from the meta.

The meta is hidden in WooCommerce's order screen.

Reading the meta from code

Use a decoder that takes both shapes. Lines from older apps hold the meta as a JSON string. The 2.0 app writes it as an object, so WordPress stores a serialized array, and values may be numbers rather than strings. A bare json_decode() fails on the array.

New POS orders also carry the private _woocommerce_pos_version order meta: the version of the WCPOS plugin that saved the order. Receipts still treat the stored line shape as authoritative, because an order taken offline may reach a newer plugin than the app that made it.

Line totals and the Discount total​

WCPOS records a till price the way WooCommerce records a sale price:

  • line_item.subtotal = price × quantity, where price is the till price.
  • line_item.total = subtotal − the line's share of coupons and Add Discount.
  • order.discount_total = coupons and Add Discount lines only. A till price is never in it.
A worked example: $45.00 → $40.00 at the till → $36.00

With no tax: a watch with a regular price of $45.00, its price typed as $40.00 at the till, then a 10% Add Discount. $40.00 − $4.00 = $36.00. The order stores it as:

  • _woocommerce_pos_data.regular_price: 45.00
  • _woocommerce_pos_data.price: 40.00, the till price
  • line_items[0].subtotal: 40.00, 40.00 × 1
  • line_items[0].total: 36.00, less the Add Discount
  • discount_total: 4.00, the Add Discount only
  • total: 36.00

The $5.00 the till price took off shows only by comparing regular_price with price. Receipts do that comparison for you; see Receipt data.

Orders from before v1.9.0

Before v1.9.0 the app sent subtotal = regular_price × quantity. WooCommerce then worked out discount_total = subtotal − total, so a till price counted as a discount. That broke coupon maths: recalculate_coupons() works from subtotal, so coupons were calculated on the original price. A server-side woocommerce_order_item_get_subtotal filter worked around it during recalculate_coupons().

v1.9.0 moved to the sale-price model above and removed the filter. Orders saved before then keep their old figures, which is why a report can show a till price inside discount_total on an old order. Receipts allow for it: see Receipt data.

Add Discount lines​

Add Discount takes an amount or a percentage off the whole order. It is a coupon line, not a coupon in your store:

  • Code — pos-discount for the first on an order, then pos-discount-2, pos-discount-3 and so on.
  • Meta — _wcpos_quick_discount on the coupon line: { "discount_type": "percent", "amount": "10" }. discount_type is percent (up to 100) or fixed_cart; amount is a positive decimal string.
  • Rules — it applies to sale items, including prices lowered at the till. It can't be combined with a coupon marked Individual use only.
  • Recalculate — WCPOS stores the coupon details on the line, so Recalculate in WooCommerce's order screen keeps the discount.

The virtual coupon exists only while WCPOS saves a POS order: the storefront can't redeem pos-discount. If a store coupon already has a code the request uses, the save is refused with Quick discount code "…" belongs to a store coupon. Use another code.

The 2.0 app no longer takes money off with a negative fee: it asks for Add Discount instead. The plugin still accepts a negative fee line from older apps and other clients.

Coupon Interaction Behaviour​

Applying a store coupon at the till works on the free plugin; creating one at the till needs WCPOS Pro. When a coupon is applied to an order that has a till price:

  1. The coupon works from the till price (_woocommerce_pos_data.price), not regular_price.
  2. Removing the coupon returns the line to its till price.

The app works coupons out on the device, so it can apply one offline, and the plugin works them out again with the same product context when it saves the order.

Where prices include tax and more than one coupon or Add Discount reaches a line, the app adds up that line's discounts first and takes the tax out once, as WooCommerce does. The till's line totals then match the saved order to the last decimal.

exclude_sale_items Behaviour​

A line whose _woocommerce_pos_data.price is below its regular_price counts as on sale, the same as a WooCommerce sale price. A till price at or above regular_price doesn't. So a coupon with Exclude sale items:

  • skips a line whose till price is below its regular_price;
  • can't be used at all while one is in the cart, if it is a fixed cart coupon. This is WooCommerce's own rule for fixed cart coupons.

To change what counts as on sale, use the wcpos_item_is_on_sale filter below.

Available Filters​

wcpos_item_is_on_sale​

Override whether a line with POS price data counts as "on sale" when the plugin validates coupons. Before WCPOS 2.0 this filter was named woocommerce_pos_item_is_on_sale; that name still works but is deprecated since 2.0 and is removed in 2.2 at the earliest (see Hook naming).

Example and parameters
add_filter( 'wcpos_item_is_on_sale', function ( $is_on_sale, $product, $item, $pos_data ) {
// Let coupons with exclude_sale_items apply to lines lowered at the till.
return false;
}, 10, 4 );
  • $is_on_sale (bool) — whether the line counts as 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.

The filter runs on the server. The app's own on-sale check, used while the cashier is still building the cart, is always price < regular_price; the server's answer applies when the order is saved.

REST API Endpoint​

Coupon endpoints live in the free plugin, so the app never gets a 404 when it asks for coupons, even without WCPOS Pro.

The 2.0 app syncs coupons through GET /wp-json/wcpos/v2/coupons, which forwards to WooCommerce's wc/v3/coupons with the POS read permission. A coupon made at the till (Pro) is saved as an ordinary WooCommerce coupon, and the cashier needs permission to create coupons.

The v1 endpoint, for apps before 2.0

Apps before 2.0 use /wp-json/wcpos/v1/coupons. Its controller extends WC_REST_Coupons_Controller and adds UUIDs for offline sync, read access for any user with the access_woocommerce_pos capability, and a fast ID-only path when posts_per_page=-1 and fields=id (or fields=id,date_modified_gmt) are requested.

For who can reach which endpoint, see Role and Endpoint Access.

Receipt Data Exposure​

Receipt templates get the savings without reading _woocommerce_pos_data themselves: each line's savings, one row per coupon or Add Discount, and order totals for both.

The receipt fields for discounts and savings
  • Each line — regular_price, unit_savings and line_savings give what a till price or a sale price saved. discounts is the line's subtotal − total: its share of coupons and Add Discount, never a till price. The exception is an order from before v1.9.0, where it may already include the saving: savings_in_discounts is then true.
  • discounts[] — one row per coupon line, Add Discount included, with label, code, discount_type and total. An Add Discount row is labelled Discount (10%), or Discount for a fixed amount, with code pos-discount and discount_type percent or fixed_cart.
  • Totals — discount_total (coupons and Add Discount, from WC_Order::get_discount_total()), sale_savings_total (regular-to-selling-price savings, till prices included), and total_saved for one figure. total_saved removes the overlap on orders from before v1.9.0.
  • total_saved_complete — false when a line lacks the price data for a reliable sum; the savings totals are then null.

Each has _incl and _excl variants. Receipt_Data_Builder::get_item_meta_pairs() leaves every underscore-prefixed meta key, _woocommerce_pos_data included, out of lines[].meta.

See Displaying regular price and savings and Totals for template examples.