{"title":"How Promotions v2 run at the register","slug":"promotions-v2-at-the-register","url":"https://support.storeconnect.com/articles/promotions-v2-at-the-register","url_markdown":"https://support.storeconnect.com/articles/promotions-v2-at-the-register.md","subtitle":null,"summary":"Check how a Promotion v2 reaches your POS registers and what the register does with it: what it settles itself and what it asks the server, offline behavior, the store variables it reads, how it differs from the web store, and how promotions show on the cart, the order, and the receipt.","type":"Help_Documentation","video_url":"","keywords":"promotions at POS, promotions v2 POS, promotion register, POS coupon, automatic promotion POS, promotions.version POS, promotion not applying at register, offline promotions, POS promotion differences, promotion receipt, discount_breakdown_lines, Display Name, Exclude From Price Reductions POS, minimum sell price promotion, maximum discount percentage promotion","last_modified":"2026-10-25T21:42:50+0000","body_markdown":"Use this topic when you set up a Promotion v2 that customers will use in store, or when a promotion behaves differently at a register than on your web store. It covers what reaches the register, what the register checks for itself, and where the result differs from the web. For the engine itself, see [About Promotions v2](advanced-promotions).\n\n## Promotions apply at every register\n\nPromotions v2 needs no enablement step at the point of sale. Every Promotion v2 that belongs to the store syncs to the store's registers with the rest of the POS data, and any promotion inside the date range set on the record applies there, unless **Channels** leaves out **POS**. Automatic promotions apply as items go into the cart. A cashier enters a coupon code from the cart, as described in [Apply a coupon at the register](apply-a-coupon-at-the-register).\n\nA register runs only Promotions v2. It never applies a [standard promotion](promotions), and it does not read the store variable `promotions.version`, which chooses the engine for the web store alone. Automatic [discounts](discounts) still apply at the register alongside Promotions v2.\n\n:::warning\nA store whose web store still runs the original engine applies the store's Promotion v2 records at the registers all the same. Set **Channels** to **Web** on any Promotion v2 you do not want at the till.\n:::\n\nA promotion created or changed in Salesforce reaches a register at the next data sync. If a new promotion is not applying, open **Settings** \u003e **Manage data** on the register and check that the five promotion objects are fully synced: `s_c__promotion2__c`, `s_c__promotion2_condition__c`, `s_c__condition__c`, `s_c__reward__c`, and `s_c__promotion2_product_scope__c`. See [Manage POS data sync](manage-pos-data-sync).\n\n## What the register checks, and what it asks the server\n\nThe web store asks the server to price the cart. A register prices the cart itself from the promotion records synced to it, and works out every promotion again from scratch each time the cart changes: an item, a quantity, a price, the cashier's discount, a coupon, a delivery address, or the customer.\n\nThe register settles what the synced records can answer, and asks the server for the rest.\n\n| Settled at the register | Asked of the server |\n|---|---|\n| Date range, **Channels**, store or outlet scope, and customer scope | The full schedule: **Start Time** and **End Time**, **Days of Week**, **Week Frequency**, and the daily window |\n| The conditions, and which lines they match | **Total Usage Limit** and **Per Customer Usage Limit** |\n| Stacking conflicts and duplicate codes | Liquid conditions |\n| The coupon limit | Which deliveries fall inside a promotion's zone |\n\nWhenever the register asks the server about a promotion, the server checks **Channels** and the store or outlet scope again, so a register still holding an out-of-date copy of the promotion cannot apply it. A coupon refused this way reads as invalid.\n\nThe order keeps the figures the register worked out. The server does not price the order again.\n\n### When the register cannot reach the server\n\nA register that is offline, or that gets an error back from the server, keeps selling with what it holds.\n\n-   **Automatic promotions keep applying**, checked against the date range, scope, and conditions only. The usage limits and the rest of the schedule are not checked, so a happy-hour promotion can apply outside the daily window and a promotion can go past the **Total Usage Limit**.\n-   **A zone-limited promotion does not apply**, because only the server can place a delivery in a zone.\n-   **A new coupon cannot be entered offline.** A coupon already on the cart keeps applying, except a zone-limited one, which gives nothing until the server can check it. The sale cannot go to payment until the server checks that coupon or it is removed.\n-   **A coupon entered while the server is erroring is accepted** without the usage limits being checked, unless it is zone-limited. The register refuses a coupon only when it knows it is offline.\n\nUsage limits are counted from the **Promotion 2 Usage** records the server holds, so a limit is only enforced while the register can reach the server.\n\n## Store variables the register reads\n\n| Store variable | At the register |\n|---|---|\n| `promotions.calculation_strategy` | Read. A subtotal reward is sized the same way as on the web. See [How Promotion v2 discounts are calculated](how-promotion-discounts-are-calculated) |\n| `promotions.max_coupon_codes_per_cart` | Enforced when a coupon is entered, and checked again by the server |\n| `promotions.eligibility.free_products` | Not read. A free product never counts toward a condition at a register |\n| `promotions.version` | Not read. A register always runs Promotions v2 |\n\nSee [Store variables](store-variables) for each setting.\n\n## Where the register and the web differ\n\nThe two follow the same rules except where this table names one.\n\n| | At the register | On the web |\n|---|---|---|\n| **Exclude Discounted** skips | Lines on sale, lines covered by a discount on the whole sale, and lines the cashier or a promotion ahead of it in the same calculation already discounted | Lines on sale, lines carrying a share of a discount on the whole sale, and lines the cashier or any other promotion discounted, including one ranked after it |\n| A **Category** scope matches | Products placed in the category | Products in the category and in the subcategories |\n| **Customer Scope Account** and **Customer Scope Membership** | Never match | Match the customer's account or the account's membership |\n| The per-customer limit counts by | Contact, and is not checked at all with no customer on the cart | Account and contact, and with no customer every use counts |\n| A **Matched Products** reward that has product scopes | Discounts the lines the reward's scopes pick | Discounts the lines the condition matched |\n| **Fixed Price for N Products** counts | Units | Cart lines |\n| A tiered pair with a blank **Priority** | Ranks first | Ranks last |\n| A coupon beaten by a larger one | Stays on the cart, marked not applied | Is removed |\n| A coupon that qualifies but gives no discount | Is accepted, and sits as not applied | Is refused |\n\nA shipping reward takes the discount off the delivery charge of a delivery arranged at the register. A cart with several deliveries shares the discount across them, and a zone-limited reward shares it only across the deliveries in the zone.\n\n### What a promotion cannot do at a register\n\n-   **A Liquid condition never fires.** The register cannot evaluate one, so the reward is never given. The server still checks **Liquid Condition (POS)**, and an expression that comes back false blocks the whole promotion. A coupon whose only condition is a Liquid condition is accepted and sits as not applied.\n-   **Points rewards are not priced.** The register discounts in currency only.\n-   **Free products never count toward a condition**, whatever `promotions.eligibility.free_products` is set to.\n\n## Promotions and the cashier's discount limits\n\nA promotion is separate from what staff take off by hand with **Adjust Price** on an item or **Adjust price** on the whole cart.\n\n-   **Maximum Discount Percentage** counts only the cashier's own discounts. A promotion on the line does not use up the allowance.\n-   **Minimum Sell Price** does not stop a promotion. A promotion can take a line below the floor. When the cashier adjusts the price of the whole cart, what the promotion took off counts toward the floor. When the cashier adjusts the price of one item, it does not, so a promotion and an item adjustment together can take the line below the floor.\n-   **Exclude From Price Reductions** set to **Promotions** keeps the product out of every Promotion v2, at the register and on the web. The line counts toward no condition and takes no reward, though a **Free Product** reward that names the product still adds it free.\n\nSee [Adjust prices at the POS checkout](discounts-and-price-overrides) for the limits themselves.\n\n## Carts holding a return\n\nA cart holding a return or an exchange is priced on what the customer is actually buying. Returned lines count against the totals a condition measures, the promotions together cannot take off more than the cart's net subtotal, and a subtotal reward is spread only over the lines being bought. A cart holding nothing but returns gets no promotion and will not take a coupon.\n\nFor example, an exchange that buys a $100 item and returns an $80 one nets to $20, so a $30 off products promotion takes off $20.\n\n## What staff and customers see\n\nEach promotion is named by the **Display Name** on the **Promotion v2** record, falling back to the record name when **Display Name** is blank. Give every promotion that can run in store a **Display Name** a customer will recognize, because staff read it from the cart and a receipt template can print it.\n\n-   **On the cart and the order**, a promotion on particular items is named under each line it discounted, and a promotion on the whole cart gets a separate row in the totals. A free product appears as a separate $0 line. See [POS cart](pos-cart) for what each row means.\n-   **On the order record**, the discount sits on the order items it reduced, and placing the order writes the same **Promotion 2 Usage**, **Reward Usage**, and **Order Item Reward** records as a web order.\n-   **On the receipt**, promotions are listed only if the receipt template asks for them. The built-in **Example Receipt Template** does; a custom template needs the block below.\n\n### List promotions on the receipt\n\nThe order's `discount_breakdown_lines` holds one entry per promotion, with the promotion's `name` and the `amount` it took off. The line prices on the order are already net of promotions, so print these rows as an explanation above the subtotal rather than subtracting them again.\n\nThis block, added to the totals section of a receipt template, prints a row such as `Weekend 10% off applied   -$8.00` for each promotion:\n\n\n```liquid\n\n{table\n  cols=2\n  margin=1\n  align=[left,right]\n  width=[*,8]\n  {% for line in current_order.discount_breakdown_lines %}\n  row=[\"{{ line.name }} applied\",\"-{{ line.amount | money }}\"]\n  {% endfor %}\n}\n```\n\n\nSee [Create or update a POS receipt template](pos-receipt-template) for where the totals section sits.\n\n## Check a promotion at the register\n\n1.  Open the **Promotion v2** record and confirm the **Start Date** is set to today or earlier and the **End Date** is blank, today, or later.\n2.  On a register at an outlet of that store, open **Settings** \u003e **Manage data** and confirm the five promotion objects, starting with `s_c__promotion2__c`, are fully synced.\n3.  Add items that meet the promotion's condition to a cart.\n4.  If the promotion uses a coupon, enter the code from **Manage cart** \u003e **Coupon code**.\n\nThe promotion's **Display Name** now appears in the cart, under each discounted line for an item promotion or as a row in the totals for a promotion on the whole cart, and the **Order total** is reduced by the amount shown."}