92 lines
2.8 KiB
TypeScript
92 lines
2.8 KiB
TypeScript
/**
|
|
* One-shot backfill — write `token` into every PurchaseOrderDocumentEntry
|
|
* in `purchase_orders.documents_history` that doesn't have one yet.
|
|
*
|
|
* Why this exists
|
|
* ────────────────
|
|
* The PO PDF route used to store the human-readable filename in
|
|
* `documents_history[i].token`, which then 404'd when PB's file proxy
|
|
* was asked to resolve it. The fix in the route handler stores the
|
|
* real on-disk token (the slug PB returns from the upload) instead —
|
|
* but legacy rows created before that change still have `file` and no
|
|
* `token`, so the new GET handler would 404 on them.
|
|
*
|
|
* Strategy:
|
|
* - For each PO row, read `documents_history` and `documents`.
|
|
* - For each entry missing `token`, look up `documents[idx]` (or
|
|
* `documents[0]` as a fallback). The two arrays are kept in sync
|
|
* by the POST handler, so index alignment is correct.
|
|
* - Patch the row with the new history array.
|
|
*
|
|
* Idempotent — re-running this script is a no-op once every entry has
|
|
* a token.
|
|
*
|
|
* Run with (from WFDASH_NEXTJS so .env.local is found):
|
|
* npx tsx scripts/migrations/2026-07-09-po-pdf-tokens.ts
|
|
*/
|
|
import { getAdminPocketBase } from "@/lib/server/pocketbase-admin";
|
|
|
|
type Entry = {
|
|
version: number;
|
|
file: string;
|
|
token?: string;
|
|
generatedAt: string;
|
|
generatedBy: string;
|
|
reason: string;
|
|
status: "current" | "superseded";
|
|
};
|
|
|
|
type PoRow = {
|
|
id: string;
|
|
documents?: string[] | string;
|
|
documents_history?: Entry[];
|
|
};
|
|
|
|
async function main() {
|
|
const pb = await getAdminPocketBase();
|
|
let patchedRows = 0;
|
|
let patchedEntries = 0;
|
|
let unresolvable = 0;
|
|
|
|
const list = await pb.collection("purchase_orders").getList<PoRow>(1, 500, {
|
|
fields: "id,documents,documents_history",
|
|
filter: "documents_history != null",
|
|
});
|
|
|
|
for (const po of list.items) {
|
|
const history = po.documents_history ?? [];
|
|
if (history.length === 0) continue;
|
|
|
|
const docsRaw = po.documents ?? [];
|
|
const docs = Array.isArray(docsRaw) ? docsRaw : docsRaw ? [docsRaw] : [];
|
|
|
|
let changed = false;
|
|
const newHistory = history.map((entry, idx) => {
|
|
if (entry.token) return entry;
|
|
const token = docs[idx] || docs[0] || "";
|
|
if (!token) {
|
|
unresolvable += 1;
|
|
return entry;
|
|
}
|
|
changed = true;
|
|
return { ...entry, token };
|
|
});
|
|
|
|
if (changed) {
|
|
await pb.collection("purchase_orders").update(po.id, {
|
|
documents_history: newHistory,
|
|
});
|
|
patchedRows += 1;
|
|
patchedEntries += newHistory.filter((e) => e.token).length - history.filter((e) => e.token).length;
|
|
}
|
|
}
|
|
|
|
console.log(
|
|
`[po-pdf-tokens] rows patched=${patchedRows} entries patched=${patchedEntries} unresolvable=${unresolvable}`,
|
|
);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("[po-pdf-tokens] failed:", err);
|
|
process.exit(1);
|
|
});
|