WFDASH-WEBAPP/scripts/migrations/2026-07-15-stock-purchase-line-state.ts
SJCoetz 71f7671e89 feat: add stock line support to collection pool
Add full integration for stock supplier pickup lines into the collection pool workflow:
- Add two idempotent database migrations for stock line drv_id and state tracking
- Update stock purchase number format to POyyMMddNNSTK convention
- Extend collection pool to load both customer order items and stock purchase items
- Add stock-specific UI, driver assignment flow, and mark collected action
- Update type definitions and service logic for stock purchases and line items
- Add comprehensive test coverage for all new workflows
2026-07-16 20:41:42 +02:00

139 lines
4.2 KiB
TypeScript

/* eslint-disable */
// scripts/migrations/2026-07-15-stock-purchase-line-state.ts
//
// One-shot schema migration for the v2 stock-order driver flow:
//
// 1. `stock_purchases` — add a `documents` file field for the
// generated PO PDF (and any other attachments the operator
// wants to send to the supplier).
// 2. `stock_purchase_items` — add a `status` select field for the
// per-line state machine
// (collection_pool | assigned_to_driver | collected | booked)
// and an `assigned_at` date field for the driver-assignment
// timestamp.
//
// Idempotent: re-running is a no-op when the fields are already
// present.
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 {
// .env.local may not exist; fall through.
}
}
loadEnvFile(".env.local");
import { getAdminPocketBase } from "@/lib/server/pocketbase-admin";
const STOCK_PURCHASES = "stock_purchases";
const STOCK_PURCHASE_ITEMS = "stock_purchase_items";
const LINE_STATUS_VALUES = [
"collection_pool",
"assigned_to_driver",
"collected",
"booked",
];
async function main() {
const pb = await getAdminPocketBase();
// ── 1. stock_purchases.documents ──────────────────────────────
{
const col = await pb.collections.getOne(STOCK_PURCHASES);
const fields = (col.fields ?? []) as Array<{ name: string }>;
if (fields.some((f) => f.name === "documents")) {
console.log(`[stock-purchase-line-state] stock_purchases.documents already present`);
} else {
const newField = {
name: "documents",
type: "file",
required: false,
presentable: false,
protected: false,
maxSelect: 5, // mirror the customer PO: up to 5 attachments
maxSize: 0, // no PB cap
mimeTypes: null,
thumbs: null,
};
await pb.collections.update(col.id, { fields: [...fields, newField] });
console.log(
`[stock-purchase-line-state] added documents (file, multi, any mime) to stock_purchases`,
);
}
}
// ── 2. stock_purchase_items.status + assigned_at ─────────────
{
const col = await pb.collections.getOne(STOCK_PURCHASE_ITEMS);
const fields = (col.fields ?? []) as Array<{ name: string }>;
let next = fields;
let added = false;
if (!fields.some((f) => f.name === "status")) {
// Append at the end so existing reads / field indexes aren't
// disturbed. Select field with the 4 state values.
next = [
...next,
{
name: "status",
type: "select",
required: false,
maxSelect: 1,
values: LINE_STATUS_VALUES,
},
];
added = true;
}
if (!fields.some((f) => f.name === "assigned_at")) {
// Mirrors `order_items.assigned_at` — the timestamp the line
// was bound to a driver. `null` = unassigned. We don't make it
// required because lines that haven't been sent yet (status
// null) have no assignment.
next = [
...next,
{
name: "assigned_at",
type: "date",
required: false,
presentable: false,
},
];
added = true;
}
if (added) {
await pb.collections.update(col.id, { fields: next });
console.log(
`[stock-purchase-line-state] added status (select) + assigned_at (date) to stock_purchase_items`,
);
} else {
console.log(
`[stock-purchase-line-state] stock_purchase_items.status + assigned_at already present`,
);
}
}
console.log(
`[stock-purchase-line-state] done. Verify via PB admin or by running the existing service tests.`,
);
}
main().catch((err) => {
console.error("[stock-purchase-line-state] failed:", err);
process.exit(1);
});