Cart Transform cost.compareAtAmountPerQuantity ignores catalog pricing when EEA “hide compare‑at” is enabled

This is a long and detailed summary, hopefully enough context for River to digest and investigate for you Shopifolk :sweat_smile:

Summary

When using a catalog with a percentage increase and rounding rules, cart.lines[].cost.compareAtAmountPerQuantity in a Cart Transform function behaves inconsistently when compare‑at prices are disabled for the buyer (either by EEA “hide compare‑at price” or by turning off “Include compare‑at prices” on the catalog):

  • If compare‑at prices are visible (EEA “hide compare‑at” off and catalog “Include compare‑at prices” on), the field returns the catalog‑adjusted, converted, rounded compare‑at price (expected).
  • If compare‑at prices are hidden (EEA “hide compare‑at” on, or catalog “Include compare‑at prices” off), the field falls back to the base compare_at_price in the product’s original currency (unadjusted, unconverted), instead of behaving like the storefront and resolving to null/blank.

This appears to be a bug: a presentation‑layer / catalog visibility setting is affecting the value delivered to backend logic (Cart Transform input), and in an inconsistent way that doesn’t match storefront Liquid.


Expected Behavior

For a cart whose buyer context is in a catalog (e.g., “Europe” catalog):

  • cart.lines[].cost.amountPerQuantity should always:

    • Use the catalog’s price adjustment (e.g. +30%),
    • Be converted to the cart currency,
    • Be rounded according to the catalog’s rounding rules.
  • cart.lines[].cost.compareAtAmountPerQuantity should:

    • When compare‑at prices are included/visible for that buyer:
      • Use the same catalog adjustment, conversion, and rounding, just like amountPerQuantity.
    • When compare‑at prices are hidden/disabled for that buyer (via EEA “hide compare‑at price” or catalog “Include compare‑at prices” = off):
      • Behave like the storefront and resolve to null, not fall back to the base list compare_at_price in its original currency.

EEA “hide compare‑at” and catalog “Include compare‑at prices” should affect visibility, but not cause a reversion to base‑currency, unadjusted compare‑at in function input.


Actual Behavior

Given:

Base product/variant (AUD):

  • Variant A:
    • price: X AUD
    • compare_at_price: Y AUD

Europe Catalog settings:

  • Pricing rule: +30% increase
  • Currency: EUR
  • Rounding: e.g. to .95 (e.g. 10.00 → 9.95, 13.37 → 13.95, etc.)
  • Catalog compare‑at visibility: “Include compare‑at prices” is enabled

Then:

Case 1 – Compare‑at visible (EEA “hide compare‑at price” OFF, catalog includes compare‑at)

  • Storefront + catalog:
    • Price: (X + 30%) converted to EUR, rounded to .95
    • Compare‑at: (Y + 30%) converted to EUR, rounded to .95
  • Cart Transform input:
    • cart.lines[].cost.amountPerQuantity:
      • (X + 30%) in EUR, rounded:white_check_mark: expected
    • cart.lines[].cost.compareAtAmountPerQuantity:
      • (Y + 30%) in EUR, rounded:white_check_mark: expected

Case 2 – Compare‑at hidden by EEA (EEA “hide compare‑at price” ON, catalog still includes compare‑at)

  • Storefront (Liquid):
    • Price: (X + 30%) converted to EUR, rounded to .95
    • Compare‑at: resolves to blank/null, as expected when EEA hide‑compare‑at is on.
  • Cart Transform input:
    • cart.lines[].cost.amountPerQuantity:
      • Still (X + 30%) in EUR, rounded:white_check_mark: expected
    • cart.lines[].cost.compareAtAmountPerQuantity:
      • Now returns Y in AUD (the base compare_at_price, unadjusted and unconverted) → :cross_mark: unexpected
      • This is both the wrong currency and ignores catalog adjustments and rounding.

Case 3 – Compare‑at disabled by catalog (“Include compare‑at prices” OFF)

  • Storefront (Liquid):
    • Compare‑at: not shown for that catalog (effectively null/blank).
  • Cart Transform input (same cart context):
    • cart.lines[].cost.compareAtAmountPerQuantity:
      • Again falls back to the base Y AUD compare_at_price (unadjusted/unconverted), instead of being null to match storefront behavior.

So the value of compareAtAmountPerQuantity changes origin and currency based on visibility settings and, when compare‑at is disabled, no longer reflects the cart’s catalog context or storefront behavior.


Steps to Reproduce

  1. Setup Product & Base Prices

    • Create a product with one variant (Variant A):
      • Base currency: AUD
      • price = X AUD (e.g. 100.00 AUD)
      • compare_at_price = Y AUD (e.g. 150.00 AUD)
  2. Create a Europe Catalog

    • Create a catalog targeting Europe (or a specific European country/region).
    • Configure pricing:
      • +30% price increase on the product or collection containing Variant A.
      • Currency: EUR.
      • Enable rounding (e.g., prices rounded to .95).
    • Ensure initially that “Include compare‑at prices” is enabled for this catalog.
  3. Enable Cart Transform Function

    • Create a Cart Transform function and query (created by dev assistant for this issue)

Operation

     query Input {
       cart {
         buyerIdentity {
           countryCode
         }
         lines {
           id
           cost {
             amountPerQuantity {
               amount
               currencyCode
             }
             compareAtAmountPerQuantity {
               amount
               currencyCode
             }
           }
           merchandise {
             __typename
             ... on ProductVariant {
               id
             }
           }
         }
       }
     }
  • In your function, just log/inspect the input JSON (e.g. with shopify app function run and a captured cart input) rather than modifying the cart.
  1. Create a Test Cart in Europe Context

    • From the Online Store (or a custom storefront using Storefront API), create a cart:
      • Add Variant A.
      • Ensure the buyer context uses the Europe catalog (e.g., shipping/billing country in Europe and correct sales channel).
  2. Case 1: Compare‑at visible (EEA “hide compare‑at” OFF)

    • In admin, disable any EEA setting that hides compare‑at/strike‑through prices.
    • Keep catalog “Include compare‑at prices” = on.
    • Load the cart and capture Cart Transform input.
    • Observe cart.lines[].cost:
      • amountPerQuantity.amount is (X + 30%) in EUR, rounded to .95.
      • compareAtAmountPerQuantity.amount is (Y + 30%) in EUR, rounded to .95.
  3. Case 2: Compare‑at hidden by EEA (EEA “hide compare‑at” ON)

    • In admin, enable EEA setting that hides compare‑at/strike‑through prices.
    • Keep catalog “Include compare‑at prices” = on.
    • With the same cart and catalog:
      • Storefront compare‑at is blank/null.
      • Cart Transform input shows:
        • amountPerQuantity.amount remains (X + 30%) in EUR, rounded.
        • compareAtAmountPerQuantity returns:
          • amountY (original AUD base value),
          • currencyCode = AUD → mismatch with cart currency.
  4. Case 3: Compare‑at disabled by catalog (“Include compare‑at prices” OFF)

    • Turn off “Include compare‑at prices” for the Europe catalog.
    • Keep EEA “hide compare‑at” either on or off (both should effectively hide compare‑at at storefront level).
    • With the same cart:
      • Storefront compare‑at is not shown (equivalent to null).
      • Cart Transform input still shows:
        • compareAtAmountPerQuantity.amountY AUD (base list price, not EUR, no catalog logic applied).

Impact

  • Cart Transform logic that relies on cost.compareAtAmountPerQuantity sees:
    • Correct, catalog‑adjusted, cart‑currency numbers when compare‑at is visible.
    • Raw base‑list compare_at values (different currency, no adjustment, no rounding) when compare‑at is hidden.
  • This leads to:
    • Incorrect discount/logic calculations (e.g., savings, thresholds, “was/now” logic),
    • Inconsistent behavior across regions and catalog configurations,
    • No reliable way in function code to distinguish “compare‑at is genuinely available in this context” vs “compare‑at is hidden and the field has fallen back to a base price”.

Request

  • Confirm whether this is a bug in how cost.compareAtAmountPerQuantity is populated when:

    • EEA “hide compare‑at” is enabled, and/or
    • The catalog’s “Include compare‑at prices” setting is turned off.
  • Ensure that, for carts using a catalog:

    • cart.lines[].cost.compareAtAmountPerQuantity always reflects either:

      • The catalog‑adjusted, converted, rounded compare‑at price (when compare‑at is included/visible for that buyer), or
      • null when compare‑at prices are disabled/hidden in that context (EEA or catalog),
    • And that it does not fall back to the base‑list compare_at_price in its original currency when compare‑at is disabled, so that Cart Transform behavior matches storefront behavior and catalog intent.

Hey @Anthony_SL :waving_hand: thanks for flagging this and for the detailed reproduction steps.

I’m looking into the behaviour you’ve described and will follow up here when I have more information to share.

Thanks @Wes-Dev-Shopify!
Just checking the docs again:

compareAtAmountPerQuantity
The compareAt price of a single unit before any discounts are applied. This field is used to calculate and display savings for customers. For example, if a product’s compareAtAmountPerQuantity is $25 and its current price is $20, then the customer sees a $5 discount. This value can change based on the buyer’s identity and is null when the value is hidden from buyers.

It’s the last sentence that says this is a bug. It should be null when hidden, but it isn’t.