/** * 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 { 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 }; }