import { ConflictException } from "@nestjs/common"; import { CashReceiptsService, cashCheckNumber } from "./cash-receipts.service"; /** * A cash receipt is two writes that must agree: the receipt and its C * credit. Checked here: the credit legacy would have posted, and that a folio * the legacy book already used is refused. Folio numbering itself runs against * real data (it reads the imported EFECTIVO journal). */ function build(opts: { legacyTaken?: boolean } = {}) { const posted: Record[] = []; const tx = { customer: { findUnique: jest.fn().mockResolvedValue({ id: "c1" }) }, cashReceipt: { aggregate: jest.fn().mockResolvedValue({ _max: { folio: 13934 } }), count: jest.fn().mockResolvedValue(0), create: jest.fn(({ data }) => Promise.resolve(data)), }, transaction: { count: jest.fn().mockResolvedValue(opts.legacyTaken ? 1 : 0), create: jest.fn(({ data }) => { posted.push(data); return Promise.resolve({ id: "tx1", ...data }); }), }, typeTransaction: { findFirst: jest.fn().mockResolvedValue({ id: "cash-deposit" }) }, $queryRaw: jest.fn().mockResolvedValue([{ mx: 13900 }]), }; const prisma = { $transaction: jest.fn((fn) => fn(tx)) }; return { service: new CashReceiptsService(prisma as never), posted }; } const base = { customerId: "c1", receivedAt: "2026-07-14", concept: "RECEIVED $1500 DLLS FOR ACCOUNT.", }; describe("cash receipts", () => { it("takes the next folio and posts a C credit", async () => { const { service, posted } = build(); const r = await service.create({ ...base, amount: 9000, currency: "MXN" }, "u1"); expect(r.folio).toBe(13935); expect(posted[0]).toMatchObject({ reference: "C13935", checkNumber: "CASH0714P", currency: "MXN", typeId: "cash-deposit", }); expect(Number(posted[0].amount)).toBe(9000); }); it("credits dollars to a pesos ledger at the given rate, as legacy did", async () => { // Legacy C13830: 1,500 USD at 16.80 posted as 25,200 MXN. const { service, posted } = build(); await service.create( { ...base, amount: 1500, currency: "USD", exchangeRate: 16.8 }, "u1", ); expect(posted[0]).toMatchObject({ currency: "MXN", checkNumber: "CASH0714D" }); expect(Number(posted[0].amount)).toBe(25200); }); it("refuses a typed folio the legacy book already used", async () => { const { service, posted } = build({ legacyTaken: true }); await expect( service.create({ ...base, folio: 13800, amount: 1, currency: "MXN" }, "u1"), ).rejects.toBeInstanceOf(ConflictException); expect(posted).toHaveLength(0); }); it("names the pseudo check by day and currency", () => { expect(cashCheckNumber(new Date("2026-01-05T00:00:00Z"), "USD")).toBe("CASH0105D"); }); });