← back to Filemaker Mcp

scripts/bulk-create-greenland-full.mjs

58 lines

// Create FileMaker WALLPAPER masters for the 908 full-catalog Greenland→Phillipe Romano SKUs.
// Series = DW prefix (SILK/PWV/WOOD/SPEC/...), JS Pattern = DW number, Mfr Pattern = real
// Greenland code, Name = "<City> <MaterialWord> Wallcovering", Color, Width, vid=GREEN.
// Retail Price left BLANK (quote-only — pricing is a separate gated step). combo-sku-with-dash
// = <prefix>-<number> = the Shopify DW SKU. Idempotent: skips existing (Series + JS Pattern).
//   node bulk-create-greenland-full.mjs                 # DRY-RUN
//   node bulk-create-greenland-full.mjs --apply [--limit=N] [--offset=N]
import { execFileSync } from 'node:child_process';
import * as fm from '../src/fm-client.js';

const APPLY = process.argv.includes('--apply');
const LIMIT = parseInt((process.argv.find(a => a.startsWith('--limit=')) || '').split('=')[1] || '0', 10);
const OFFSET = parseInt((process.argv.find(a => a.startsWith('--offset=')) || '').split('=')[1] || '0', 10);
const ENTRY = 'Add wallcovering', FULL = '*List Wallpapers - Full View';
const PSQL = process.env.PSQL_BIN || '/opt/homebrew/opt/postgresql@14/bin/psql';
const DB = 'postgresql:///dw_unified?host=/tmp&user=stevestudio2';
const sleep = ms => new Promise(r => setTimeout(r, ms));

const MATWORD = { Silk:'Silk', Grass:'Grasscloth', Wood:'Wood Veneer', Suede:'Suede', Linen:'Linen',
  Raffia:'Raffia', 'Paper Weave':'Paper Weave', Abaca:'Abaca', Hemp:'Hemp', Sisal:'Sisal', Jute:'Jute',
  Velvet:'Velvet', Wool:'Wool', Arrowroot:'Arrowroot', 'Cotton Yarn':'Cotton', 'Water hyacinth':'Water Hyacinth',
  'Metal Leaf':'Metal Leaf' };
const matWord = m => (MATWORD[m] ?? '');
const widthOf = w => { const s = String(w||'');
  const inch = s.match(/([\d.]+)\s*(?:''|")/); if (inch) return `${Math.round(parseFloat(inch[1]))}"`;
  const cm = s.match(/([\d.]+)\s*cm/); if (cm) return `${Math.round(parseFloat(cm[1])/2.54)}"`;
  const n = s.match(/([\d.]+)/); return n ? `${Math.round(parseFloat(n[1]))}"` : ''; };

const rows = JSON.parse(execFileSync(PSQL, [DB, '-tAc',
  `select coalesce(json_agg(json_build_object('sku',dw_sku,'prefix',prefix,'num',split_part(dw_sku,'-',2),'mfr',mfr_model,'city',city,'mat',material,'color',clean_color,'wi',width) order by dw_sku)::text,'[]')
   from greenland_full_catalog`], { encoding:'utf8', maxBuffer:128e6 }).trim());

async function exists(prefix, num) {
  const r = await fm.findRecords('WALLPAPER', FULL, { 'Series':'=='+prefix, 'JS Pattern':'=='+num }, { limit:1 }).catch(()=>({records:[]}));
  return (r.records?.length||0) > 0;
}

async function main() {
  let todo = rows.slice(OFFSET, LIMIT ? OFFSET+LIMIT : undefined);
  console.log(`greenland_full_catalog: ${rows.length} rows · this run: ${todo.length} (offset ${OFFSET}) · ${APPLY?'APPLY':'DRY-RUN'}`);
  let ok=0, skip=0, fail=0;
  for (const r of todo) {
    const mw = matWord(r.mat);
    const name = `${r.city} ${mw ? mw+' ' : ''}Wallcovering`.replace(/\s+/g,' ').trim();
    const fd = { Series:r.prefix, 'JS Pattern':r.num, 'Mfr Pattern':r.mfr,
      'Name of Pattern':name, 'Color of Pattern':r.color, Width:widthOf(r.wi), vid:'GREEN' };
    if (!APPLY) { if (ok<6) console.log(`  [${r.sku}] Series=${r.prefix} JS=${r.num} Mfr=${r.mfr} "${name}" (${r.color}) w=${fd.Width}`); ok++; continue; }
    try {
      if (await exists(r.prefix, r.num)) { skip++; continue; }
      await fm.createRecord('WALLPAPER', ENTRY, fd, { dryRun:false });
      ok++; if (ok%25===0) console.log(`  ...created ${ok} (skip ${skip}, fail ${fail})`);
      await sleep(120);
    } catch (e) { fail++; console.error(`  FAIL ${r.sku}: ${e.fmCode||e.message}`); await sleep(400); }
  }
  console.log(`\nDONE. ${APPLY?'created':'planned'}=${ok} skipped=${skip} fail=${fail}`);
}
main().catch(e=>{console.error(e);process.exit(1);});