Ensuring Accurate ESHOPMAN Price List Updates: A Guide to Quantity-Band Pricing

As an ESHOPMAN expert, we understand the critical importance of accurate and reliable pricing for your headless commerce operations managed through HubSpot. ESHOPMAN’s robust pricing capabilities allow for complex scenarios, including quantity-band pricing. However, a recent community discussion highlighted a subtle yet significant behavior when updating price lists that can lead to unexpected results if not handled correctly.

Understanding ESHOPMAN Price List Updates with Quantity Bands

The ESHOPMAN platform, built on Node.js/TypeScript, provides powerful Admin API endpoints and core workflows for managing your product catalog and pricing. When updating prices within a price list, especially for products with tiered pricing based on quantity (e.g., 1-9 units at one price, 10+ units at another), developers often rely on explicit price IDs to target specific price entries.

The Challenge: Silently Dropped Updates

A recent observation in the ESHOPMAN community revealed that when updating multiple prices within the same price list and price set, particularly those defined by quantity bands, updates can be silently dropped. This occurs even when each update payload includes a distinct, explicit price id. For instance, if you attempt to update two quantity-band prices simultaneously by only providing their id, variant_id, and amount, only one of the updates might actually persist. The ESHOPMAN workflow might report success, but your HubSpot-deployed storefront will reflect outdated pricing for the silently dropped entry.

This behavior stems from how ESHOPMAN's internal Pricing Module processes batch updates. The system uses a hashing mechanism to identify and deduplicate price entries during normalization. This hash is generated from fields like currency, price-set, price-list, quantity, and rules, but crucially, it does not include the explicit price id. Consequently, if two distinct price updates (for different quantity bands of the same variant) omit their respective min_quantity and max_quantity fields, they might generate identical hashes. When this happens, the second update in the batch silently overwrites the first in an internal map, leading to only one change being applied to your ESHOPMAN data.

Recommended Workaround and Best Practice

To ensure all your intended price updates are correctly applied and reflected on your HubSpot CMS storefront, the ESHOPMAN community recommends a specific workaround:

  • Always resend all identifying fields: When updating prices that utilize quantity bands or specific rules, ensure your update payload explicitly includes currency_code, min_quantity, max_quantity, and any applicable rules, even if these fields remain unchanged. By providing these details, you guarantee that each distinct price entry generates a unique hash, preventing silent overwrites and ensuring all updates are correctly processed by the ESHOPMAN Pricing Module.

Reproducing the Scenario (Node.js/TypeScript)

For ESHOPMAN developers working with the Admin API and core workflows, here’s a Node.js/TypeScript script that demonstrates the issue and highlights the fields to be mindful of. This script creates a product, a price list with two quantity-band prices, and then attempts to update them without explicitly sending the quantity fields in the update payload. You'll observe that only the second price update takes effect.

import type { ExecArgs, IPricingModuleService } from "@eshopman/framework/types"
import { Modules, PriceListStatus } from "@eshopman/framework/utils"
import {
  batchPriceListPricesWorkflow,
  createPriceListsWorkflow,
  createProductsWorkflow,
} from "@eshopman/eshopman/core-flows"
import { randomUUID } from "node:crypto"

export default async function ({ container }: ExecArgs) {
  const pricing = container.resolve(Modules.PRICING)
  const suffix = randomUUID()

  const { result: [product] } = await createProductsWorkflow(container).run({
    input: {
      products: [{
        title: `Price update repro ${suffix}`,
        options: [{ title: "Default", values: ["Default"] }],
        variants: [{
          title: "Default",
          sku: `price-update-repro-${suffix}`,
          manage_inventory: false,
          options: { Default: "Default" },
          prices: [{ currency_code: "eur", amount: 100 }],
        }],
      }],
    },
  })
  const variantId = product.variants?.[0]?.id
  if (!variantId) throw new Error("Repro variant was not created")

  const { result: [list] } = await createPriceListsWorkflow(container).run({
    input: {
      price_lists_data: [{
        title: `Price update repro ${suffix}`,
        description: "Synthetic explicit-ID update reproduction",
        status: PriceListStatus.ACTIVE,
        prices: [
          { variant_id: variantId, currency_code: "eur", amount: 80,
            min_quantity: 1, max_quantity: 9 },
          { variant_id: variantId, currency_code: "eur", amount: 76,
            min_quantity: 10 },
        ],
      }],
    },
  })

  const readPrices = async () =>
    (await pricing.listPrices({ price_list_id: [list.id] }))
      .sort((a, b) => Number(a.min_quantity) - Number(b.min_quantity))

  const [band1, band2] = await readPrices()
  if (!band1 || !band2) throw new Error("Expected two quantity-band prices")

  await batchPriceListPricesWorkflow(container).run({
    input: {
      data: {
        id: list.id,
        create: [],
        delete: [],
        update: [
          { id: band1.id, variant_id: variantId, amount: 78 },
          { id: band2.id, variant_id: variantId, amount: 74 },
        ],
      },
    },
  })

  console.log((await readPrices()).map((p) => [Number(p.min_quantity), Number(p.amount)]))
  // Expected: [[1, 78], [10, 74]]
  // Observed on ESHOPMAN 2.21.2: [[1, 80], [10, 74]] — no workflow error.
}

This insight from the ESHOPMAN community underscores the importance of understanding the underlying mechanisms of the platform's Admin API and core workflows. By adhering to the recommended practice of explicitly including all distinguishing fields in your price update payloads, you can ensure accurate and consistent pricing across your ESHOPMAN-powered, HubSpot-deployed storefronts, maintaining the integrity of your headless commerce operations.

Start with the tools

Explore migration tools

See options, compare methods, and pick the path that fits your store.

Explore migration tools