[object Object]

← back to Gmc Titlefix

auto-save: 2026-07-09T08:09:34 (2 files) — _diag425-tmp.js gmc-price-override.js

a04456ac54ceb7a913e90a318bf453e824d0f027 · 2026-07-09 08:09:40 -0700 · Steve Abrams

Files touched

Diff

commit a04456ac54ceb7a913e90a318bf453e824d0f027
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 9 08:09:40 2026 -0700

    auto-save: 2026-07-09T08:09:34 (2 files) — _diag425-tmp.js gmc-price-override.js
---
 _diag425-tmp.js       |  31 ++++++++++++++
 gmc-price-override.js | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 144 insertions(+)

diff --git a/_diag425-tmp.js b/_diag425-tmp.js
new file mode 100644
index 0000000..c8e2e5a
--- /dev/null
+++ b/_diag425-tmp.js
@@ -0,0 +1,31 @@
+const {token,MERCHANT}=require('./_auth.js');
+const fs=require('fs');
+// load feed skus
+const lines=fs.readFileSync('/Users/macstudio3/Projects/designerwallcoverings/today-viewer/data/google-merchant-feed.tsv','utf8').split('\n');
+const cols=lines[0].split('\t'); const iId=cols.indexOf('id');
+const feedSkus=new Set(); for(let i=1;i<lines.length;i++){if(lines[i]){const s=lines[i].split('\t')[iId]; if(s)feedSkus.add(s);}}
+(async()=>{
+  const t=await token(); const H={Authorization:'Bearer '+t};
+  let page=null,scanned=0,n425=0,sampleSku=0,baseInFeed=0,baseNotInFeed=0; const ex=[];
+  do{
+    const r=await (await fetch(`https://shoppingcontent.googleapis.com/content/v2.1/${MERCHANT}/products?maxResults=250`+(page?`&pageToken=${page}`:''),{headers:H})).json();
+    if(r.error)break;
+    for(const p of (r.resources||[])){
+      scanned++;
+      const price=parseFloat(p.price?.value||'0');
+      if(Math.abs(price-4.25)<0.01){ n425++;
+        const mpn=p.mpn||''; const isSampleSku=/sample/i.test(mpn)||/sample/i.test(p.offerId||'');
+        const base=mpn.replace(/-?sample.*$/i,'');
+        if(isSampleSku)sampleSku++; else if(feedSkus.has(base)||feedSkus.has(mpn))baseInFeed++; else baseNotInFeed++;
+        if(ex.length<12)ex.push(`offerId=${(p.offerId||'').slice(0,45)} | mpn=${mpn} | ${isSampleSku?'SAMPLE-SKU':feedSkus.has(base)?'BASE-IN-FEED':'NO-MATCH'} | ${(p.title||'').slice(0,35)}`);
+      }
+    }
+    page=r.nextPageToken;
+    if(scanned>=20000)break;
+  }while(page);
+  console.log(`sampled ${scanned} offers, ${n425} at $4.25`);
+  console.log(`  sample-SKU offers (mpn/offerId contains 'sample'): ${sampleSku}`);
+  console.log(`  base-sku IS in feed (roll exists, this is its sample): ${baseInFeed}`);
+  console.log(`  genuinely no match: ${baseNotInFeed}`);
+  console.log('--- examples ---'); ex.forEach(e=>console.log('  '+e));
+})().catch(e=>console.error('FATAL',e.message));
diff --git a/gmc-price-override.js b/gmc-price-override.js
new file mode 100644
index 0000000..85fec44
--- /dev/null
+++ b/gmc-price-override.js
@@ -0,0 +1,113 @@
+// GMC PRICE OVERRIDE (Option A) — fix the $4.25 leak on LIVE Google offers by riding a
+// SUPPLEMENTAL price override on top of the Shopify channel's offers (same offerId → no
+// duplicate listing). For every live Google offer currently priced $4.25 whose product has
+// a real (higher) price in the controlled feed, override the price to the real/highest value.
+// 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 --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 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';
+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;
+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").
+function loadFeedPrices() {
+  const lines = fs.readFileSync(FEED_TSV, 'utf8').split('\n');
+  const cols = lines[0].split('\t');
+  const iId = cols.indexOf('id'), iPrice = cols.indexOf('price'), iMpn = cols.indexOf('mpn');
+  const map = new Map(); // sku -> max real price seen
+  for (let i = 1; i < lines.length; i++) {
+    if (!lines[i]) continue;
+    const f = lines[i].split('\t');
+    const price = parseFloat((f[iPrice] || '').split(' ')[0]);
+    if (isNaN(price)) continue;
+    for (const key of [f[iId], f[iMpn]]) {
+      if (!key) continue;
+      if (!map.has(key) || price > map.get(key)) map.set(key, price);
+    }
+  }
+  return map;
+}
+
+async function listMcOffers() {
+  const tok = await token(); const H = { Authorization: 'Bearer ' + tok };
+  const offers = []; let page = null, scanned = 0;
+  do {
+    const r = await (await fetch(`https://shoppingcontent.googleapis.com/content/v2.1/${MERCHANT}/products?maxResults=250` + (page ? `&pageToken=${page}` : ''), { headers: H })).json();
+    if (r.error) { console.error('MC list err:', JSON.stringify(r.error).slice(0, 200)); break; }
+    for (const p of (r.resources || [])) {
+      scanned++;
+      offers.push({ offerId: p.offerId, mpn: p.mpn || '', price: parseFloat(p.price?.value || '0'), feedLabel: p.feedLabel || 'US', contentLanguage: p.contentLanguage || 'en', title: (p.title || '').slice(0, 50) });
+    }
+    page = r.nextPageToken;
+    if (scanned % 5000 === 0) process.stderr.write(`  ...${scanned} MC offers read\n`);
+  } while (page);
+  return offers;
+}
+
+async function createDataSource() {
+  const tok = await token();
+  const url = `https://merchantapi.googleapis.com/datasources/v1/accounts/${MERCHANT}/dataSources`;
+  for (const supp of [{}, { contentLanguage: 'en' }, { contentLanguage: 'en', feedLabel: 'US' }]) {
+    const body = { displayName: 'DW Real-Price Overrides', supplementalProductDataSource: supp };
+    const r = await fetch(url, { method: 'POST', headers: { Authorization: 'Bearer ' + tok, 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
+    const j = await r.json();
+    if (r.status >= 200 && r.status < 300 && j.name) { console.log('DATASOURCE CREATED:\n' + j.name); return; }
+    console.error('  shape', JSON.stringify(supp), '->', r.status, (j.error?.message || '').slice(0, 120));
+  }
+  console.error('all datasource shapes failed');
+}
+
+async function applyOverrides(ds) {
+  const list = JSON.parse(fs.readFileSync(OUT, 'utf8')).overrides;
+  let tok = await token(), tokAt = Date.now(), ok = 0, fail = 0;
+  console.log(`Pushing ${list.length} price overrides → ${ds}`);
+  for (let i = 0; i < list.length; i++) {
+    if (Date.now() - tokAt > 50 * 60 * 1000) { tok = await token(); tokAt = Date.now(); }
+    const row = list[i];
+    const url = `https://merchantapi.googleapis.com/products/v1/accounts/${MERCHANT}/productInputs:insert?dataSource=${encodeURIComponent(ds)}`;
+    const body = { offerId: row.offerId, contentLanguage: row.contentLanguage, feedLabel: row.feedLabel, productAttributes: { price: { amountMicros: String(Math.round(row.realPrice * 1e6)), currencyCode: 'USD' } } };
+    const r = await fetch(url, { method: 'POST', headers: { Authorization: 'Bearer ' + tok, 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
+    if (r.ok) ok++; else { fail++; if (fail <= 10) console.error('FAIL', row.offerId, r.status, (await r.text()).slice(0, 120)); }
+    if (i % 500 === 0) console.log(`  ${i}/${list.length} | ok ${ok} fail ${fail}`);
+    if (r.status === 429) await sleep(3000);
+  }
+  console.log(`DONE: ok ${ok} fail ${fail} of ${list.length}`);
+}
+
+(async () => {
+  if (CREATE_DS) return createDataSource();
+  if (APPLY) { if (!DS) { console.error('need datasource name: --apply <DS>'); process.exit(1); } return applyOverrides(DS); }
+
+  // DRY-RUN: build the override list (feed ⋈ MC)
+  const feed = loadFeedPrices();
+  console.log(`Loaded ${feed.size} SKU→real-price entries from the controlled feed.`);
+  const offers = await listMcOffers();
+  console.log(`Read ${offers.length} live Google offers.`);
+  const overrides = []; let leakNoMatch = 0, sampleOnlyOk = 0, alreadyReal = 0;
+  for (const o of offers) {
+    if (o.price > 4.26) { alreadyReal++; continue; }            // already showing real price
+    const real = feed.get(o.mpn) || feed.get(o.offerId);
+    if (real === undefined) { if (o.price <= 4.26) leakNoMatch++; continue; } // can't map — skip (needs review)
+    if (real > 4.26) overrides.push({ offerId: o.offerId, mpn: o.mpn, contentLanguage: o.contentLanguage, feedLabel: o.feedLabel, currentPrice: o.price, realPrice: real, title: o.title });
+    else sampleOnlyOk++;                                        // feed price is also $4.25 → legit sample-only
+  }
+  fs.writeFileSync(OUT, JSON.stringify({ generated_at: new Date().toISOString(), total_offers: offers.length, overrides_needed: overrides.length, sample_only_left_at_425: sampleOnlyOk, already_real_price: alreadyReal, leak_unmatched_needs_review: leakNoMatch, overrides }, null, 2));
+  console.log(`\nOVERRIDES NEEDED (roll products at $4.25 → real price): ${overrides.length}`);
+  console.log(`  legit sample-only left at $4.25: ${sampleOnlyOk}`);
+  console.log(`  already showing real price: ${alreadyReal}`);
+  console.log(`  $4.25 offers with NO feed match (review): ${leakNoMatch}`);
+  console.log(`Wrote ${OUT}`);
+  console.log('--- sample overrides ---');
+  overrides.slice(0, 8).forEach(o => console.log(`  ${o.offerId}  $${o.currentPrice} → $${o.realPrice}  ${o.title}`));
+})().catch(e => { console.error('FATAL', e.message); process.exit(1); });

← cefbf23 chore: macstudio3 migration — reconcile from mac2 + repoint  ·  back to Gmc Titlefix  ·  auto-save: 2026-07-09T08:39:40 (1 files) — _diag425-tmp.js 282cc63 →