From 4f0f70428fe5b622fcce6f18e1f5b05cfe41eec9 Mon Sep 17 00:00:00 2001 From: Arthur Bassi Date: Tue, 11 Aug 2026 14:51:29 -0300 Subject: [PATCH] fix(work-orders): search one-character WO numbers for duplicates --- src/domain/work-orders/api/work-orders-api.ts | 10 +++---- .../work-orders/api/work-orders-api.test.ts | 30 +++++++++++++++++-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/domain/work-orders/api/work-orders-api.ts b/src/domain/work-orders/api/work-orders-api.ts index c4c754e6..ad9a93d1 100644 --- a/src/domain/work-orders/api/work-orders-api.ts +++ b/src/domain/work-orders/api/work-orders-api.ts @@ -193,13 +193,13 @@ export const workOrdersApi = { findByWoNumber: async (woNumber: string): Promise => { const normalized = woNumber.trim(); - // board/search contract: only send search when length >= 2. - const minLength = 2; - // Safety cap — if hit before exhausting results, fail closed (do not treat as unique). - const safetyMaxPages = 50; - if (normalized.length < minLength) { + // Search every non-empty WO# — backend advanced search accepts 1-character queries, and + // wizard/create allow single-digit numbers (SH-189 must not bypass remote uniqueness). + if (!normalized) { return undefined; } + // Safety cap — if hit before exhausting results, fail closed (do not treat as unique). + const safetyMaxPages = 50; // `board/search` is fuzzy and paginated (0-based); exhaust pages for an exact WO#. const pageSize = 100; diff --git a/src/test/domain/work-orders/api/work-orders-api.test.ts b/src/test/domain/work-orders/api/work-orders-api.test.ts index cc84a5ed..6731259d 100644 --- a/src/test/domain/work-orders/api/work-orders-api.test.ts +++ b/src/test/domain/work-orders/api/work-orders-api.test.ts @@ -364,12 +364,38 @@ describe("workOrdersApi.findByWoNumber", () => { expect(result).toMatchObject({ id: 999, woNumber: "10000009999" }); }); - it("skips remote search for single-character WO numbers", async () => { - const result = await workOrdersApi.findByWoNumber("1"); + it("skips remote search for empty WO numbers", async () => { + const result = await workOrdersApi.findByWoNumber(" "); expect(apiGet).not.toHaveBeenCalled(); expect(result).toBeUndefined(); }); + it("finds a one-character WO number duplicate outside loaded board rows", async () => { + apiGet.mockResolvedValue({ + items: [ + { + id: 7, + woNumber: "9", + lifecycleStatusLabel: "Incomplete", + }, + ], + totalCount: 1, + page: 0, + pageSize: 100, + }); + + const result = await workOrdersApi.findByWoNumber("9"); + + expect(apiGet).toHaveBeenCalledTimes(1); + const searchParams = apiGet.mock.calls[0]?.[1]?.searchParams as URLSearchParams; + expect(Object.fromEntries(searchParams)).toEqual({ + search: "9", + page: "0", + pageSize: "100", + }); + expect(result).toMatchObject({ id: 7, woNumber: "9" }); + }); + it("throws when the safety page limit is hit before exhausting results", async () => { apiGet.mockResolvedValue({ items: Array.from({ length: 100 }, (_, index) => ({