Receipt Data
WP admin: templates are edited in WP Admin → POS → Templates; this page lists the fields they can print.
To print or email a receipt, see Receipts and the Paid Screen; to pick a template, or have AI change one for you, see Customise Your Receipt.
This page is for editing a template's code by hand. Every template, HTML or thermal, prints from the same receipt data: the store, the order, the items, the totals and the payments. The page shows how to print a field, gives three common examples, and lists every field.
Jump to: Find a field · Common examples · If something goes wrong
Print a field
- Open the template in WP Admin → POS → Templates (Edit it by hand has the details).
- Where the field should print, type its path between double braces, such as
{{store.name}},{{order.number}}or{{totals.total_display}}, or insert it from the field list beside the code. - Check the preview below the code, then click Update to save.
For a list, such as the items, wrap the row in a section and it repeats once per item:
{{#lines}}
{{name}} x {{qty}} —
{{line_total_display}}
{{/lines}}
A section also leaves its contents out when the field is empty, zero or false, so {{#customer.id}}…{{/customer.id}} prints only for a named customer. To print money, use the field that ends in _display; see Currency fields.
Receipts print offline too: the app builds a copy of the receipt data from the order on the device.
Why receipts can differ
Where the data comes from
Your store's server builds the receipt data for each order, and the app builds a shorter copy from the order on the device: it has no cashier, no store opening hours, notes or policies, and no totals.paid_total or totals.change_total. An HTML or thermal template opens at once from the device's copy and switches to the server's when it arrives; offline, the device's copy is the one that prints, so those fields print empty. With WCPOS Pro, the device's copy carries the store logo too; without Pro, the logo arrives with the server's copy. A legacy PHP template is always rendered by the server. That's why a template uses the fields below and never PHP order methods. A few fields print only when the server renders the template, as it does for a PDF download; If something goes wrong lists them.
The receipt, mapped
Each part of a printed receipt comes from one section of the receipt data. Point at a part of the receipt or at its card to light up the pair; click or tap either one to jump to that section's fields. On a phone, the cards follow the receipt in the same order, top to bottom.
The receipt below, the first two recipes and the price breakdown use the same sample order. Its item amounts are before the coupon (line_subtotal_display); the gallery's receipt templates print them after it (line_total_display, 26.10 for the espresso).
line_subtotal_displaystore.name · store.address_lines · store.tax_ids · store.logoorder.number · order.created · cashier.name · customer.namelines[] · line_savings · savings_in_discountstotals.* · discounts[] · fees[] · shipping[] · totals.total_savedpayments[]store.personal_notes · store.policies_and_conditions · i18n.thank_you_purchaseCommon recipes
Most hand edits are one of these two, shown for an HTML template, and a third under them covers reprinting older orders. Each card shows the new rows on a receipt, then their code, then where to add it in your template. The item name, amount and TOTAL come from the rest of your template.
Using these in a thermal template
The same fields and sections work unchanged: put each printed line in a <text> element and leave out <s>, because thermal XML has no strike-through and prints the price plain (Thermal Templates).
{{#lines}}
{{#line_savings}}
<s>{{line_regular_total_display}}</s>
{{i18n.savings}}:
-{{line_savings_display}}
{{/line_savings}}
{{/lines}}{{#lines}} section, add the {{#line_savings}} block after the item row. 34.00 regular price − 5.00 sale saving − 2.90 coupon = 26.10 paid. The block leaves out both rows on orders with no recorded price history.{{#totals.total_saved_complete}}
{{#totals.total_saved}}
{{i18n.total_saved}}:
{{totals.total_saved_display}}
{{/totals.total_saved}}
{{/totals.total_saved_complete}}Reprint an order made before WCPOS 1.9.0
{{#lines}}
{{#line_savings}}
<s>{{line_regular_total_display}}</s>
{{^savings_in_discounts}}
{{i18n.savings}}:
-{{line_savings_display}}
{{/savings_in_discounts}}
{{/line_savings}}
{{#discounts}}
{{i18n.discount}}:
-{{discounts_display}}
{{/discounts}}
{{/lines}}{{#lines}} section, add the {{#line_savings}} and {{#discounts}} blocks after the item row. The saving is already inside the discount, so this prints the discount and leaves the saving out.Field reference
Expand a section to see its fields. The first item lists every section and its type.
All sections and their types
Top-level sections
| Section | What it holds |
|---|---|
order (object) | Order identity, status, dates, note, and payment URL information |
store (object) | Store identity, address, contact details, tax IDs, logo, hours, and footer text |
cashier (object) | User who processed the order |
customer (object) | Customer display name, addresses, and tax IDs |
lines (array) | Product line items |
fees (array) | Fee rows |
shipping (array) | Shipping rows |
discounts (array) | Coupon/discount rows |
totals (object) | Order totals, payment totals, refund summary, and item counts |
tax (object) | Tax display mode flags for section guards |
tax_summary (array) | Per-rate tax summary rows |
has_tax_summary (boolean) | Convenience guard for tax_summary |
payments (array) | Payment rows |
refunds (array) | Refund records applied to the order |
fiscal (object) | Fiscal snapshot fields populated by fiscal integrations |
register (object) | The register that took the sale: id and name. Empty strings when unknown |
software (object) | The software that issued the receipt: name (WCPOS), plugin_version, app_version, app_build, platform |
presentation_hints (object) | Formatting and renderer hints |
i18n (object) | Translated labels for bundled and custom templates |
Order number, status and dates — order
order
| Field | Type | Example / description |
|---|---|---|
order.id | number | 1042 |
order.number | string | Human-facing order number, e.g. "1042" |
order.currency | string | ISO currency code, e.g. "USD" |
order.customer_note | string | Customer/order note |
order.wc_status | string | Raw WooCommerce status slug, e.g. "processing" |
order.status_label | string | Localised status label, including custom statuses |
order.created_via | string | Source/channel, e.g. "woocommerce-pos" |
order.needs_payment | boolean | Whether a payment section should be shown |
order.payment_url | string | Order payment URL when available |
order.created | date object | Order creation date |
order.paid | date object | Paid date, empty strings when not paid |
order.completed | date object | Completed date, empty strings when incomplete |
order.printed | date object | Render-time print/reprint timestamp |
Store name, address, contact and hours — store
store
| Field | Type | Example / description |
|---|---|---|
store.id | number | Store ID, or historical ID for deleted stores |
store.name | string | Store display name |
store.address.address_1 | string | Street address line 1 |
store.address.address_2 | string | Suite/unit line |
store.address.city | string | City/locality |
store.address.state | string | State/region |
store.address.postcode | string | Postal code |
store.address.country | string | ISO country code |
store.address_lines | array | Preformatted address lines; recommended for most templates |
store.tax_ids | array | Structured business tax IDs; loop this instead of using a single tax ID |
store.phone | string | Store phone |
store.email | string | Store email |
store.logo | string/null | Store logo URL or data URI: the store's own logo, or else the WordPress site logo unless the store turns that off. null when there is neither |
store.opening_hours | string/null | Compact opening-hours text |
store.opening_hours_vertical | string/null | Multi-line opening-hours block |
store.opening_hours_inline | string/null | Comma-separated opening-hours text |
store.opening_hours_notes | string/null | Free-text opening-hours notes |
store.personal_notes | string/null | Receipt footer/personal note |
store.policies_and_conditions | string/null | Refund, returns, or terms text |
store.footer_imprint | string/null | Legal footer imprint |
Tax ID objects
store.tax_ids and customer.tax_ids contain objects with the same shape:
| Field | Type | Description |
|---|---|---|
type | string | Identifier such as eu_vat, de_steuernummer, au_abn, br_cpf, us_ein, or other |
value | string | Tax ID value to print |
country | string/null | ISO country code; null for eu_vat and other |
label | string | Localised display label, resolved before rendering |
Example:
{{#store.tax_ids}}
{{label}}: {{value}}
{{/store.tax_ids}}
Who served the customer — cashier
cashier
| Field | Type | Example / description |
|---|---|---|
cashier.id | number | WordPress user ID, 0 when unknown |
cashier.name | string | Cashier display name |
Customer name, addresses and tax IDs — customer
customer
| Field | Type | Example / description |
|---|---|---|
customer.id | number/null | Customer ID, or null for guests |
customer.name | string | Customer display name, or guest label |
customer.first_name | string | The billing first name, for a greeting such as "Thanks, Nia". Empty for guests without one |
customer.billing_address.* | object | WooCommerce billing address fields |
customer.shipping_address.* | object | WooCommerce shipping address fields |
customer.tax_ids | array | Structured customer tax IDs snapshotted from the order |
Common address keys include first_name, last_name, company, address_1, address_2, city, state, postcode, country, email, and phone.
Items sold — lines
lines
Loop with {{#lines}}...{{/lines}}. Sample values are the espresso line from the sample order.
Every money field comes in two: the number, for a {{#…}} section (zero and null hide it, and a null field has no _display field at all), and the _display text, for printing. Fields marked incl/excl also come with _incl and _excl versions, each with its own _display.
null on orders without POS price history — wrap it in a section.discounts, as on orders made before v1.9.0. Use it so a saving isn't printed twice.{key, value} pairs.{key, value} pairs.Displaying regular price and savings
For a unit-price layout, show the recorded regular price, selling price, and saving like this:
{{#lines}}
{{#unit_savings}}
<span style="text-decoration: line-through;">{{regular_price_display}}</span>
<span>{{selling_price_display}}</span>
<span>{{i18n.savings}}: -{{unit_savings_display}}</span>
{{/unit_savings}}
{{/lines}}
Use the line-total fields when quantity matters:
{{#lines}}
{{#line_savings}}
<span style="text-decoration: line-through;">{{line_regular_total_display}}</span>
<span>{{i18n.savings}}: -{{line_savings_display}}</span>
{{/line_savings}}
{{/lines}}
selling_price is the price before coupons. Use unit_price or line_total for the final amount after WooCommerce discounts.
Older WCPOS orders may already include the regular-to-selling-price reduction in discounts. If your custom template prints both line savings and line discounts, use savings_in_discounts to avoid showing the same reduction twice:
{{#lines}}
{{#line_savings}}
<span style="text-decoration: line-through;">{{line_regular_total_display}}</span>
{{^savings_in_discounts}}
<span>{{i18n.savings}}: -{{line_savings_display}}</span>
{{/savings_in_discounts}}
{{/line_savings}}
{{#discounts}}
<span>{{i18n.discount}}: -{{discounts_display}}</span>
{{/discounts}}
{{/lines}}
If the template does not print a line-level discounts row, keep showing line_savings even when savings_in_discounts is true. The bundled templates handle this distinction automatically.
Fees and shipping — fees, shipping
fees and shipping
Loop with {{#fees}}...{{/fees}} and {{#shipping}}...{{/shipping}}.
| Field | Type | Description |
|---|---|---|
label | string | Fee label or shipping method name |
method_id | string | Shipping method ID (shipping only) |
total / _incl / _excl | number | Display-side, inclusive, and exclusive totals |
taxes | array | Per-rate tax rows |
meta | array | {key, value} meta pairs |
Formatted variants: total_display, total_incl_display, and total_excl_display.
Coupons and discounts — discounts
discounts
Loop with {{#discounts}}...{{/discounts}}.
| Field | Type | Description |
|---|---|---|
label | string | Coupon description or code fallback. A discount added in the POS cart reads Discount, or Discount (10%) for a percentage |
code | string | Coupon code |
discount_type | string | WooCommerce coupon type slug, for example fixed_cart, percent, or fixed_product. Empty when the coupon no longer exists. |
total / _incl / _excl | number | Discount amount as a positive value |
Formatted variants: total_display, total_incl_display, and total_excl_display. Add your own minus sign in the template if you want discounts shown as negative rows.
discount_type was added in Receipt Data v1.2. Fiscal receipt snapshots captured before then do not carry it.
Totals, amounts paid, refunds and item counts — totals
totals
Sample values are from the sample order, which later received a $7.25 partial refund.
Money fields follow the same pairing as line items: a bare number for guards and a _display string for printing, with _incl/_excl variants where marked.
total_incl is always the amount charged.0 when nothing was refunded, so guard it with refund_total.Use total_saved when you want one customer-facing figure for everything saved. Do not calculate it by adding discount_total and sale_savings_total in the template: orders created before v1.9.0 may already include their POS price saving in WooCommerce's discount total. The canonical total_saved field detects and removes that overlap.
Savings totals are null and total_saved_complete is false when any product line lacks enough recorded price data for a reliable aggregate. Guard the row with total_saved_complete, then use total_saved to hide a zero value:
{{#totals.total_saved_complete}}
{{#totals.total_saved}}
<span>{{i18n.total_saved}}: {{totals.total_saved_display}}</span>
{{/totals.total_saved}}
{{/totals.total_saved_complete}}
The bundled templates print Total saved from the tax-inclusive variant, totals.total_saved_incl_display, inside a single {{#totals.total_saved_incl}} guard. That one guard is enough because the value is null when the price history is incomplete and zero when nothing was saved.
Tax rows — tax, tax_summary
tax and tax_summary
Use tax for display-mode guards and tax_summary for itemized rate rows.
| Tax field | Type | Description |
|---|---|---|
tax.display | string | incl or excl |
tax.display_incl | boolean | True when prices display inclusive of tax |
tax.display_excl | boolean | True when prices display exclusive of tax |
tax.breakdown | string | hidden, single, or itemized |
tax.breakdown_hidden | boolean | True when tax rows should be hidden |
tax.breakdown_single | boolean | True when a single tax total is preferred |
tax.breakdown_itemized | boolean | True when per-rate rows are preferred |
has_tax_summary | boolean | True when tax_summary contains rows |
Loop tax_summary with {{#tax_summary}}...{{/tax_summary}}.
| Field | Type | Description |
|---|---|---|
code | string | Tax rate ID/code |
rate | number/null | Rate percentage when resolved |
label | string | Tax rate label |
compound | boolean | Whether the rate is compounded |
taxable_amount_excl | number/null | Taxable base excluding tax |
tax_amount | number | Tax collected |
taxable_amount_incl | number/null | Taxable base including tax |
Formatted variants: taxable_amount_excl_display, tax_amount_display, and taxable_amount_incl_display.
Payments taken — payments
payments
Loop with {{#payments}}...{{/payments}}.
| Field | Type | Description |
|---|---|---|
method_id | string | Payment method identifier |
method_title | string | Payment method display title |
amount | number | Amount applied to the order |
transaction_id | string | Gateway transaction ID |
tendered | number | Cash amount tendered when present |
change | number | Cash change returned when present |
Formatted variants: amount_display, tendered_display, and change_display.
Refunds — refunds
refunds
Loop with {{#refunds}}...{{/refunds}}. Refund amounts are positive numbers: the template adds a minus sign, or prints them in their own returned-items block.
| Field | Type | Description |
|---|---|---|
id | number | Refund record ID |
date | date object | Refund creation date |
amount | number | Refund total |
subtotal | number | Refunded line subtotal |
tax_total | number | Tax refunded |
shipping_total | number | Shipping amount refunded |
shipping_tax | number | Shipping tax refunded |
reason | string | Refund reason |
refunded_by_id | number/null | User ID that issued the refund |
refunded_by_name | string | User display name that issued the refund |
refunded_payment | boolean | Whether the payment was refunded through the gateway |
destination | string | Where the money went back: original_method, cash or manual for a refund made in the POS; empty otherwise |
gateway_id | string | Gateway ID used for the refund |
gateway_title | string | Gateway display title |
processing_mode | string | Provider/manual processing mode |
lines | array | Refunded product rows |
fees | array | Refunded fee rows |
shipping | array | Refunded shipping rows |
Refund line fields include name, sku, qty, total, total_incl, total_excl, and taxes; templates rendered in the POS app also get line_total and unit_total. Refund fee and shipping rows use label, total, total_incl, total_excl, and taxes, and shipping rows add method_id. Display variants are added for totals and tax amounts.
Fiscal records — fiscal
fiscal
Fiscal integrations and WCPOS Pro fill in the identity fields, such as the receipt number, hash and QR payload. The document fields say what kind of document is printing, so a template can print a refund or a register report differently from a sale.
| Field | Type | Description |
|---|---|---|
fiscal.document_type | string | sale by default; also refund, void, cancellation, closure, xreport or handover |
fiscal.is_sale_document | boolean | True for a sale. Use the is_… flags as section guards |
fiscal.is_refund_document | boolean | True for a refund |
fiscal.is_void_document | boolean | True for a void: a sale cancelled before it was completed |
fiscal.is_cancellation_document | boolean | True for a cancellation: a completed sale cancelled later |
fiscal.is_closure_document | boolean | True for a register closure |
fiscal.is_x_report | boolean | True for an X-report |
fiscal.is_handover | boolean | True for a hand-over slip |
fiscal.corrects | string | On a refund, the identity of the sale it corrects |
fiscal.sale_time | date object/null | When the till recorded the sale, in the till's timezone |
fiscal.sale_tz | string | The till's timezone, e.g. "Europe/Berlin" |
fiscal.received_at | date object/null | When the store's server received the sale |
fiscal.sale_counter | number/null | The till's own sale counter |
fiscal.immutable_id | string | Immutable fiscal identifier |
fiscal.receipt_number | string | Fiscal receipt number |
fiscal.sequence | number/null | Sequence counter |
fiscal.hash | string | Hash/signature value |
fiscal.qr_payload | string | QR payload for fiscal verification |
fiscal.tax_agency_code | string | Tax authority code |
fiscal.signed_at | string | Fiscal signing timestamp |
fiscal.signature_excerpt | string | Truncated signature for display |
fiscal.document_label | string | Document label, e.g. Tax Invoice |
fiscal.is_reprint | boolean | Whether this render is a reprint |
fiscal.reprint_count | number | Reprint count |
fiscal.extra_fields | array/object | Jurisdiction-specific values, usually a list of {label, value} rows |
Print the extra fields with a loop:
{{#fiscal.extra_fields}}
{{label}}: {{value}}
{{/fiscal.extra_fields}}
Formatting — presentation_hints
presentation_hints
These fields are mostly for the app and the server, which use them to format the receipt. Templates rendered by the server can print all of them; templates rendered in the POS app get only display_tax, prices_entered_with_tax, rounding_mode, locale and order_barcode_type.
| Field | Type | Description |
|---|---|---|
presentation_hints.display_tax | string | incl or excl. Filled in by the app; the server leaves it out |
presentation_hints.hour12 | boolean/null | True when the store's time format uses a 12-hour clock |
presentation_hints.hour_token | string/null | The hour pattern from the store's time format: h, hh, H or HH |
presentation_hints.prices_entered_with_tax | boolean | Whether catalogue prices include tax |
presentation_hints.rounding_mode | string | WooCommerce tax rounding setting |
presentation_hints.locale | string | Locale used for formatting |
presentation_hints.timezone | string | Receipt timezone |
presentation_hints.currency_position | string | Currency symbol position |
presentation_hints.currency_symbol | string | Currency symbol |
presentation_hints.price_thousand_separator | string | Thousands separator |
presentation_hints.price_decimal_separator | string | Decimal separator |
presentation_hints.price_num_decimals | number | Decimal places |
presentation_hints.price_display_suffix | string | WooCommerce price display suffix |
presentation_hints.order_barcode_type | string | Barcode type for a template that reads it: code128 (the default), qrcode, ean13, ean8 or upca. Filled in by the app; the server leaves it out, and the gallery templates set their barcode type in the template |
The pre-formatted money fields (the *_display values such as totals.total_display) render amounts with the currency symbol in the order's currency, using the position and separators above — so receipts show $12.00, not USD 12.00. The raw order.currency field remains the ISO code for any logic in your template.
Receipt wording — i18n
i18n
Use i18n labels instead of hardcoding text where possible:
{{i18n.order}} #{{order.number}}
{{i18n.cashier}}: {{cashier.name}}
{{i18n.total}}: {{totals.total_display}}
Common keys include order, date, cashier, customer, item, sku, qty, unit_price, regular_price, selling_price, savings, discount, subtotal, total_saved, total, tax, paid, tendered, change, tax_summary, refunded, net_total, customer_note, thank_you_purchase, opening_hours, the fiscal labels copy (prints COPY), reprint, register, sale_time, software, document_refund, document_void and document_cancellation, and the tax-ID label keys such as store_tax_id_label_eu_vat and customer_tax_id_label_other. Extra keys may be added by extensions.
How the prices relate
One item on the receipt carries several prices, and the names are easiest to follow on the amounts themselves. This is the espresso line from the sample order — a $17.00 product on sale for $14.50, with a 10% coupon on top:
unit_price → 13.05 · line_total → 26.10lines[].discounts → 此商品行优惠 2.90unit_savings → 2.50 · line_savings → 5.00totals.sale_savings_total = 5.00totals.discount_total = 5.28totals.total_saved = 10.28A sale saving is the gap between the regular price and the selling price. A discount is what a coupon took off, as WooCommerce works it out. WooCommerce's order totals and reports hold only the discounts, so total_saved adds the two into one figure for the customer.
Rendering rules
How money, prices with or without tax, and dates print. Open the one you need.
Print money, and hide zero amounts
Currency fields
Money fields stay numbers, and each one gets a _display field formatted in the store's language and currency:
| Numeric field | Display field |
|---|---|
totals.total | totals.total_display |
lines[].line_total | lines[].line_total_display |
payments[].amount | payments[].amount_display |
tax_summary[].tax_amount | tax_summary[].tax_amount_display |
Print the _display field. Use the number only for a section or for machine-readable output: {{#totals.change_total}}…{{/totals.change_total}} leaves its row out when the amount is zero.
Print prices with or without tax
Tax display-aware fields
Several fields come in three versions: with tax (_incl), without tax (_excl), and a plain one that follows the store's setting for showing prices in the cart with or without tax.
| Convenience field | Inclusive field | Exclusive field |
|---|---|---|
lines[].regular_price | lines[].regular_price_incl | lines[].regular_price_excl |
lines[].selling_price | lines[].selling_price_incl | lines[].selling_price_excl |
lines[].unit_savings | lines[].unit_savings_incl | lines[].unit_savings_excl |
lines[].line_regular_total | lines[].line_regular_total_incl | lines[].line_regular_total_excl |
lines[].line_selling_total | lines[].line_selling_total_incl | lines[].line_selling_total_excl |
lines[].line_savings | lines[].line_savings_incl | lines[].line_savings_excl |
lines[].unit_price | lines[].unit_price_incl | lines[].unit_price_excl |
lines[].unit_subtotal | lines[].unit_subtotal_incl | lines[].unit_subtotal_excl |
lines[].line_subtotal | lines[].line_subtotal_incl | lines[].line_subtotal_excl |
lines[].discounts | lines[].discounts_incl | lines[].discounts_excl |
lines[].line_total | lines[].line_total_incl | lines[].line_total_excl |
fees[].total | fees[].total_incl | fees[].total_excl |
shipping[].total | shipping[].total_incl | shipping[].total_excl |
discounts[].total | discounts[].total_incl | discounts[].total_excl |
totals.subtotal | totals.subtotal_incl | totals.subtotal_excl |
totals.discount_total | totals.discount_total_incl | totals.discount_total_excl |
totals.sale_savings_total | totals.sale_savings_total_incl | totals.sale_savings_total_excl |
totals.total_saved | totals.total_saved_incl | totals.total_saved_excl |
totals.total | totals.total_incl | totals.total_excl |
Print dates and times
Date objects
Each date comes ready-formatted in several ways, so a template never has to format a date itself.
| Field | Description |
|---|---|
datetime, date, time | Default date/time strings |
datetime_short, datetime_long, datetime_full | Locale-aware combined formats |
date_short, date_long, date_full | Locale-aware date-only formats |
date_ymd, date_dmy, date_mdy | Fixed-order date formats |
weekday_short, weekday_long | Day names |
day, month, month_short, month_long, year | Individual date parts |
Available date objects: order.created, order.paid, order.completed, order.printed, refunds[].date, fiscal.sale_time, and fiscal.received_at. order.printed is refreshed at render time, which is useful for reprints. The two fiscal dates are null when the receipt has no fiscal record, so wrap them in a section.
Extending receipt data
A plugin can add its own fields to the receipt data through the wcpos_receipt_data filter.
The filter — its arguments, the six modes and an example
WCPOS keeps the fields above free of any one plugin's data. When a plugin needs the receipt to know something WooCommerce does not model, it adds that itself through the wcpos_receipt_data filter, the same way plugins filter WooCommerce's own order totals. The filter runs on every receipt the server builds: live receipts, fiscal snapshots at payment time, PDF downloads, and legacy PHP templates. Before WCPOS 2.0 this filter was named woocommerce_pos_receipt_data; that name still works but is deprecated since 2.0 and is removed in 2.2 at the earliest (see Hook naming).
The third argument, $mode, says which document is being built: live, fiscal, refund, preview (sample receipts in the template editor and gallery), closure or xreport (register closure and X-report documents). In preview, closure and xreport the order is an unsaved one whose ID is 0; a plugin that needs a saved order should return $data unchanged when $order->get_id() is 0.
add_filter( 'wcpos_receipt_data', function ( array $data, WC_Abstract_Order $order, string $mode ) {
foreach ( $data['discounts'] as &$row ) {
if ( 'smart_coupon' === $row['discount_type'] ) {
$row['gift_card'] = true;
}
}
return $data;
}, 10, 3 );
A template can then guard on the new key like any other section:
{{#discounts}}
{{#gift_card}}Gift Card{{/gift_card}}{{^gift_card}}{{i18n.discount}}{{/gift_card}}{{#label}}: {{label}}{{/label}}
{{/discounts}}
Keys defined in the schema should keep their documented types. Added keys are not part of the schema, so they do not appear in the template editor's field picker, and fiscal snapshots only carry what the filter added at the time the order was paid. The filter is available from WCPOS 1.10.8.
Which receipts print an added key
Every added key reaches the receipts the server renders itself: PDF downloads and legacy PHP templates. HTML and thermal templates rendered in the POS app rebuild the data first: a key added inside order, store, cashier or customer comes through, but a key added anywhere else, such as on the discounts row above, is left out. On a live receipt with a fiscal snapshot, the fiscal identity, register and software are copied from the snapshot after the filter runs, so changing them in this filter has no effect; enrich the snapshot instead.
If something goes wrong
A row prints $0.00 on one receipt and nothing on another. Some _display fields, such as totals.change_total_display, totals.refund_total_display and payments[].tendered_display, are empty at zero when the server renders the receipt, and $0.00 when the app does. Put the row inside a section on the number instead, so it is left out of both: {{#totals.change_total}}…{{/totals.change_total}}.
The Total saved row is missing. totals.total_saved_complete is false: at least one item has no recorded regular price, so the saving can't be worked out. Orders rung up before WCPOS 1.9.0 are the usual case.
The total is less than the customer paid. The store shows prices excluding tax, so totals.total is the total before tax. Print totals.total_incl_display for the amount charged. The legacy PHP receipt that ships with WCPOS prints that amount.
A field prints in the PDF download but not in the POS. Templates rendered in the app rebuild the data first and leave out a few fields, which print only in PDF downloads and legacy PHP templates. Print a field from the list below only in those, or guard it with a section so the POS receipt leaves it out.
Fields that differ between PDF and POS receipts
lines[].attributes,lines[].total_refunded_displayandlines[].unit_subtotal_display(with its_incland_excltwins)software.platformpresentation_hintsother thandisplay_tax,prices_entered_with_tax,rounding_mode,localeandorder_barcode_type- customer tax IDs of the German, Dutch, French, UK company and Swiss types
- keys a plugin adds outside
order,store,cashierandcustomer; see Extending receipt data
Versions
The receipt data has its own version number, separate from the plugin's. Newer fields are missing from older receipts, such as a fiscal snapshot taken before the field existed, so guard them with a section.
| Version | Added |
|---|---|
| 1.1 | Regular price, selling price and savings for each item, and the order's savings totals, including Total saved. None of them change the order totals or WooCommerce reports |
| 1.2 | discounts[].discount_type |
| 1.3 | customer.first_name and the customer display labels in i18n |
| 1.4 | register, software, the fiscal document fields (fiscal.document_type, the is_… flags, sale_time, received_at, sale_tz, sale_counter, corrects) and their i18n labels |