From 05242f09f4dedbee000cfa9a440d558d8f8492cd Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Wed, 29 Jul 2026 20:00:45 +0200 Subject: [PATCH] feat(buyer-tasks): markGroupReady with cross-order check Co-Authored-By: Claude Opus 4.8 Co-authored-by: Orca --- src/lib/server/buyer-tasks-service.test.ts | 54 ++++++++++++++++++++++ src/lib/server/buyer-tasks-service.ts | 41 ++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/src/lib/server/buyer-tasks-service.test.ts b/src/lib/server/buyer-tasks-service.test.ts index 024c0f4..c021352 100644 --- a/src/lib/server/buyer-tasks-service.test.ts +++ b/src/lib/server/buyer-tasks-service.test.ts @@ -308,4 +308,58 @@ describe("generatePoForGroup", () => { const { generatePoForGroup } = await import("./buyer-tasks-service"); await expect(generatePoForGroup("order-1", "", [{ id: "item-1", costPrice: 25, quantity: 1 }])).rejects.toThrow(/supplierId/i); }); +}); + +describe("markGroupReady", () => { + it("updates all items to ready_delivery", async () => { + const getOne = vi.fn().mockResolvedValue({ order: "order-1" }); + const updateItem = vi.fn().mockResolvedValue({}); + const admin = { + collection: vi.fn().mockReturnValue({ getOne, update: updateItem }), + }; + vi.mocked(getAdminPocketBase).mockResolvedValue(admin as never); + + const { markGroupReady } = await import("./buyer-tasks-service"); + const result = await markGroupReady("order-1", "sup-1", [ + { id: "item-1" }, + { id: "item-2" }, + ]); + + expect(result.updated).toBe(2); + expect(result.failed).toEqual([]); + expect(updateItem).toHaveBeenCalledWith("item-1", { item_status: "ready_delivery" }); + expect(updateItem).toHaveBeenCalledWith("item-2", { item_status: "ready_delivery" }); + }); + + it("returns 0 when no items provided", async () => { + const admin = { collection: vi.fn() }; + vi.mocked(getAdminPocketBase).mockResolvedValue(admin as never); + + const { markGroupReady } = await import("./buyer-tasks-service"); + const result = await markGroupReady("order-1", "sup-1", []); + + expect(result.updated).toBe(0); + expect(result.failed).toEqual([]); + }); + + it("collects per-item failures", async () => { + const getOne = vi.fn().mockResolvedValue({ order: "order-1" }); + const updateItem = vi.fn().mockImplementation((id: string) => { + if (id === "item-2") throw new Error("PB error"); + return Promise.resolve({}); + }); + const admin = { + collection: vi.fn().mockReturnValue({ getOne, update: updateItem }), + }; + vi.mocked(getAdminPocketBase).mockResolvedValue(admin as never); + + const { markGroupReady } = await import("./buyer-tasks-service"); + const result = await markGroupReady("order-1", "sup-1", [ + { id: "item-1" }, + { id: "item-2" }, + ]); + + expect(result.updated).toBe(1); + expect(result.failed).toEqual([{ id: "item-2", error: "PB error" }]); + }); }); \ No newline at end of file diff --git a/src/lib/server/buyer-tasks-service.ts b/src/lib/server/buyer-tasks-service.ts index fc3091d..0b8ee62 100644 --- a/src/lib/server/buyer-tasks-service.ts +++ b/src/lib/server/buyer-tasks-service.ts @@ -146,3 +146,44 @@ export async function generatePoForGroup( return po.id; } + +export async function markGroupReady( + orderId: string, + supplierId: string, + items: Array<{ id: string }> +): Promise<{ updated: number; failed: Array<{ id: string; error: string }> }> { + if (!orderId || !supplierId) { + throw new Error("orderId and supplierId are required"); + } + if (items.length === 0) return { updated: 0, failed: [] }; + + const admin = await getAdminPocketBase(); + const failed: Array<{ id: string; error: string }> = []; + let updated = 0; + + for (const item of items) { + // Cross-order check: verify the item belongs to this order before + // mutating it. Same pattern as updateOrderItemsPrices. + try { + const current = await admin.collection("order_items").getOne(item.id); + if (current.order !== orderId) { + failed.push({ + id: item.id, + error: "Item does not belong to this order", + }); + continue; + } + await admin.collection("order_items").update(item.id, { + item_status: "ready_delivery", + }); + updated += 1; + } catch (err) { + failed.push({ + id: item.id, + error: err instanceof Error ? err.message : "Unknown error", + }); + } + } + + return { updated, failed }; +}