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
139 lines
4.2 KiB
TypeScript
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);
|
|
});
|