78 lines
No EOL
2.8 KiB
TypeScript
78 lines
No EOL
2.8 KiB
TypeScript
/**
|
|
* PREREQUISITE: Before this code can run against a real PocketBase
|
|
* instance, the operator must add two fields to the `orders`
|
|
* collection in the PB admin UI (per the AGENTS.md convention — PB
|
|
* schema is not self-service from code):
|
|
* - `order_number_duplicate` (Relation → orders, single)
|
|
* - `order_number_duplicate_checked` (Bool)
|
|
* Without these fields, the `.update()` below will 500 in production.
|
|
*/
|
|
|
|
/**
|
|
* order-number-duplicate-link-service — detects when an order's
|
|
* `order_number` is reused by ANOTHER order (any status, no time
|
|
* limit). Catches two cases that have bitten the operator:
|
|
* - internal ORD-number collisions from a partial intake race
|
|
* - operator not realising a PO was already entered
|
|
*
|
|
* Visibility only. Nothing downstream reads this to drive pricing,
|
|
* supplier assignment, or processing — a wrong flag costs nothing, the
|
|
* operator just ignores the pill. That's what justifies writing the
|
|
* match automatically with no human confirmation step, unlike the
|
|
* order↔quote matcher's deliberately conservative suggest-only policy
|
|
* (see order-quote-link-service.ts) — that policy exists because a
|
|
* wrong quote link puts wrong prices in front of a buyer.
|
|
*
|
|
* NOTE: customer PO refs (e.g. "PO-12345") and placeholders like
|
|
* "NO-REF" are ALLOWED to repeat by design — one blanket customer PO
|
|
* is often split into several partial-delivery orders. This matcher
|
|
* flags any exact-string duplicate regardless of shape; the order
|
|
* review collision check (orders-service.ts) is the only place that
|
|
* gates on ORD-YYYY-MM-NNN shape.
|
|
*/
|
|
|
|
import { getAdminPocketBase } from "@/lib/server/pocketbase-admin";
|
|
|
|
const ORDER_COLLECTION = "orders";
|
|
|
|
export interface OrderNumberDuplicateResult {
|
|
orderId: string;
|
|
duplicateOrderId: string | null;
|
|
}
|
|
|
|
function esc(s: string): string {
|
|
return (s || "").replace(/"/g, '\\"');
|
|
}
|
|
|
|
export async function suggestOrderNumberDuplicate(
|
|
orderId: string,
|
|
): Promise<OrderNumberDuplicateResult> {
|
|
const pb = await getAdminPocketBase();
|
|
pb.autoCancellation(false);
|
|
|
|
const order = await pb
|
|
.collection(ORDER_COLLECTION)
|
|
.getOne<{ id: string; order_number?: string }>(orderId);
|
|
|
|
if (!order.order_number) {
|
|
return { orderId, duplicateOrderId: null };
|
|
}
|
|
|
|
const others = await pb
|
|
.collection(ORDER_COLLECTION)
|
|
.getFullList<{ id: string; created: string }>({
|
|
filter: `order_number = "${esc(order.order_number)}" && id != "${esc(orderId)}"`,
|
|
fields: "id,created",
|
|
sort: "created",
|
|
batch: 500,
|
|
});
|
|
|
|
const duplicateOrderId = others.length > 0 ? others[0].id : null;
|
|
|
|
await pb.collection(ORDER_COLLECTION).update(orderId, {
|
|
order_number_duplicate: duplicateOrderId,
|
|
order_number_duplicate_checked: true,
|
|
});
|
|
|
|
return { orderId, duplicateOrderId };
|
|
} |