// Create the `suppliers` collection and link purchase orders + cash-float slips to it (idempotent). // // One supplier list for the whole business. Every purchase order and every petty-cash slip is // booked against a supplier, so "how much did we spend at X" is one query. // // terms "cash" paid on the spot (petty cash) // "30" account, payable in 30 days // "60" account, payable in 60 days // "" not set yet — a supplier that appeared on a PO before anyone classified it // // `key` is the supplier's name folded (case, punctuation, PTY/LTD/CC removed) and is UNIQUE, so // "Lube Force Traders cc" and "LUBE FORCE TRADERS" cannot become two suppliers. // // Usage: node scripts/setup-suppliers-collection.mjs import PocketBase from "pocketbase"; import { readFileSync } from "node:fs"; for (const l of readFileSync(new URL("../.env.local", import.meta.url), "utf8").split("\n")) { const i = l.indexOf("="); if (i > 0 && !l.trim().startsWith("#")) process.env[l.slice(0, i).trim()] = l.slice(i + 1).trim(); } const pb = new PocketBase(process.env.NEXT_PUBLIC_PB_URL); pb.autoCancellation(false); await pb.collection("_superusers").authWithPassword(process.env.PB_ADMIN, process.env.PB_PASS); const ref = await pb.collections.getOne("monthly_income"); const rules = { listRule: ref.listRule ?? "", viewRule: ref.viewRule ?? "", createRule: ref.createRule ?? "", updateRule: ref.updateRule ?? "", deleteRule: ref.deleteRule ?? "", }; const fields = [ { name: "name", type: "text", required: true }, { name: "key", type: "text", required: true }, { name: "code", type: "text" }, { name: "terms", type: "text" }, { name: "notes", type: "text" }, { name: "source", type: "text" }, ]; const indexes = ["CREATE UNIQUE INDEX idx_suppliers_key ON suppliers (key)"]; let suppliers = (await pb.collections.getFullList({ filter: 'name = "suppliers"', perPage: 1 }))[0]; if (!suppliers) { suppliers = await pb.send("/api/collections", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "suppliers", type: "base", fields, indexes, ...rules }) }); console.log(` ✓ created suppliers (${suppliers.id})`); } else console.log(" · suppliers already present"); for (const target of ["purchases", "cash_float_entries"]) { const c = await pb.collections.getOne(target).catch(() => null); if (!c) { console.log(` ! ${target} missing — skipped`); continue; } if (c.fields.some((f) => f.name === "supplier_ref")) { console.log(` · ${target}.supplier_ref already present`); continue; } await pb.send(`/api/collections/${target}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ fields: [...c.fields, { name: "supplier_ref", type: "relation", collectionId: suppliers.id, minSelect: 0, maxSelect: 1, cascadeDelete: false }] }) }); console.log(` ✓ added ${target}.supplier_ref`); }