From 1a824d347739ff88f8ad8aa4c959ecd92f4b01bc Mon Sep 17 00:00:00 2001 From: Dave Date: Sun, 30 Aug 2026 22:08:24 +0200 Subject: [PATCH] fix(orders): document PB schema prerequisite + strengthen duplicate-matcher tests --- ...rder-number-duplicate-link-service.test.ts | 46 ++++++++++++++++--- .../order-number-duplicate-link-service.ts | 10 ++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/lib/server/order-number-duplicate-link-service.test.ts b/src/lib/server/order-number-duplicate-link-service.test.ts index 08db1d4..855dd33 100644 --- a/src/lib/server/order-number-duplicate-link-service.test.ts +++ b/src/lib/server/order-number-duplicate-link-service.test.ts @@ -41,8 +41,23 @@ const { store, pbMock, clearStore } = vi.hoisted(() => { const items = where(name, opts.filter || ""); return { items, totalItems: items.length, page: 1, perPage: _perPage }; }, - async getFullList(opts: { filter?: string } = {}) { - return where(name, opts.filter || ""); + async getFullList(opts: { filter?: string; sort?: string } = {}) { + const items = where(name, opts.filter || ""); + // Honor `sort` so tests can pin behaviour that depends on row + // order. The real PB SDK sorts by the requested field + // ascending; descending requires a `-` prefix which we do not + // currently model. Sufficient for the duplicate-matcher tests + // that request `sort: "created"`. + if (opts.sort) { + const dir = opts.sort.startsWith("-") ? -1 : 1; + const field = opts.sort.replace(/^-/, ""); + items.sort((a, b) => { + const av = String(a[field] ?? ""); + const bv = String(b[field] ?? ""); + return dir * (av < bv ? -1 : av > bv ? 1 : 0); + }); + } + return items; }, async getOne( id: string, @@ -159,16 +174,20 @@ describe("suggestOrderNumberDuplicate", () => { }); it("prefers the oldest other order when more than two orders share a number", async () => { - await pbMock.collection("orders").create({ - id: "order-oldest", - order_number: "SHARED", - created: "2026-01-01 00:00:00.000Z", - }); + // Insertion order is deliberately NOT chronological: `order-middle` + // goes in FIRST so the mock's Map-iteration-order (which the real PB + // SDK would replace via `sort: "created"`) returns the wrong row at + // index 0 if the service forgets to pass the sort option. await pbMock.collection("orders").create({ id: "order-middle", order_number: "SHARED", created: "2026-02-01 00:00:00.000Z", }); + await pbMock.collection("orders").create({ + id: "order-oldest", + order_number: "SHARED", + created: "2026-01-01 00:00:00.000Z", + }); const orderNewest = await pbMock.collection("orders").create({ id: "order-newest", order_number: "SHARED", @@ -179,4 +198,17 @@ describe("suggestOrderNumberDuplicate", () => { expect(result.duplicateOrderId).toBe("order-oldest"); }); + + it("does not stamp _checked when order has no order_number (early return)", async () => { + const order = await pbMock.collection("orders").create({ + id: "order-no-num", + // order_number deliberately omitted + }); + + const result = await suggestOrderNumberDuplicate(order.id); + + expect(result.duplicateOrderId).toBeNull(); + const updated = await pbMock.collection("orders").getOne("order-no-num"); + expect(updated.order_number_duplicate_checked).toBeFalsy(); + }); }); \ No newline at end of file diff --git a/src/lib/server/order-number-duplicate-link-service.ts b/src/lib/server/order-number-duplicate-link-service.ts index eb28b10..af17c89 100644 --- a/src/lib/server/order-number-duplicate-link-service.ts +++ b/src/lib/server/order-number-duplicate-link-service.ts @@ -1,3 +1,13 @@ +/** + * 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