// The delivery-note inbox board, as text. Read-only. // // Renders what /dashboard/financial/delivery-notes/inbox would show, so // the pipeline can be checked over SSH or while the app is mid-deploy. // Batches hold the left-hand columns; once assembly is settled the batch // dissolves and its documents continue individually. // // npx tsx --env-file=.env.local scripts/dn-board.mts // // .mts, not .ts: top-level await, and the package has no "type": "module". import { pbAdmin } from "../src/lib/pb"; import { displayBatchStage, displayCandidateStage } from "../src/lib/dn-pipeline/stages"; type Row = Record; const COLUMNS: { key: string; label: string }[] = [ { key: "queued", label: "Queued" }, { key: "reading", label: "Reading photos" }, { key: "split_review", label: "Check page grouping" }, { key: "needs_review", label: "Needs matching" }, { key: "ready_to_post", label: "Ready to post" }, { key: "posted", label: "Complete" }, ]; const pb = await pbAdmin(); const batches = (await pb.collection("dn_batches").getFullList()) as Row[]; const notes = (await pb.collection("dn_batch_notes").getFullList({ expand: "supplier" })) as Row[]; const batchById = new Map(batches.map((batch) => [batch.id, batch])); const columns: Record = Object.fromEntries(COLUMNS.map((column) => [column.key, []])); for (const batch of batches) { const stage = displayBatchStage(batch.status ?? "", batch.stage ?? ""); if (stage === "failed") continue; if (columns[stage] && stage !== "complete") { columns[stage].push(`BATCH ${batch.id} · ${batch.source_count ?? 0} source(s) · ${batch.page_count ?? 0} page(s)`); } } for (const note of notes) { const batch = batchById.get(note.batch); if (!batch || displayBatchStage(batch.status ?? "", batch.stage ?? "") !== "complete") continue; const stage = displayCandidateStage(note.status ?? "", note.stage ?? ""); // `matching` is the runner's transient state; to an operator it is the // same outstanding job as needs_review. const key = stage === "matching" ? "needs_review" : stage; if (!columns[key]) continue; const supplier = note.expand?.supplier?.name ?? note.supplier_name_raw ?? "supplier unknown"; columns[key].push(`${note.doc_number || "(unnumbered)"} — ${supplier}`); } for (const { key, label } of COLUMNS) { console.log(`\n${label} (${columns[key].length})`); for (const line of columns[key]) console.log(` • ${line}`); if (columns[key].length === 0) console.log(" —"); } const failed = batches.filter((batch) => displayBatchStage(batch.status ?? "", batch.stage ?? "") === "failed"); if (failed.length > 0) { console.log(`\nFailed (${failed.length})`); for (const batch of failed) console.log(` • ${batch.id} — ${batch.last_error || "no error recorded"}`); }