72 lines
2.6 KiB
TypeScript
72 lines
2.6 KiB
TypeScript
import Database from 'better-sqlite3';
|
|
import { Invoice } from '../src/invoicing';
|
|
import { SqliteInvoiceRepo } from '../src/db/invoice-repo';
|
|
|
|
const invoice = (overrides: Partial<Invoice> = {}): Invoice => ({
|
|
id: 'INV-2026-07-0001',
|
|
customerId: 'cust_2',
|
|
period: '2026-07',
|
|
status: 'draft',
|
|
lines: [{ endpointId: 'storage', calls: 2, cents: 1500 }],
|
|
totalCents: 1500,
|
|
creditCents: 1000,
|
|
billableCents: 500,
|
|
billingType: 'stripe',
|
|
...overrides,
|
|
});
|
|
|
|
describe('SqliteInvoiceRepo', () => {
|
|
it('saves and retrieves an invoice with its lines', () => {
|
|
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
|
|
repo.save(invoice());
|
|
const got = repo.get('INV-2026-07-0001');
|
|
expect(got?.billableCents).toBe(500);
|
|
expect(got?.lines).toEqual([{ endpointId: 'storage', calls: 2, cents: 1500 }]);
|
|
});
|
|
|
|
it('re-saving an invoice replaces its lines (draft regeneration)', () => {
|
|
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
|
|
repo.save(invoice());
|
|
repo.save(invoice({ lines: [{ endpointId: 'transform', calls: 5, cents: 20 }], totalCents: 20 }));
|
|
const got = repo.get('INV-2026-07-0001');
|
|
expect(got?.lines).toEqual([{ endpointId: 'transform', calls: 5, cents: 20 }]);
|
|
expect(repo.list({})).toHaveLength(1);
|
|
});
|
|
|
|
it('filters by customer, period, and status', () => {
|
|
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
|
|
repo.save(invoice());
|
|
repo.save(invoice({ id: 'INV-2026-07-0002', customerId: 'cust_3', status: 'issued' }));
|
|
repo.save(invoice({ id: 'INV-2026-08-0001', period: '2026-08' }));
|
|
expect(repo.list({ customerId: 'cust_3' })).toHaveLength(1);
|
|
expect(repo.list({ period: '2026-08' })).toHaveLength(1);
|
|
expect(repo.list({ status: 'issued' })).toHaveLength(1);
|
|
expect(repo.list({ period: '2026-07' })).toHaveLength(2);
|
|
expect(repo.list({})).toHaveLength(3);
|
|
});
|
|
|
|
it('increments sequences per period independently', () => {
|
|
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
|
|
expect(repo.nextSequence('2026-07')).toBe(1);
|
|
repo.save(invoice());
|
|
expect(repo.nextSequence('2026-07')).toBe(2);
|
|
expect(repo.nextSequence('2026-08')).toBe(1);
|
|
});
|
|
|
|
it('persists lifecycle timestamps and PO numbers', () => {
|
|
const repo = new SqliteInvoiceRepo(new Database(':memory:'));
|
|
repo.save(
|
|
invoice({
|
|
billingType: 'purchase_order',
|
|
poNumber: 'PO-1',
|
|
status: 'issued',
|
|
issuedAtMs: 1000,
|
|
dueAtMs: 2000,
|
|
}),
|
|
);
|
|
const got = repo.get('INV-2026-07-0001');
|
|
expect(got?.poNumber).toBe('PO-1');
|
|
expect(got?.issuedAtMs).toBe(1000);
|
|
expect(got?.dueAtMs).toBe(2000);
|
|
});
|
|
});
|