[object Object]

← back to Gmc Titlefix

auto-save: 2026-07-26T17:14:46 (1 files) — gmc-price-override.js

c10e177acfe41cc3afa66afe7d7f9a8f4e548cb2 · 2026-07-26 17:14:54 -0700 · Steve Abrams

Files touched

Diff

commit c10e177acfe41cc3afa66afe7d7f9a8f4e548cb2
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun Jul 26 17:14:54 2026 -0700

    auto-save: 2026-07-26T17:14:46 (1 files) — gmc-price-override.js
---
 gmc-price-override.js | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/gmc-price-override.js b/gmc-price-override.js
index 85fec44..3b65ace 100644
--- a/gmc-price-override.js
+++ b/gmc-price-override.js
@@ -5,19 +5,26 @@
 // Sample-only products (feed price IS $4.25) are left at $4.25 (Steve policy).
 //
 //   node gmc-price-override.js                 # DRY-RUN: build override list from feed⋈MC, no writes
+//   node gmc-price-override.js --dwla-pid-join # DRY-RUN (DWLA only): build override list via pid join, no writes
 //   node gmc-price-override.js --create-ds      # GATED: create the supplemental datasource (prints its name)
 //   node gmc-price-override.js --apply <DS>     # GATED: push price overrides to datasource <DS>
 //
 // Cost: $0 (Google Content/Merchant API + local feed read; no per-call charge).
 const { token, MERCHANT } = require('./_auth.js');
 const fs = require('fs');
+const { execFileSync } = require('child_process');
 
 const FEED_TSV = '/Users/macstudio3/Projects/designerwallcoverings/today-viewer/data/google-merchant-feed.tsv';
 const OUT = '/Users/macstudio3/.claude/yolo-queue/gmc-price-override-list.json';
+// Separate output for the DWLA pid-join path so it never clobbers the default SKU-join list.
+const OUT_DWLA = '/Users/macstudio3/.claude/yolo-queue/gmc-price-override-dwla-list.json';
 const args = process.argv.slice(2);
 const CREATE_DS = args.includes('--create-ds');
 const APPLY = args.includes('--apply');
 const DS = APPLY ? args[args.indexOf('--apply') + 1] : null;
+// OPT-IN, guarded DWLA path — pid-based feed↔MC join (see 2026-07-16 memo EXECUTION LOG).
+// Off by default; does NOT change the default SKU/mpn-join dry-run behavior above.
+const DWLA_PID_JOIN = args.includes('--dwla-pid-join');
 const sleep = ms => new Promise(r => setTimeout(r, ms));
 
 // 1) Load real/highest price per SKU from the controlled feed (id/mpn = sku, price = "X.XX USD").
@@ -39,6 +46,67 @@ function loadFeedPrices() {
   return map;
 }
 
+// --- DWLA pid-join path (OPT-IN via --dwla-pid-join) -----------------------------------------
+// The controlled feed keys on SKU (DWLA-XXXXXX-Yard) but the live MC offers carry an empty mpn
+// and an offerId of the form shopify_US_<pid>_<vid>, so the default feed.get(mpn||offerId) join
+// misses every DWLA offer. The bridge that DOES exist is the Shopify product numeric id (pid):
+// it is embedded in the MC offerId AND present in dw_unified.justin_david_pricing_2026.shopify_id
+// (gid://shopify/Product/<pid>), which also carries the authoritative retail_yd. So we join on pid.
+
+// Build pid -> retail_yd from dw_unified.justin_david_pricing_2026 (apply_status='ok').
+// Read-only local PG via psql (no pg module dependency; host=/tmp socket, dbname=dw_unified).
+function loadDwlaPidRetailMap() {
+  const sql = "SELECT regexp_replace(shopify_id, '^gid://shopify/Product/', '') AS pid, retail_yd "
+            + "FROM justin_david_pricing_2026 "
+            + "WHERE apply_status='ok' AND shopify_id ~ '^gid://shopify/Product/[0-9]+$';";
+  const out = execFileSync('psql', ['host=/tmp dbname=dw_unified', '-tA', '-F', '\t', '-c', sql],
+    { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
+  const map = new Map(); // pid (string) -> retail_yd (number)
+  for (const line of out.split('\n')) {
+    if (!line.trim()) continue;
+    const [pid, retail] = line.split('\t');
+    const r = parseFloat(retail);
+    if (pid && !isNaN(r)) map.set(pid, r);
+  }
+  return map;
+}
+
+// Extract <pid> from a MC offerId shaped shopify_US_<pid>_<vid>.
+function pidFromOfferId(offerId) {
+  const m = /^shopify_US_(\d+)_/.exec(offerId || '');
+  return m ? m[1] : null;
+}
+
+// DRY-RUN (DWLA only, pid join): mark an override when the offer's pid is a DWLA pid AND the offer
+// price <= 4.26 AND retail_yd > 4.26. Writes to OUT_DWLA (never the default OUT). NO writes to MC.
+async function dryRunDwlaPidJoin() {
+  const pidMap = loadDwlaPidRetailMap();
+  console.log(`Loaded ${pidMap.size} DWLA pid→retail_yd entries from justin_david_pricing_2026 (apply_status='ok').`);
+  const offers = await listMcOffers();
+  console.log(`Read ${offers.length} live Google offers.`);
+  const overrides = [];
+  let dwlaAlreadyReal = 0, dwlaSampleLeak = 0, nonDwla = 0, dwlaNoRetail = 0;
+  for (const o of offers) {
+    const pid = pidFromOfferId(o.offerId);
+    if (!pid || !pidMap.has(pid)) { nonDwla++; continue; } // not a DWLA offer — leave untouched
+    const retail = pidMap.get(pid);
+    if (o.price > 4.26) { dwlaAlreadyReal++; continue; }   // DWLA offer already at a real price
+    if (!(retail > 4.26)) { dwlaNoRetail++; continue; }    // no real per-yard price to promote to
+    dwlaSampleLeak++;
+    overrides.push({ offerId: o.offerId, pid, mpn: o.mpn, contentLanguage: o.contentLanguage, feedLabel: o.feedLabel, currentPrice: o.price, realPrice: retail, title: o.title });
+  }
+  fs.writeFileSync(OUT_DWLA, JSON.stringify({ generated_at: new Date().toISOString(), join: 'dwla-pid', total_offers: offers.length, dwla_pids_in_map: pidMap.size, overrides_needed: overrides.length, dwla_already_real_price: dwlaAlreadyReal, dwla_no_retail: dwlaNoRetail, non_dwla_offers: nonDwla, overrides }, null, 2));
+  console.log(`\n[DWLA pid-join] OVERRIDES NEEDED ($4.25 DWLA offers → per-yard retail): ${overrides.length}`);
+  console.log(`  DWLA offers already showing a real price: ${dwlaAlreadyReal}`);
+  console.log(`  DWLA offers with no real per-yard retail (>4.26): ${dwlaNoRetail}`);
+  console.log(`  non-DWLA offers left untouched: ${nonDwla}`);
+  console.log(`Wrote ${OUT_DWLA}`);
+  console.log('--- sample DWLA overrides ---');
+  overrides.slice(0, 8).forEach(o => console.log(`  ${o.offerId}  pid ${o.pid}  $${o.currentPrice} → $${o.realPrice}  ${o.title}`));
+  console.log('\nNOTE: no MC writes performed. To apply, Steve gates: --create-ds then --apply <DS> (apply reads the default OUT list; point it at OUT_DWLA or copy first).');
+}
+// --- end DWLA pid-join path -------------------------------------------------------------------
+
 async function listMcOffers() {
   const tok = await token(); const H = { Authorization: 'Bearer ' + tok };
   const offers = []; let page = null, scanned = 0;
@@ -88,6 +156,7 @@ async function applyOverrides(ds) {
 (async () => {
   if (CREATE_DS) return createDataSource();
   if (APPLY) { if (!DS) { console.error('need datasource name: --apply <DS>'); process.exit(1); } return applyOverrides(DS); }
+  if (DWLA_PID_JOIN) return dryRunDwlaPidJoin();   // OPT-IN DWLA pid-join dry-run (no writes)
 
   // DRY-RUN: build the override list (feed ⋈ MC)
   const feed = loadFeedPrices();

← 2abd4af auto-save: 2026-07-17T19:47:29 (1 files) — gmc-dwla-peryard-  ·  back to Gmc Titlefix  ·  price-override: --apply self-resolves DS (file→DEFAULT_DS), 9204be4 →