diff --git a/src/lib/server/pos-service.test.ts b/src/lib/server/pos-service.test.ts index dc259c8..4a0136f 100644 --- a/src/lib/server/pos-service.test.ts +++ b/src/lib/server/pos-service.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from "vitest"; -import { checkoutSale, listActiveStockCatalog } from "./pos-service"; +import { checkoutSale, listActiveStockCatalog, listSalesInRange } from "./pos-service"; function stubPb(items: unknown[]) { return { @@ -279,3 +279,89 @@ describe("checkoutSale", () => { expect(updates).toEqual([]); }); }); + +describe("listSalesInRange", () => { + it("fetches sales in the range and their line items in one follow-up call", async () => { + const salesGetList = vi.fn().mockResolvedValue({ + items: [ + { id: "sale-1", total: 100 }, + { id: "sale-2", total: 200 }, + ], + }); + const itemsGetList = vi.fn().mockResolvedValue({ + items: [ + { id: "si-1", sale: "sale-1", qty: 2 }, + { id: "si-2", sale: "sale-2", qty: 1 }, + ], + }); + const pb = { + collection: (name: string) => { + if (name === "pos_sales") { + return { + getFullList: vi.fn(), + getOne: vi.fn(), + create: vi.fn(), + update: vi.fn(), + getList: salesGetList, + }; + } + if (name === "pos_sale_items") { + return { + getFullList: vi.fn(), + getOne: vi.fn(), + create: vi.fn(), + update: vi.fn(), + getList: itemsGetList, + }; + } + throw new Error(`unexpected collection ${name}`); + }, + }; + + const result = await listSalesInRange(pb, { + from: "2026-08-30 00:00:00", + to: "2026-08-30 23:59:59", + }); + + expect(result.sales).toHaveLength(2); + expect(result.items).toHaveLength(2); + expect(salesGetList).toHaveBeenCalledWith(1, 500, { + filter: "created >= '2026-08-30 00:00:00' && created <= '2026-08-30 23:59:59'", + sort: "-created", + }); + expect(itemsGetList).toHaveBeenCalledWith(1, 2000, { + filter: "sale = 'sale-1' || sale = 'sale-2'", + }); + }); + + it("skips the items query when there are no sales in range", async () => { + const salesGetList = vi.fn().mockResolvedValue({ items: [] }); + const itemsGetList = vi.fn(); + const pb = { + collection: (name: string) => + name === "pos_sales" + ? { + getFullList: vi.fn(), + getOne: vi.fn(), + create: vi.fn(), + update: vi.fn(), + getList: salesGetList, + } + : { + getFullList: vi.fn(), + getOne: vi.fn(), + create: vi.fn(), + update: vi.fn(), + getList: itemsGetList, + }, + }; + + const result = await listSalesInRange(pb, { + from: "2026-08-30 00:00:00", + to: "2026-08-30 23:59:59", + }); + + expect(result).toEqual({ sales: [], items: [] }); + expect(itemsGetList).not.toHaveBeenCalled(); + }); +}); diff --git a/src/lib/server/pos-service.ts b/src/lib/server/pos-service.ts index f701033..ef1ab90 100644 --- a/src/lib/server/pos-service.ts +++ b/src/lib/server/pos-service.ts @@ -264,3 +264,30 @@ export async function checkoutSale( return { ok: true, sale, items, stockErrors, duplicate: false }; } + +// ─── listSalesInRange ─────────────────────────────────────────────── + +export type PosSalesRangeResult = { + sales: PosSale[]; + items: PosSaleItem[]; +}; + +/** + * Sales + their line items for history.html / cashup.html. `from`/`to` + * are PB datetime filter strings (e.g. "2026-08-30 00:00:00"). + */ +export async function listSalesInRange( + pb: PosPb, + range: { from: string; to: string }, +): Promise { + const salesPage = await pb.collection("pos_sales").getList(1, 500, { + filter: `created >= '${escapePbValue(range.from)}' && created <= '${escapePbValue(range.to)}'`, + sort: "-created", + }); + const sales = salesPage.items; + if (sales.length === 0) return { sales: [], items: [] }; + + const filter = sales.map((s) => `sale = '${escapePbValue(s.id)}'`).join(" || "); + const itemsPage = await pb.collection("pos_sale_items").getList(1, 2000, { filter }); + return { sales, items: itemsPage.items }; +}