← back to Sanderson Onboard

tk10873/promote_trade_verified.mjs

67 lines

#!/usr/bin/env node
// promote_trade_verified.mjs — TK-10873 cycle 5. Refine the 89 HELD_UNVERIFIED rows.
//
// Cycle-4 lumped all feed-less rows into HELD_UNVERIFIED. But 38 of them carry a real
// price_trade and their price_retail == trade/0.65/0.85 EXACTLY (the Option-A contract) —
// so they are VALIDLY priced, merely feed-less. They should be reprice-eligible. The
// other 51 have NO trade; web spot-check (Verdure zc$200 vs real US $362-550, sold-by-
// metre/double-roll) shows they're likely GBP-as-USD low BUT on non-standard units, so a
// blanket multiple is UNSAFE — they stay held, flagged for a targeted US-retail harvest.
//
// READ-ONLY. NO Shopify/dw_unified writes. Produces v3 (local) + a restore map v3->v2.
import fs from 'node:fs';

const DIR = new URL('.', import.meta.url).pathname;
const V2_IN = `${DIR}zoffany_optionA_draft_feedreconciled.json`;
const V3_OUT = `${DIR}zoffany_optionA_draft_v3.json`;
// NOTE: this file is an ACTION-DELTA log (HELD_UNVERIFIED->REPRICE), NOT a full row snapshot.
// The true revert of v3 is the intact prior file zoffany_optionA_draft_feedreconciled.json (v2),
// or `git revert` of the commit. The 38 promotions are web-spot-verified in price_source_verification.md.
const RESTORE_OUT = `${DIR}promote_restore_map.json`;
const SUMMARY_OUT = `${DIR}promote_summary.json`;
const TOL = 0.02;

// Pure: is this held row validly trade-derived (retail == trade/0.65/0.85)?
export function isTradeVerified(row) {
  const t = row.price_trade == null ? null : Number(row.price_trade);
  if (t == null || !(t > 0)) return false;
  const expect = t / 0.65 / 0.85;
  return Math.abs(Number(row.proposed_sellable_price) - expect) < TOL;
}

function main() {
  const v2 = JSON.parse(fs.readFileSync(V2_IN, 'utf8'));
  const v3 = [];
  const restore = [];
  let promoted = 0, stillHeld = 0;
  for (const row of v2) {
    if (row.action === 'HELD_UNVERIFIED' && isTradeVerified(row)) {
      promoted++;
      restore.push({ mfr_sku: row.mfr_sku, from_action: 'HELD_UNVERIFIED', to_action: 'REPRICE' });
      v3.push({ ...row, action: 'REPRICE', price_source: 'zoffany_catalog_trade_derived', price_field: 'price_retail (== trade/0.65/0.85, Option-A contract; validated, feed had no row)' });
    } else {
      if (row.action === 'HELD_UNVERIFIED') stillHeld++;
      v3.push(row);
    }
  }
  const counts = {};
  for (const r of v3) counts[r.action] = (counts[r.action] || 0) + 1;
  const summary = {
    ticket: 'TK-10873', cycle: 5, generated_at: new Date().toISOString(), read_only: true,
    promoted_trade_verified: promoted,          // 38: retail==trade/0.65/0.85, valid
    still_held_no_trade: stillHeld,             // 51: no trade, unit-ambiguous, flagged for harvest
    reprice_now: counts.REPRICE,                // 239 feed + 38 trade = 277
    counts,
    note: 'Promoted 38 trade-derived rows (Option-A contract-valid) to REPRICE; the 51 no-trade rows stay HELD_UNVERIFIED — web spot-check shows likely GBP-as-USD low but on non-standard units (sold-by-metre/double-roll), so a blanket multiple is UNSAFE. Flag: targeted US-retail harvest needed for the 51. LIVE reprice stays Steve-gated.',
  };
  fs.writeFileSync(V3_OUT, JSON.stringify(v3, null, 2));
  fs.writeFileSync(RESTORE_OUT, JSON.stringify(restore, null, 2));
  fs.writeFileSync(SUMMARY_OUT, JSON.stringify(summary, null, 2));
  console.log('=== v3: promote trade-verified held rows (READ-ONLY) ===');
  console.log(`promoted=${promoted}  still_held_no_trade=${stillHeld}  reprice_now=${counts.REPRICE}`);
  console.log(`counts=${JSON.stringify(counts)}`);
  console.log(`artifacts:\n  ${V3_OUT}\n  ${RESTORE_OUT}\n  ${SUMMARY_OUT}`);
}

if (import.meta.url === `file://${process.argv[1]}`) main();