[object Object]

← back to Wolfgordon Crawl

push: publish ACTIVE products to ALL channels + set inventory 2026 on activation

f41202103d217b949e061724b10dcf977a8589d3 · 2026-06-20 04:02:03 -0700 · Steve

Mirrors cadence-import.js (Steve 2026-06-20, activation-all-channels-and-2026-inventory).
publish() now publishes to ALL publications (was Online Store only — the bug that left
419 active WG products on ~6/13 channels); setInventory2026() sets on_hand=2026 at
Ventura Blvd on the sellable variant after activation. Both cap-free + idempotent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Files touched

Diff

commit f41202103d217b949e061724b10dcf977a8589d3
Author: Steve <steve@designerwallcoverings.com>
Date:   Sat Jun 20 04:02:03 2026 -0700

    push: publish ACTIVE products to ALL channels + set inventory 2026 on activation
    
    Mirrors cadence-import.js (Steve 2026-06-20, activation-all-channels-and-2026-inventory).
    publish() now publishes to ALL publications (was Online Store only — the bug that left
    419 active WG products on ~6/13 channels); setInventory2026() sets on_hand=2026 at
    Ventura Blvd on the sellable variant after activation. Both cap-free + idempotent.
    
    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
 push-shopify.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 46 insertions(+), 8 deletions(-)

diff --git a/push-shopify.js b/push-shopify.js
index 44d8d41..7b270eb 100644
--- a/push-shopify.js
+++ b/push-shopify.js
@@ -408,24 +408,62 @@ async function createProduct(row, { draft } = {}) {
   const ue = data.productSet.userErrors;
   if (ue && ue.length) throw new Error('productSet: ' + JSON.stringify(ue));
   const prod = data.productSet.product;
-  if (!isDraft) await publish(prod.id);
+  if (!isDraft) {
+    // Steve 2026-06-20 (memory activation-all-channels-and-2026-inventory): every
+    // ACTIVE product must be published to ALL sales channels AND carry inventory
+    // 2026 at Ventura Blvd on its sellable variant. Mirrors cadence-import.js.
+    await publish(prod.id);                       // ALL channels (was: Online Store only)
+    await setInventory2026([row.dw_sku]);         // on_hand=2026 on the sellable (non-Sample) variant
+  }
   await recordPid(row.mfr_sku, prod.id.split('/').pop());
   return prod;
 }
 
-// Publish a product to the Online Store sales channel.
+// Publish a product to ALL sales channels (was: Online Store only — the bug that
+// left 419 active WG products on ~6 of 13 channels). "already published" userErrors
+// are benign. Copies cadence-import.js publishToChannels().
+let _PUBLICATION_IDS = null;
+async function loadPublications() {
+  if (_PUBLICATION_IDS) return _PUBLICATION_IDS;
+  const r = await gql(`{ publications(first:50){ edges{ node{ id name } } } }`);
+  _PUBLICATION_IDS = (r.publications?.edges || []).map(e => e.node);
+  return _PUBLICATION_IDS;
+}
 async function publish(productId) {
   try {
-    const pubs = await gql(`{ publications(first:10){ edges{ node{ id name } } } }`);
-    const online = pubs.publications.edges.find(e => /online store/i.test(e.node.name));
-    if (!online) return;
+    const pubs = await loadPublications();
+    if (!pubs.length) return;
+    const input = pubs.map(p => ({ publicationId: p.id }));
     await gql(`
-      mutation($id:ID!,$pid:ID!){
-        publishablePublish(id:$id, input:{ publicationId:$pid }){ userErrors{ message } }
-      }`, { id: productId, pid: online.node.id });
+      mutation($id:ID!,$input:[PublicationInput!]!){
+        publishablePublish(id:$id, input:$input){ userErrors{ field message } }
+      }`, { id: productId, input });
   } catch (e) { console.error('  publish warn:', e.message); }
 }
 
+// Inventory on_hand=2026 at the Ventura Blvd location on the given (sellable) SKUs.
+// Cap-free (touches existing variants). Copies cadence-import.js setInventory2026().
+const INV_LOCATION_2026 = 'gid://shopify/Location/5795643504';  // 15442 Ventura Blvd.
+async function setInventory2026(skus) {
+  if (!skus.length) return;
+  try {
+    const map = new Map();
+    for (let i = 0; i < skus.length; i += 40) {
+      const batch = skus.slice(i, i + 40);
+      const q = batch.map(s => `sku:"${String(s).replace(/"/g, '\\"')}"`).join(' OR ');
+      const r = await gql(`query($q:String!){ productVariants(first:250,query:$q){ edges{ node{ sku inventoryItem{ id } } } } }`, { q });
+      for (const e of (r.productVariants?.edges || [])) {
+        if (e.node.sku && e.node.inventoryItem?.id) map.set(e.node.sku, e.node.inventoryItem.id);
+      }
+    }
+    const pairs = skus.filter(s => map.has(s)).map(s => ({ inventoryItemId: map.get(s), locationId: INV_LOCATION_2026, quantity: 2026 }));
+    for (let i = 0; i < pairs.length; i += 250) {
+      await gql(`mutation($input:InventorySetQuantitiesInput!){ inventorySetQuantities(input:$input){ userErrors{ field message code } } }`,
+        { input: { name: 'on_hand', reason: 'correction', ignoreCompareQuantity: true, quantities: pairs.slice(i, i + 250) } });
+    }
+  } catch (e) { console.error('  inventory-2026 warn:', e.message); }
+}
+
 // UPDATE: fix the $4.25 placeholder -> catalog retail price + replace SKU with
 // the sequential DW SKU. Images ADD-only (never delete).
 async function updateProduct(item) {

← 2fe399e Wire net-new pusher to shared variant-budget ledger + kill-s  ·  back to Wolfgordon Crawl  ·  chore: macstudio3 migration — reconcile from mac2 + repoint 6e1cabb →