- server/pb-mcp.mjs: 15-tool MCP server for purchase-order workflow (CRUD, suppliers, stock, customers, customer POs, validate_draft, generate_order_number) - src/lib/api/purchaseOrders.ts: typed helpers shared by UI + MCP + n8n - src/components/purchase-orders/pdf.ts: jsPDF PO generator - src/components/purchase-orders/ViewOrderDialog.tsx: read-only dialog - src/components/purchase-orders/AttachmentsDialog.tsx: attachment mgmt - src/components/purchase-orders/NumberInput.tsx: numeric input - scripts/mcp-create-po.mjs: end-to-end driver (MCP stdio smoke test) - pocketbase/pb_migrations/1786200007_*: schema migration - docs/MCP_AND_N8N.md: MCP + n8n integration guide Verified end-to-end via MCP: generate_order_number → create_purchase_order with snapshot_items for an actual quote (DRILL-19042 / 161053).
62 lines
2.4 KiB
JavaScript
62 lines
2.4 KiB
JavaScript
/// <reference path="../pb_data/types.d.ts" />
|
|
|
|
// =========================================================================
|
|
// purchase_orders — extend status enum + add soft-delete flag
|
|
// =========================================================================
|
|
//
|
|
// What this migration does:
|
|
// 1. Extends the `status` select enum with `Approved` and `Rejected`
|
|
// so the row-menu "Approve" / "Reject" actions stop failing with 400.
|
|
// 2. Adds a `deleted` boolean (default false). The Purchase Orders list
|
|
// filters it out so soft-deletes don't clutter the table.
|
|
// (We avoid hard-deletes so historical invoices / attachments remain
|
|
// traceable; the record is hidden from the UI, not erased from PB.)
|
|
// =========================================================================
|
|
|
|
migrate((app) => {
|
|
const collection = app.findCollectionByNameOrId("purchase_orders");
|
|
|
|
if (!collection) {
|
|
throw new Error("purchase_orders collection not found — aborting.");
|
|
}
|
|
|
|
// -- 1. extend status enum ---------------------------------------------
|
|
const statusField = collection.fields.find((f) => f.name === "status");
|
|
if (statusField && statusField.type === "select") {
|
|
const wanted = ["Pending", "Pending Approval", "Approved", "Ordered", "Received", "Rejected"];
|
|
const current = Array.isArray(statusField.values) ? statusField.values : [];
|
|
const merged = Array.from(new Set([...current, ...wanted]));
|
|
statusField.values = merged;
|
|
}
|
|
|
|
// -- 2. add `deleted` bool ---------------------------------------------
|
|
const hasDeleted = collection.fields.some((f) => f.name === "deleted");
|
|
if (!hasDeleted) {
|
|
collection.fields.push({
|
|
hidden: false,
|
|
id: "bool_po_deleted",
|
|
name: "deleted",
|
|
presentable: false,
|
|
required: false,
|
|
system: false,
|
|
type: "bool",
|
|
});
|
|
}
|
|
|
|
return app.save(collection);
|
|
}, (app) => {
|
|
// Rollback: remove the values we added (keep historical ones intact) and
|
|
// drop the `deleted` field.
|
|
const collection = app.findCollectionByNameOrId("purchase_orders");
|
|
if (!collection) return;
|
|
|
|
const statusField = collection.fields.find((f) => f.name === "status");
|
|
if (statusField && Array.isArray(statusField.values)) {
|
|
statusField.values = statusField.values.filter(
|
|
(v) => v !== "Approved" && v !== "Rejected" && v !== "Pending Approval",
|
|
);
|
|
}
|
|
collection.fields = collection.fields.filter((f) => f.name !== "deleted");
|
|
|
|
return app.save(collection);
|
|
});
|