/** * Backfill — flip `ready_for_collection` to `true` on every row that is * already `item_status = "assigned"` but never had the flag set. * * Why this exists as a sibling to the original migration: * * `2026-07-07-order-items-ready-for-collection.ts` introduced the * `ready_for_collection` bool AND backfilled every existing * `collection_pool` row to `true`. It deliberately did NOT touch * rows that were already `assigned` — at the time the migration * ran, the theory was "assigned items have already been hand-vetted * by the operator, so the flag should already be on them, so we * don't need to backfill". * * That theory was wrong. In practice, several `assigned` rows had * been created via paths that did not set the flag (direct PB admin * edits, an older assignment workflow that pre-dated the column). * The driver app's `getMyAssignedItems` filter requires * `ready_for_collection = true`, so the driver sees an empty * Assigned tab even though they have real stops in the DB. * * This script mirrors the original pool-backfill shape so the gap * closes without forcing the operator to re-toggle every row in the * dashboard. * * Idempotent — only flips rows where `ready_for_collection != true`. * * Run with (from WFDASH_NEXTJS so .env.local is found): * npx tsx scripts/migrations/2026-07-07-backfill-assigned-rfc.ts */ /* eslint-disable */ import PocketBase from "pocketbase"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; 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 { /* fine — rely on shell env */ } } loadEnvFile(".env"); loadEnvFile(".env.local"); 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( "Set NEXT_PUBLIC_POCKETBASE_URL, PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD (e.g. sourced from .env.local).", ); } const pb = new PocketBase(url); await pb.collection("_superusers").authWithPassword(email, password); console.log(`[backfill-assigned-rfc] Authenticated against ${url}`); // Sanity check — make sure the column exists before we query on it. const col = await pb.collections.getOne("order_items"); const hasField = (col.fields as Array<{ name: string }>).some( (f) => f.name === "ready_for_collection", ); if (!hasField) { throw new Error( "ready_for_collection field missing on order_items — run 2026-07-07-order-items-ready-for-collection.ts first.", ); } // Find every assigned row that isn't already ready. // `!= true` matches false AND null — null is the default for rows // created before the column existed, plus any rows where the // boolean was never written. const rows = await pb.collection("order_items").getFullList({ filter: 'item_status = "assigned" && ready_for_collection != true', fields: "id,item_status,ready_for_collection,drv_id", }); console.log( `[backfill-assigned-rfc] candidates (assigned, rfc!=true): ${rows.length}`, ); let flipped = 0; let failed = 0; for (const row of rows) { try { await pb.collection("order_items").update(row.id, { ready_for_collection: true, }); flipped++; } catch (err) { failed++; console.error( `[backfill-assigned-rfc] update failed for ${row.id}:`, err instanceof Error ? err.message : err, ); } } // Post-state snapshot — confirm the filter now returns 0. const remaining = await pb.collection("order_items").getFullList({ filter: 'item_status = "assigned" && ready_for_collection != true', fields: "id", }); console.log(`[backfill-assigned-rfc] flipped: ${flipped}`); console.log(`[backfill-assigned-rfc] failed: ${failed}`); console.log( `[backfill-assigned-rfc] post-state: ${remaining.length} assigned rows still have rfc!=true`, ); console.log("[backfill-assigned-rfc] Done."); } main().catch((err) => { console.error("[backfill-assigned-rfc] failed:", err); process.exit(1); });