← back to Dw Signup Fulfillment

verification/tk10836/verify-delivery-profiles.js

86 lines

#!/usr/bin/env node
// TK-10836 Option D: READ-ONLY delivery-profile diagnosis.
// Why was the $0.00 "Free Shipping (No Tracking)" method not offered on Kelly's cart?
// Hypothesis: the free rate is PRICE-CONDITIONED with a minimum > $0, so when the
// "DW Free Samples" automatic discount zeroes the item subtotal to $0.00 the cart
// falls BELOW the minimum and the free rate disappears.
// minimal .env loader (no dotenv dep)
const fs=require('fs'),os=require('os');
for (const line of fs.readFileSync(os.homedir()+'/Projects/secrets-manager/.env','utf8').split('\n')) {
  const m=line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)$/); if(!m) continue;
  let v=m[2].trim().replace(/^['"]|['"]$/g,''); if(!process.env[m[1]]) process.env[m[1]]=v;
}
const STORE = process.env.SHOPIFY_STORE_DOMAIN || 'designer-laboratory-sandbox.myshopify.com';
const TOKEN = process.env.SHOPIFY_FULL_ACCESS_TOKEN; // has read_shipping
const API = '2024-10';
async function gql(query, variables = {}) {
  const r = await fetch(`https://${STORE}/admin/api/${API}/graphql.json`, {
    method: 'POST',
    headers: { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, variables })
  });
  const j = await r.json();
  if (j.errors) console.error('GQL ERRORS:', JSON.stringify(j.errors, null, 2));
  return j.data;
}
const Q = `
query {
  deliveryProfiles(first: 10) {
    nodes {
      id name default
      productVariantsCount { count }
      profileLocationGroups {
        locationGroupZones(first: 10) {
          nodes {
            zone { id name }
            methodDefinitions(first: 15) {
              nodes {
                id name active description
                rateProvider {
                  __typename
                  ... on DeliveryRateDefinition { id price { amount currencyCode } }
                  ... on DeliveryParticipant { id carrierService { formattedName } }
                }
                methodConditions {
                  id operator field
                  conditionCriteria {
                    __typename
                    ... on MoneyV2 { amount currencyCode }
                    ... on Weight { unit value }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}`;
(async () => {
  const d = await gql(Q);
  if (!d) return;
  const out = [];
  for (const p of d.deliveryProfiles.nodes) {
    out.push(`\n=== PROFILE: ${p.name} (default=${p.default}, variants=${p.productVariantsCount?.count}) ===`);
    for (const lg of p.profileLocationGroups) {
      for (const z of lg.locationGroupZones.nodes) {
        const zc = '';
        out.push(`  ZONE: ${z.zone.name} [${zc.slice(0, 120)}]`);
        for (const m of z.methodDefinitions.nodes) {
          let rate = m.rateProvider.__typename;
          if (m.rateProvider.price) rate = `FLAT ${m.rateProvider.price.amount} ${m.rateProvider.price.currencyCode}`;
          else if (m.rateProvider.carrierService) rate = `CARRIER ${m.rateProvider.carrierService.formattedName}`;
          const conds = (m.methodConditions || []).map(c => {
            const cc = c.conditionCriteria || {};
            const val = cc.amount !== undefined ? `$${cc.amount}` : (cc.value !== undefined ? `${cc.value}${cc.unit}` : '?');
            return `${c.field} ${c.operator} ${val}`;
          });
          out.push(`    - "${m.name}" active=${m.active} | ${rate}` + (conds.length ? `\n        CONDITIONS: ${conds.join(' AND ')}` : `\n        CONDITIONS: (none — always offered)`));
        }
      }
    }
  }
  console.log(out.join('\n'));
})();