Add complete stock purchase order workflow including: - Status tracking with sending/mail_failed states and UI labels - Server-side PDF rendering and document storage - File proxy API support for stock PO attachments - Send outcome webhook callback for email notifications Add driver app support: - Unified stop list API for both customer deliveries and stock pickups - Status update endpoint for collected quantities and problem reporting - Collection map page support for stock pickup locations Include matching PocketBase migrations, type definitions, and stock inventory fulfillment logic.
114 lines
3.4 KiB
TypeScript
114 lines
3.4 KiB
TypeScript
// scripts/migrations/2026-07-17-add-stock-purchase-problem-fields.ts
|
|
//
|
|
// Adds the operator-side problem-tracking fields to
|
|
// `stock_purchase_items`, mirroring the same fields on
|
|
// `order_items`:
|
|
//
|
|
// - problem_flag (bool) — driver reported an issue at the
|
|
// supplier (short-ship, wrong item,
|
|
// damaged, etc.)
|
|
// - problem_type (text) — structured category of the issue
|
|
// (mirrors `order_items.problem_type`)
|
|
// - problem_notes (text) — free-text detail
|
|
//
|
|
// These three fields are added in one migration so the v1
|
|
// driver-app PATCH can stamp them in a single PB write. The
|
|
// fields are all optional — most lines never have a problem.
|
|
//
|
|
// Idempotent: re-running is a no-op when the fields are already
|
|
// present.
|
|
|
|
/* eslint-disable */
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import PocketBase from "pocketbase";
|
|
|
|
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 doesn't exist — fall through.
|
|
}
|
|
}
|
|
|
|
loadEnvFile(".env.local");
|
|
|
|
const COLLECTION_NAME = "stock_purchase_items";
|
|
|
|
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(
|
|
"NEXT_PUBLIC_POCKETBASE_URL, PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD must be set.",
|
|
);
|
|
}
|
|
|
|
const pb = new PocketBase(url);
|
|
await pb.collection("_superusers").authWithPassword(email, password);
|
|
|
|
const collection = await pb.collections.getOne(COLLECTION_NAME);
|
|
const existing = (collection.fields ?? []) as Array<{ name: string }>;
|
|
const have = (n: string) => existing.some((f) => f.name === n);
|
|
|
|
const additions: Array<Record<string, unknown>> = [];
|
|
if (!have("problem_flag")) {
|
|
additions.push({
|
|
name: "problem_flag",
|
|
type: "bool",
|
|
required: false,
|
|
presentable: false,
|
|
});
|
|
}
|
|
if (!have("problem_type")) {
|
|
additions.push({
|
|
name: "problem_type",
|
|
type: "text",
|
|
required: false,
|
|
presentable: false,
|
|
// No PB-side max — service layer clamps. Matches the
|
|
// shape of `order_items.problem_type` (free-text in
|
|
// practice, but operator types into a small set of
|
|
// known values).
|
|
max: 0,
|
|
});
|
|
}
|
|
if (!have("problem_notes")) {
|
|
additions.push({
|
|
name: "problem_notes",
|
|
type: "text",
|
|
required: false,
|
|
presentable: false,
|
|
max: 0,
|
|
});
|
|
}
|
|
|
|
if (additions.length === 0) {
|
|
console.log(
|
|
`[stock-purchase-problem-fields] all three fields already present on ${COLLECTION_NAME}`,
|
|
);
|
|
return;
|
|
}
|
|
|
|
await pb.collections.update(collection.id, {
|
|
fields: [...existing, ...additions],
|
|
});
|
|
console.log(
|
|
`[stock-purchase-problem-fields] added ${additions.length} field(s) to ${COLLECTION_NAME}: ${additions.map((a) => a.name).join(", ")}`,
|
|
);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("[stock-purchase-problem-fields] failed:", err);
|
|
process.exit(1);
|
|
});
|