← back to Sanderson Onboard
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 cadence if either tree moved.
// KEEP IN SYNC — the TK-11357 fixture harness hashes both copies 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;
}