This commit adds the complete v2/v3 stock management functionality: - add new stock-specific item statuses and update the status visual map - implement full stock purchase API suite (create, list, retrieve, send, receive) - reorganize sidebar navigation to group stock-related pages together - add stock item CRUD APIs, status utilities, low stock filtering, and surplus booking logic - add database migration for the `stock_items.target_qty` field - update type definitions and add comprehensive tests for all new stock components
72 lines
2.1 KiB
TypeScript
72 lines
2.1 KiB
TypeScript
// scripts/migrations/2026-07-15-add-stock-items-target-qty.ts
|
|
//
|
|
// One-shot schema migration: add a `target_qty` number field to the
|
|
// `stock_items` collection. Used by the bulk-PO-from-low-stock flow
|
|
// (see src/app/(app)/stock-orders/new/page.tsx) to compute the order
|
|
// qty per line: `order_qty = max(0, target_qty - current_qty)`. Items
|
|
// with `target_qty` unset or 0 are opted out of the bulk flow.
|
|
//
|
|
// Idempotent: re-running is a no-op when the field is already present.
|
|
|
|
/* eslint-disable */
|
|
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 doesn't exist — fall through; PB env vars may be
|
|
// set in the shell.
|
|
}
|
|
}
|
|
|
|
loadEnvFile(".env.local");
|
|
|
|
import { getAdminPocketBase } from "@/lib/server/pocketbase-admin";
|
|
|
|
const COLLECTION_NAME = "stock_items";
|
|
const FIELD_NAME = "target_qty";
|
|
|
|
async function main() {
|
|
const pb = await getAdminPocketBase();
|
|
|
|
const collection = await pb.collections.getOne(COLLECTION_NAME);
|
|
const existing = (collection.fields ?? []) as Array<{ name: string }>;
|
|
if (existing.some((f) => f.name === FIELD_NAME)) {
|
|
console.log(`[stock-items-target-qty] field already present on ${COLLECTION_NAME}`);
|
|
return;
|
|
}
|
|
|
|
const newField = {
|
|
name: FIELD_NAME,
|
|
type: "number",
|
|
required: false,
|
|
options: {
|
|
min: 0,
|
|
noDecimal: false,
|
|
},
|
|
};
|
|
|
|
await pb.collections.update(collection.id, {
|
|
fields: [...existing, newField],
|
|
});
|
|
|
|
console.log(
|
|
`[stock-items-target-qty] created field on ${COLLECTION_NAME} (number, optional, min 0)`,
|
|
);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("[stock-items-target-qty] failed:", err);
|
|
process.exit(1);
|
|
});
|
|
|