import "./_env-loader"; import { getAdminPocketBase } from "@/lib/server/pocketbase-admin"; /** * Migration — 2026-08-13-fix-delivery-notes-relations.ts * * `delivery_notes` was created with relation fields (`customer`, * `company`, `order`, `created_by`) but the live schema has * `collectionId: null` on those fields, so any `expand=...` query * silently returns nothing. The list page relies on * `expand=company.company` for per-company grouping, so all DNs * collapse into a single "No company" bucket — and crucially, the * records remain in the table, but grouped wrong (and reported * missing when the operator searches by number). * * This migration patches the relation targets so expansion resolves, * then backfills `delivery_notes.company` from the parent order's * company where it's currently empty (defensive — most records * already have the right value from the original create path). * * Run: * npx tsx scripts/migrations/2026-08-13-fix-delivery-notes-relations.ts */ const CUSTOMERS = "customers"; const COMPANIES = "companies"; const ORDERS = "orders"; const USERS = "users"; const DELIVERY_NOTES = "delivery_notes"; async function patchRelation( pb: Awaited>, collectionId: string, fieldName: string, targetCollectionId: string, log: string[], ): Promise { const collection = await pb.collections.getOne(collectionId); const field = (collection.fields ?? []).find((f) => f.name === fieldName); if (!field) { log.push(` ✗ ${fieldName} not found on ${collection.name}`); return; } if (field.type !== "relation") { log.push(` ✗ ${fieldName} is not a relation (type=${field.type})`); return; } const opts = (field.options ?? {}) as { collectionId?: string | null }; if (opts.collectionId === targetCollectionId) { log.push(` · ${fieldName}.collectionId already set — skip`); return; } log.push(` → patching ${fieldName}.collectionId to ${targetCollectionId}`); await pb.collections.update(collection.id, { fields: [ { ...field, options: { ...opts, collectionId: targetCollectionId } }, ], }); } async function backfillCompany( pb: Awaited>, log: string[], ): Promise<{ updated: number; skipped: number; failed: number }> { let updated = 0; let skipped = 0; let failed = 0; const records = await pb.collection(DELIVERY_NOTES).getFullList<{ id: string; company: string; order: string; }>({ filter: "company = '' || company = null" }); log.push(` · ${records.length} delivery note(s) need a company backfill`); for (const dn of records) { if (!dn.order) { skipped += 1; continue; } try { const order = await pb.collection(ORDERS).getOne(dn.order, { fields: "company", }); const company = (order as unknown as { company?: string }).company; if (!company) { skipped += 1; continue; } await pb.collection(DELIVERY_NOTES).update(dn.id, { company }); updated += 1; } catch (err) { failed += 1; log.push( ` ✗ ${dn.id} (${dn.delivery_note_number || dn.id}): ${err instanceof Error ? err.message : String(err)}`, ); } } return { updated, skipped, failed }; } async function main() { const log: string[] = []; log.push("[migration:fix-delivery-notes-relations] starting"); const pb = await getAdminPocketBase(); const customersCol = await pb.collections.getOne(CUSTOMERS); const companiesCol = await pb.collections.getOne(COMPANIES); const ordersCol = await pb.collections.getOne(ORDERS); const usersCol = await pb.collections.getOne(USERS); log.push("[migration:fix-delivery-notes-relations] step 1 — patch relation targets"); await patchRelation(pb, DELIVERY_NOTES, "customer", customersCol.id, log); await patchRelation(pb, DELIVERY_NOTES, "company", companiesCol.id, log); await patchRelation(pb, DELIVERY_NOTES, "order", ordersCol.id, log); await patchRelation(pb, DELIVERY_NOTES, "created_by", usersCol.id, log); log.push("[migration:fix-delivery-notes-relations] step 2 — backfill empty company from order"); const { updated, skipped, failed } = await backfillCompany(pb, log); log.push( `[migration:fix-delivery-notes-relations] updated=${updated} skipped=${skipped} failed=${failed}`, ); log.push("[migration:fix-delivery-notes-relations] done"); for (const line of log) console.log(line); } main().catch((err) => { console.error("[migration:fix-delivery-notes-relations] fatal:", err); process.exit(1); });