← back to Rebel Walls Push

scripts/lib/inventory-stamp-guard.mjs.pre-shared-lift

81 lines

// VENDORED COPY — canonical source: Designer-Wallcoverings/shopify/scripts/lib/inventory-stamp-guard.mjs
// Vendored (not cross-repo-imported) on purpose: this repo ships and runs independently, and a
// cross-repo relative import would hard-crash the scheduled daily cadence if either tree moved.
// KEEP IN SYNC — the TK-11357 fixture harness hashes every copy and FAILS on drift.
// TK-11357 (lineage TK-10825/10965/11140/11299/11301/11357).
// TK-10965 — Fix B (prevention): the inventory-stamp invariant, as a pure guard.
//
// ROOT CAUSE (see ../FINDINGS.md): importers stamp a positive "cap-free" stock
// number (the year literal 2026) on the SELLABLE non-Sample variant of every
// activated product — both in the product-create payload (`inventory_quantity: 2026`)
// and on reconcile (`setInventory2026()`). When that sellable variant is ALSO
// priced $0 (quote-only / contact-for-price lines like Phillipe Romano, Fentucci
// Naturals), positive stock makes it `availableForSale` → checkout-orderable for $0.
//
// THE INVARIANT this module enforces (one place, both call sites):
//   A sellable variant that is priced $0 OR belongs to a quote-only / price-
//   suppressed line must NEVER receive positive inventory. It gets 0 → not orderable.
//   (The $4.25 Sample variant is unaffected — it is not the sellable variant and is
//    already qty=0/non-orderable by design.)
//
// PURE + dependency-free on purpose: no network, no env, no Shopify client, so it
// unit-tests offline and drops into any importer runtime unchanged. $0 (local).

// Tag family that means "this line has no public retail price" — a superset of the
// single `quote-only` tag the standing canary keyed on (which is why Fentucci, tagged
// `quotes`/`Needs-Price`, was the canary's 462-product blind spot).
export const PRICE_SUPPRESSED_TAGS = new Set([
  'quote-only', 'quote only', 'quote_only',
  'quotes', 'contact-for-price', 'contact for price', 'needs-price', 'needs price',
]);

const norm = t => String(t).trim().toLowerCase();

/**
 * Is this product a quote-only / price-suppressed line?
 * @param {{tags?: string[]|string, vendor?: string}} product
 */
export function isPriceSuppressed(product = {}) {
  const tags = Array.isArray(product.tags)
    ? product.tags
    : String(product.tags || '').split(',');
  if (tags.some(t => PRICE_SUPPRESSED_TAGS.has(norm(t)))) return true;
  // Vendor fallback for untagged cohorts (Fentucci Naturals ships quote-only with
  // zero quote-only tags). Extend as new price-on-request lines are onboarded.
  return norm(product.vendor) === 'fentucci naturals';
}

/**
 * A variant is the "sellable" one iff it is NOT the Sample variant.
 * (Importers create exactly two variants: `Sample` @ $4.25 and the real unit @ price.)
 * @param {{title?: string, option1?: string}} variant
 */
export function isSellableVariant(variant = {}) {
  const label = variant.title ?? variant.option1 ?? '';
  return !/sample/i.test(label);
}

/**
 * Would giving this sellable variant positive stock make it a $0-orderable defect?
 * True iff it's the sellable variant AND (price is 0 OR the line is price-suppressed).
 * @param {object} variant  the variant about to be stamped
 * @param {object} product  its parent (for tags/vendor)
 */
export function isZeroPriceOrderableRisk(variant = {}, product = {}) {
  if (!isSellableVariant(variant)) return false;
  const price = Number(variant.price);
  return price === 0 || Number.isNaN(price) || isPriceSuppressed(product);
}

/**
 * THE GUARD. Return the inventory quantity that is SAFE to stamp on this variant.
 * Drop-in replacement for the literal `2026` at both call sites:
 *   - create payload:  inventory_quantity: safeStampQuantity(variant, product)
 *   - setInventory2026: quantity:        safeStampQuantity(variant, product)
 * Returns `desired` (2026) for normal priced variants; 0 for the defect class.
 * @returns {number} 0 for a zero-price-orderable risk, else `desired`
 */
export function safeStampQuantity(variant, product, desired = 2026) {
  return isZeroPriceOrderableRisk(variant, product) ? 0 : desired;
}