From 4f0be217323957fea583fcbd3c53c288628cb0b3 Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Wed, 29 Jul 2026 20:03:26 +0200 Subject: [PATCH] feat(buyer-tasks): POST generate-po route Co-authored-by: Orca --- .../v1/buyer-tasks/generate-po/route.test.ts | 13 ++++++ .../api/v1/buyer-tasks/generate-po/route.ts | 41 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/app/api/v1/buyer-tasks/generate-po/route.test.ts create mode 100644 src/app/api/v1/buyer-tasks/generate-po/route.ts diff --git a/src/app/api/v1/buyer-tasks/generate-po/route.test.ts b/src/app/api/v1/buyer-tasks/generate-po/route.test.ts new file mode 100644 index 0000000..781e612 --- /dev/null +++ b/src/app/api/v1/buyer-tasks/generate-po/route.test.ts @@ -0,0 +1,13 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +vi.mock("@/lib/server/buyer-tasks-service", () => ({ generatePoForGroup: vi.fn(), })); +import { POST } from "./route"; +import { generatePoForGroup } from "@/lib/server/buyer-tasks-service"; +function makeRequest(body: unknown) { return new Request("http://test/api/v1/buyer-tasks/generate-po", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); } +describe("POST /api/v1/buyer-tasks/generate-po", () => { + beforeEach(() => { vi.clearAllMocks(); }); + it("creates PO and returns id", async () => { vi.mocked(generatePoForGroup).mockResolvedValue("po-new"); const res = await POST(makeRequest({ orderId: "order-1", supplierId: "sup-1", items: [{ id: "item-1", costPrice: 25, quantity: 2 }] }) as never); expect(res.status).toBe(200); const body = await res.json(); expect(body.purchaseOrderId).toBe("po-new"); }); + it("returns 400 on missing orderId", async () => { const res = await POST(makeRequest({ supplierId: "sup-1", items: [] }) as never); expect(res.status).toBe(400); }); + it("returns 400 on missing supplierId", async () => { const res = await POST(makeRequest({ orderId: "order-1", items: [] }) as never); expect(res.status).toBe(400); }); + it("returns 400 on empty items", async () => { const res = await POST(makeRequest({ orderId: "order-1", supplierId: "sup-1", items: [] }) as never); expect(res.status).toBe(400); }); + it("returns 500 on service error", async () => { vi.mocked(generatePoForGroup).mockRejectedValue(new Error("PB down")); const res = await POST(makeRequest({ orderId: "order-1", supplierId: "sup-1", items: [{ id: "item-1", costPrice: 25, quantity: 1 }] }) as never); expect(res.status).toBe(500); }); +}); diff --git a/src/app/api/v1/buyer-tasks/generate-po/route.ts b/src/app/api/v1/buyer-tasks/generate-po/route.ts new file mode 100644 index 0000000..aec5d01 --- /dev/null +++ b/src/app/api/v1/buyer-tasks/generate-po/route.ts @@ -0,0 +1,41 @@ +import { NextRequest, NextResponse } from "next/server"; +import { generatePoForGroup } from "@/lib/server/buyer-tasks-service"; + +type Body = { + orderId?: string; + supplierId?: string; + items?: Array<{ id: string; costPrice: number; quantity: number }>; +}; + +export async function POST(req: NextRequest) { + let body: Body; + try { + body = (await req.json()) as Body; + } catch { + return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); + } + + if (!body.orderId) { + return NextResponse.json({ error: "orderId is required" }, { status: 400 }); + } + if (!body.supplierId) { + return NextResponse.json({ error: "supplierId is required" }, { status: 400 }); + } + if (!body.items || body.items.length === 0) { + return NextResponse.json({ error: "items must be non-empty" }, { status: 400 }); + } + + try { + const purchaseOrderId = await generatePoForGroup( + body.orderId, + body.supplierId, + body.items + ); + return NextResponse.json({ purchaseOrderId }); + } catch (err) { + return NextResponse.json( + { error: err instanceof Error ? err.message : "Failed to generate PO" }, + { status: 500 } + ); + } +}