← 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
A _diag425-tmp.jsA gmc-price-override.js
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 →