137 lines
4.9 KiB
TypeScript
137 lines
4.9 KiB
TypeScript
import { buildCsv, buildRows } from './cashbox-export.utils';
|
|
|
|
describe('buildRows', () => {
|
|
const team = (overrides: Partial<{ transactions: any[]; players: any[] }> = {}) => ({
|
|
id: 5,
|
|
name: 'Team A',
|
|
alias: 'team-a',
|
|
transactions: [],
|
|
players: [],
|
|
...overrides,
|
|
});
|
|
|
|
it('includes team-wallet credit and expense rows as "Teamkasse"', () => {
|
|
const rows = buildRows(
|
|
team({
|
|
transactions: [
|
|
{ date: '2026-08-05T00:00:00.000Z', amount: 100, note: 'Sponsoring', type: { name: 'credit' } },
|
|
{ date: '2026-08-10T00:00:00.000Z', amount: -20, note: 'Bälle', type: { name: 'expense' } },
|
|
],
|
|
}) as any,
|
|
'2026-08-01',
|
|
'2026-08-31',
|
|
);
|
|
|
|
expect(rows).toEqual([
|
|
{ date: '2026-08-05T00:00:00.000Z', type: 'credit', who: 'Teamkasse', note: 'Sponsoring', amount: 100, runningTotal: 100 },
|
|
{ date: '2026-08-10T00:00:00.000Z', type: 'expense', who: 'Teamkasse', note: 'Bälle', amount: -20, runningTotal: 80 },
|
|
]);
|
|
});
|
|
|
|
it('includes only "payment" player transactions, excluding fee/levy/fine', () => {
|
|
const rows = buildRows(
|
|
team({
|
|
players: [
|
|
{
|
|
firstName: 'Alex',
|
|
lastName: 'Muster',
|
|
transactions: [
|
|
{ date: '2026-08-03T00:00:00.000Z', amount: 10, note: 'Bar bezahlt', type: { name: 'payment' } },
|
|
{ date: '2026-08-04T00:00:00.000Z', amount: 15, note: 'Monatsbeitrag', type: { name: 'fee' } },
|
|
],
|
|
},
|
|
],
|
|
}) as any,
|
|
'2026-08-01',
|
|
'2026-08-31',
|
|
);
|
|
|
|
expect(rows).toEqual([
|
|
{ date: '2026-08-03T00:00:00.000Z', type: 'payment', who: 'Alex Muster', note: 'Bar bezahlt', amount: 10, runningTotal: 10 },
|
|
]);
|
|
});
|
|
|
|
it('excludes rows outside the [from, to] range and sorts the rest chronologically', () => {
|
|
const rows = buildRows(
|
|
team({
|
|
transactions: [
|
|
{ date: '2026-07-31T23:59:00.000Z', amount: 5, note: 'zu früh', type: { name: 'credit' } },
|
|
{ date: '2026-09-01T00:00:01.000Z', amount: 5, note: 'zu spät', type: { name: 'credit' } },
|
|
{ date: '2026-08-20T00:00:00.000Z', amount: 5, note: 'zweitens', type: { name: 'credit' } },
|
|
{ date: '2026-08-01T00:00:00.000Z', amount: 5, note: 'erstens', type: { name: 'credit' } },
|
|
],
|
|
}) as any,
|
|
'2026-08-01',
|
|
'2026-08-31',
|
|
);
|
|
|
|
expect(rows.map((row) => row.note)).toEqual(['erstens', 'zweitens']);
|
|
});
|
|
|
|
it('returns an empty array when nothing falls in range', () => {
|
|
const rows = buildRows(team() as any, '2026-08-01', '2026-08-31');
|
|
expect(rows).toEqual([]);
|
|
});
|
|
|
|
it('skips transactions with null type and includes valid ones', () => {
|
|
const rows = buildRows(
|
|
team({
|
|
transactions: [
|
|
{ date: '2026-08-05T00:00:00.000Z', amount: 100, note: 'Valid credit', type: { name: 'credit' } },
|
|
{ date: '2026-08-06T00:00:00.000Z', amount: 50, note: 'Null type team wallet', type: null },
|
|
],
|
|
players: [
|
|
{
|
|
firstName: 'Bob',
|
|
lastName: 'Smith',
|
|
transactions: [
|
|
{ date: '2026-08-07T00:00:00.000Z', amount: 20, note: 'Valid payment', type: { name: 'payment' } },
|
|
{ date: '2026-08-08T00:00:00.000Z', amount: 30, note: 'Null type player', type: null },
|
|
],
|
|
},
|
|
],
|
|
}) as any,
|
|
'2026-08-01',
|
|
'2026-08-31',
|
|
);
|
|
|
|
expect(rows).toEqual([
|
|
{ date: '2026-08-05T00:00:00.000Z', type: 'credit', who: 'Teamkasse', note: 'Valid credit', amount: 100, runningTotal: 100 },
|
|
{ date: '2026-08-07T00:00:00.000Z', type: 'payment', who: 'Bob Smith', note: 'Valid payment', amount: 20, runningTotal: 120 },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('buildCsv', () => {
|
|
it('renders the header and formatted rows with German decimals', () => {
|
|
const csv = buildCsv([
|
|
{ date: '2026-08-05T00:00:00.000Z', type: 'payment', who: 'Alex Muster', note: 'Bar bezahlt', amount: 10, runningTotal: 10 },
|
|
{ date: '2026-08-10T00:00:00.000Z', type: 'expense', who: 'Teamkasse', note: 'Bälle', amount: -20.5, runningTotal: -10.5 },
|
|
]);
|
|
|
|
expect(csv).toBe(
|
|
'Datum;Typ;Wer;Notiz;Betrag;Periodensaldo\r\n' +
|
|
'2026-08-05;Zahlung;Alex Muster;Bar bezahlt;10,00;10,00\r\n' +
|
|
'2026-08-10;Ausgabe;Teamkasse;Bälle;-20,50;-10,50',
|
|
);
|
|
});
|
|
|
|
it('quotes notes containing a semicolon and escapes embedded quotes', () => {
|
|
const csv = buildCsv([
|
|
{ date: '2026-08-05T00:00:00.000Z', type: 'credit', who: 'Teamkasse', note: 'Spende; "danke"', amount: 5, runningTotal: 5 },
|
|
]);
|
|
|
|
expect(csv).toBe(
|
|
'Datum;Typ;Wer;Notiz;Betrag;Periodensaldo\r\n' +
|
|
'2026-08-05;Guthaben;Teamkasse;"Spende; ""danke""";5,00;5,00',
|
|
);
|
|
});
|
|
|
|
it('shows a placeholder row when there are no bookings', () => {
|
|
const csv = buildCsv([]);
|
|
expect(csv).toBe(
|
|
'Datum;Typ;Wer;Notiz;Betrag;Periodensaldo\r\nKeine Buchungen im gewählten Zeitraum',
|
|
);
|
|
});
|
|
});
|