← 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);});