feat(buyer-tasks): generatePoForGroup creates PO and updates items

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
SJCoetz 2026-07-29 19:58:07 +02:00
parent b4c9f2d2ce
commit 98a0bc4143
2 changed files with 85 additions and 0 deletions

View file

@ -251,4 +251,61 @@ describe("listBuyerTasks", () => {
expect(groups).toHaveLength(1);
expect(groups[0].step).toBe("po_sent");
});
});
describe("generatePoForGroup", () => {
it("creates a PO and updates items", async () => {
const createPo = vi.fn().mockResolvedValue({ id: "po-new" });
const updateItem = vi.fn().mockResolvedValue({});
const admin = {
collection: vi.fn().mockImplementation((name: string) => {
if (name === "purchase_orders") return { create: createPo };
if (name === "order_items") return { update: updateItem };
return { getList: vi.fn(), getFullList: vi.fn() };
}),
};
vi.mocked(getAdminPocketBase).mockResolvedValue(admin as never);
const { generatePoForGroup } = await import("./buyer-tasks-service");
const poId = await generatePoForGroup("order-1", "sup-1", [
{ id: "item-1", costPrice: 25, quantity: 2 },
]);
expect(poId).toBe("po-new");
expect(createPo).toHaveBeenCalledWith({
order: "order-1",
supplier: "sup-1",
status: "draft",
});
expect(updateItem).toHaveBeenCalledWith("item-1", {
purchase_order: "po-new",
item_status: "po_generated",
});
});
it("returns null when no items provided", async () => {
const admin = { collection: vi.fn() };
vi.mocked(getAdminPocketBase).mockResolvedValue(admin as never);
const { generatePoForGroup } = await import("./buyer-tasks-service");
const poId = await generatePoForGroup("order-1", "sup-1", []);
expect(poId).toBeNull();
});
it("throws when orderId is missing", async () => {
const admin = { collection: vi.fn() };
vi.mocked(getAdminPocketBase).mockResolvedValue(admin as never);
const { generatePoForGroup } = await import("./buyer-tasks-service");
await expect(generatePoForGroup("", "sup-1", [{ id: "item-1", costPrice: 25, quantity: 1 }])).rejects.toThrow(/orderId/i);
});
it("throws when supplierId is missing", async () => {
const admin = { collection: vi.fn() };
vi.mocked(getAdminPocketBase).mockResolvedValue(admin as never);
const { generatePoForGroup } = await import("./buyer-tasks-service");
await expect(generatePoForGroup("order-1", "", [{ id: "item-1", costPrice: 25, quantity: 1 }])).rejects.toThrow(/supplierId/i);
});
});

View file

@ -118,3 +118,31 @@ export async function listBuyerTasks(tab: BuyerTaskTab): Promise<BuyerTaskGroup[
return a.supplierName.localeCompare(b.supplierName);
});
}
export async function generatePoForGroup(
orderId: string,
supplierId: string,
items: Array<{ id: string; costPrice: number; quantity: number }>
): Promise<string | null> {
if (!orderId) throw new Error("orderId is required");
if (!supplierId) throw new Error("supplierId is required");
if (items.length === 0) return null;
const admin = await getAdminPocketBase();
const po = await admin.collection("purchase_orders").create({
order: orderId,
supplier: supplierId,
status: "draft",
});
await Promise.all(
items.map((item) =>
admin.collection("order_items").update(item.id, {
purchase_order: po.id,
item_status: "po_generated",
})
)
);
return po.id;
}