← back to Tk 10965 Zero Price Analysis
evidence/tk10965_quant.mjs
35 lines
import fs from 'node:fs';
const env=fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env','utf8');
const val=k=>(env.match(new RegExp('^'+k+'=(.*)$','m'))||[])[1]?.trim();
const DOM=val('SHOPIFY_STORE_DOMAIN'), TOK=val('SHOPIFY_ADMIN_TOKEN');
const API=`https://${DOM}/admin/api/2024-10/graphql.json`;
async function gql(q,v){for(let a=0;a<8;a++){const r=await fetch(API,{method:'POST',headers:{'X-Shopify-Access-Token':TOK,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})});const j=await r.json();if(j.errors){if(JSON.stringify(j.errors).includes('THROTTLED')){await new Promise(s=>setTimeout(s,1800*(a+1)));continue;}throw new Error(JSON.stringify(j.errors));}return j.data;}throw new Error('retries');}
const SEARCH=`status:active AND (tag:'quote-only' OR tag:'Quote Only' OR tag:'quote_only' OR tag:'Quote-Only')`;
let after=null,pages=0,all=[];
do{ const d=await gql(`query($q:String!,$after:String){products(first:100,query:$q,after:$after){pageInfo{hasNextPage endCursor} nodes{id vendor variants(first:20){nodes{title price availableForSale inventoryPolicy inventoryQuantity inventoryItem{tracked}}}}}}`,{q:SEARCH,after});
all.push(...d.products.nodes); after=d.products.pageInfo.hasNextPage?d.products.pageInfo.endCursor:null; pages++;
}while(after&&pages<40);
// bad = non-sample $0 variant that is availableForSale
const badVars=[];
for(const p of all){
for(const v of p.variants.nodes){
if(!/sample/i.test(v.title||'') && Number(v.price)===0 && v.availableForSale===true){
badVars.push({vendor:p.vendor, policy:v.inventoryPolicy, qty:v.inventoryQuantity, tracked:v.inventoryItem?.tracked});
}
}
}
const byVendor={}, byPolicy={}, byQty={}, byTracked={};
for(const b of badVars){
byVendor[b.vendor]=(byVendor[b.vendor]||0)+1;
byPolicy[b.policy]=(byPolicy[b.policy]||0)+1;
const qk = b.qty>0? (b.qty===2026?'2026':'other_positive') : (b.qty===0?'0':'neg/null');
byQty[qk]=(byQty[qk]||0)+1;
byTracked[String(b.tracked)]=(byTracked[String(b.tracked)]||0)+1;
}
console.log('quote_only_active_products:', all.length);
console.log('bad_zero_price_orderable_variants:', badVars.length);
console.log('by_vendor:', JSON.stringify(byVendor));
console.log('by_inventoryPolicy:', JSON.stringify(byPolicy));
console.log('by_inventoryQty:', JSON.stringify(byQty));
console.log('by_tracked:', JSON.stringify(byTracked));