// scripts/migrations/2026-07-17-add-stock-purchase-problem-fields.ts // // Adds the operator-side problem-tracking fields to // `stock_purchase_items`, mirroring the same fields on // `order_items`: // // - problem_flag (bool) — driver reported an issue at the // supplier (short-ship, wrong item, // damaged, etc.) // - problem_type (text) — structured category of the issue // (mirrors `order_items.problem_type`) // - problem_notes (text) — free-text detail // // These three fields are added in one migration so the v1 // driver-app PATCH can stamp them in a single PB write. The // fields are all optional — most lines never have a problem. // // Idempotent: re-running is a no-op when the fields are already // present. /* eslint-disable */ import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import PocketBase from "pocketbase"; function loadEnvFile(name: string) { try { const raw = readFileSync(resolve(process.cwd(), name), "utf8"); for (const line of raw.split("\n")) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith("#")) continue; const m = trimmed.match(/^([A-Z0-9_]+)\s*=\s*(.+)$/i); if (m && !process.env[m[1]]) { process.env[m[1]] = m[2].replace(/^["']|["']$/g, "").trim(); } } } catch { // .env.local doesn't exist — fall through. } } loadEnvFile(".env.local"); const COLLECTION_NAME = "stock_purchase_items"; async function main() { const url = process.env.NEXT_PUBLIC_POCKETBASE_URL; const email = process.env.PB_ADMIN_EMAIL; const password = process.env.PB_ADMIN_PASSWORD; if (!url || !email || !password) { throw new Error( "NEXT_PUBLIC_POCKETBASE_URL, PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD must be set.", ); } const pb = new PocketBase(url); await pb.collection("_superusers").authWithPassword(email, password); const collection = await pb.collections.getOne(COLLECTION_NAME); const existing = (collection.fields ?? []) as Array<{ name: string }>; const have = (n: string) => existing.some((f) => f.name === n); const additions: Array> = []; if (!have("problem_flag")) { additions.push({ name: "problem_flag", type: "bool", required: false, presentable: false, }); } if (!have("problem_type")) { additions.push({ name: "problem_type", type: "text", required: false, presentable: false, // No PB-side max — service layer clamps. Matches the // shape of `order_items.problem_type` (free-text in // practice, but operator types into a small set of // known values). max: 0, }); } if (!have("problem_notes")) { additions.push({ name: "problem_notes", type: "text", required: false, presentable: false, max: 0, }); } if (additions.length === 0) { console.log( `[stock-purchase-problem-fields] all three fields already present on ${COLLECTION_NAME}`, ); return; } await pb.collections.update(collection.id, { fields: [...existing, ...additions], }); console.log( `[stock-purchase-problem-fields] added ${additions.length} field(s) to ${COLLECTION_NAME}: ${additions.map((a) => a.name).join(", ")}`, ); } main().catch((err) => { console.error("[stock-purchase-problem-fields] failed:", err); process.exit(1); });