/** * Maps scripts/nss-hex-bolts-din931-pilot.csv into PocketBase * `stock_items` records for review. * * Output: scripts/nss-hex-bolts-din931-pb-import.json */ import { readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; const SOURCE_CSV = join(import.meta.dirname, "nss-hex-bolts-din931-pilot.csv"); const OUTPUT_JSON = join(import.meta.dirname, "nss-hex-bolts-din931-pb-import.json"); const SOURCE_URL = "https://nssfasteners.co.za/hexagon-head-fasteners/hex-head-bolts/hex-bolt-din931.html?gradematerial=772,773"; const SOURCE_LABEL = "NSS Fasteners price list - hex bolt DIN931 (Grade 8.8 + A2)"; const EXTRACTED_AT = "2026-09-16"; const SUPPLIER_NSS_ID = "6xuu7a0pmmubtqd"; const CATEGORY_FASTENERS_ID = "4emvaanzogj0586"; const MARKUP_PERCENT = 80; type CsvRow = { sku: string; posDescription: string; description: string; material: string; grade: string; diameter: string; metric: string; imperial: string; finish: string; pack: string; unitPrice: string; url: string; }; const parsePrice = (s: string): number | null => { const m = s.match(/([\d.]+)/); if (!m) return null; const n = parseFloat(m[1]); return Number.isFinite(n) ? n : null; }; const round2 = (n: number) => Math.round(n * 100) / 100; const toPbRecord = (r: CsvRow) => { const costPrice = parsePrice(r.unitPrice); const notes = [ `Material: ${r.material}`, `Grade: ${r.grade}`, `Diameter: ${r.diameter}`, `Length: ${r.length}`, `Finish: ${r.finish}`, r.pack ? `Pack rule: ${r.pack}` : "", `Source: ${SOURCE_LABEL}`, ] .filter(Boolean) .join(" | "); const costHistory = [ { price: costPrice, currency: "ZAR", source: "nssfasteners.co.za", extracted_at: EXTRACTED_AT, source_url: SOURCE_URL, qty_basis: 1, extraction_method: "browser_automation", notes: SOURCE_LABEL, }, ]; return { name: r.posDescription, description: r.posDescription, sku: r.sku, location: SOURCE_LABEL, notes, cost_history: costHistory, cost_price: costPrice, selling_price: costPrice != null ? round2(costPrice * (1 + MARKUP_PERCENT / 100)) : 0, markup: MARKUP_PERCENT, unit: r.length || "Each", // bolts sold by length current_qty: 0, min_qty: 0, target_qty: 0, is_active: true, supplier: SUPPLIER_NSS_ID, category: CATEGORY_FASTENERS_ID, }; }; const csv = readFileSync(SOURCE_CSV, "utf8").trim(); const [headerLine, ...dataLines] = csv.split("\n"); const headers = headerLine.split(",") as (keyof CsvRow)[]; const rows: CsvRow[] = dataLines.map((line) => { const cols = line.split(","); const obj = {} as CsvRow; headers.forEach((h, i) => { (obj as Record)[h] = cols[i] ?? ""; }); return obj; }); const records = rows.map(toPbRecord); writeFileSync( OUTPUT_JSON, JSON.stringify( { meta: { source_csv: "scripts/nss-hex-bolts-din931-pilot.csv", source_url: SOURCE_URL, extracted_at: EXTRACTED_AT, row_count: records.length, pb_collection: "stock_items", notes: "selling_price = cost_price * (1 + 80%). name=description both carry the PosCode. unit set to length (e.g. '65 mm') since these bolts are sold by length.", }, records, }, null, 2, ), ); console.log(`Wrote ${records.length} records to ${OUTPUT_JSON}`);