import { describe, expect, it } from "vitest"; import { Product } from "@/lib/server/models/Product"; import { Purchase } from "@/lib/server/models/Purchase"; import { Transaction } from "@/lib/server/models/Transaction"; import { backfillPurchaseMovements, stockOf } from "@/lib/server/stock"; import * as cash from "@/app/api/transactions/cash/route"; import * as transfers from "@/app/api/stock/transfers/route"; import * as profit from "@/app/api/reports/profit/route"; import * as summary from "@/app/api/dashboard/summary/route"; import { json, makeTenant, req, uuid, type Actor } from "./helpers"; /** * The "Completion and retest plan" from the 11 October 2026 test report * (AquaFlow_Initial_Test_Report, pages 9-10), with its expected numbers. */ describe("initial test report: completion and retest plan", () => { it("matches every planned balance and total", async () => { const t = await makeTenant("ZERRO"); const [refill3] = await Product.insertMany([ { businessId: t.businessId, label: "QA 20261011 3L Refill", sizeLiters: 3, unitPrice: 40 }, ]); const l3 = refill3._id.toString(); const l5 = t.water5; // KES 60 // Fixtures recorded before the ledger existed (QA-20261011-STOCK-A-3L / -5L), // brought into stock by the one-off backfill. await Purchase.insertMany([ { businessId: t.businessId, branchId: t.branchA, supplierName: "QA", productId: l3, productLabel: "3L", quantity: 20, unitCost: 10, totalCost: 200, recordedBy: t.owner.id }, { businessId: t.businessId, branchId: t.branchA, supplierName: "QA", productId: l5, productLabel: "5L", quantity: 10, unitCost: 20, totalCost: 200, recordedBy: t.owner.id }, ]); await backfillPurchaseMovements(t.businessId); const stock = async (branch: string, product: string) => stockOf(t.businessId, branch, product); expect([await stock(t.branchA, l3), await stock(t.branchA, l5)]).toEqual([20, 10]); const sell = async (as: Actor, lines: { productId: string; quantity: number }[], offline = false) => json( await cash.POST( req("POST", "/x", { as, body: { sales: [ { clientUuid: uuid(), staffId: as.id, branchId: as.branchId, lines, ...(offline ? { clientSubmittedAt: new Date().toISOString() } : {}), }, ], }, }) ) ); // Online sale: 2 x 3L + 1 x 5L = KES 140; A balances 18 and 9. const online = await sell(t.cashierA, [{ productId: l3, quantity: 2 }, { productId: l5, quantity: 1 }]); expect(online.body.results[0]).toMatchObject({ status: "saved", amountTotal: 140 }); expect([await stock(t.branchA, l3), await stock(t.branchA, l5)]).toEqual([18, 9]); // Transfer 5 x 3L from A to B: A 13, B 5, combined 18. const transfer = await transfers.POST( req("POST", "/x", { as: t.owner, body: { clientUuid: uuid(), fromBranchId: t.branchA, toBranchId: t.branchB, productId: l3, quantity: 5 }, }) ); expect(transfer.status).toBe(201); expect([await stock(t.branchA, l3), await stock(t.branchB, l3)]).toEqual([13, 5]); // Two offline sales replayed on reconnect (sale 2: 1 x 3L; sale 3: 2 x 3L + 1 x 5L), // then replayed again: exactly two new sales (three lines), A 10 x 3L and 8 x 5L. const before = await Transaction.countDocuments({ businessId: t.businessId }); const queued = [ { clientUuid: uuid(), staffId: t.cashierA.id, branchId: t.branchA, clientSubmittedAt: new Date().toISOString(), lines: [{ productId: l3, quantity: 1 }] }, { clientUuid: uuid(), staffId: t.cashierA.id, branchId: t.branchA, clientSubmittedAt: new Date().toISOString(), lines: [{ productId: l3, quantity: 2 }, { productId: l5, quantity: 1 }] }, ]; for (let pass = 0; pass < 2; pass++) { const r = await json(await cash.POST(req("POST", "/x", { as: t.cashierA, body: { sales: queued } }))); expect(r.body.results.map((x: any) => x.status)).toEqual(pass === 0 ? ["saved", "saved"] : ["duplicate", "duplicate"]); } expect((await Transaction.countDocuments({ businessId: t.businessId })) - before).toBe(3); expect([await stock(t.branchA, l3), await stock(t.branchA, l5)]).toEqual([10, 8]); // Branch B sells 1 x 3L for KES 40. await sell(t.cashierB, [{ productId: l3, quantity: 1 }]); // Final: A 3L 10, B 3L 4, A 5L 8; combined 3L 14 = 20 - 6 sold, 5L 8 = 10 - 2 sold. expect([await stock(t.branchA, l3), await stock(t.branchB, l3), await stock(t.branchA, l5)]).toEqual([10, 4, 8]); // Revenue: A KES 320, B KES 40, total KES 360 — on the report and the owner dashboard. const report = async (q: string) => (await json(await profit.GET(req("GET", "/api/reports/profit" + q, { as: t.owner })))).body; expect((await report("")).revenue).toBe(360); expect((await report("?branchId=" + t.branchA)).revenue).toBe(320); expect((await report("?branchId=" + t.branchB)).revenue).toBe(40); const dash = (await json(await summary.GET(req("GET", "/api/dashboard/summary", { as: t.owner })))).body; expect(dash.totals.cashTotal).toBe(360); const ranking = Object.fromEntries(dash.branchRanking.map((b: any) => [b.branchName, b.revenue])); expect(ranking).toEqual({ "ZERRO A": 320, "ZERRO B": 40 }); }); });