feat: add buildRows for cashbox export row filtering
This commit is contained in:
@@ -0,0 +1,75 @@
|
|||||||
|
import { 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([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { Team } from 'src/teams/entities/team.entity';
|
||||||
|
|
||||||
|
export interface CashboxExportRow {
|
||||||
|
date: string;
|
||||||
|
type: string;
|
||||||
|
who: string;
|
||||||
|
note: string;
|
||||||
|
amount: number;
|
||||||
|
runningTotal: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RawRow {
|
||||||
|
date: string;
|
||||||
|
type: string;
|
||||||
|
who: string;
|
||||||
|
note: string;
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildRows(team: Team, from: string, to: string): CashboxExportRow[] {
|
||||||
|
const fromTime = new Date(`${from}T00:00:00.000Z`).getTime();
|
||||||
|
const toTime = new Date(`${to}T23:59:59.999Z`).getTime();
|
||||||
|
|
||||||
|
const raw: RawRow[] = [];
|
||||||
|
|
||||||
|
for (const transaction of team.transactions ?? []) {
|
||||||
|
raw.push({
|
||||||
|
date: transaction.date,
|
||||||
|
type: transaction.type.name,
|
||||||
|
who: 'Teamkasse',
|
||||||
|
note: transaction.note,
|
||||||
|
amount: Number(transaction.amount),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const player of team.players ?? []) {
|
||||||
|
for (const transaction of player.transactions ?? []) {
|
||||||
|
if (transaction.type.name !== 'payment') continue;
|
||||||
|
raw.push({
|
||||||
|
date: transaction.date,
|
||||||
|
type: transaction.type.name,
|
||||||
|
who: `${player.firstName} ${player.lastName}`,
|
||||||
|
note: transaction.note,
|
||||||
|
amount: Number(transaction.amount),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filtered = raw
|
||||||
|
.filter((row) => {
|
||||||
|
const time = new Date(row.date).getTime();
|
||||||
|
return time >= fromTime && time <= toTime;
|
||||||
|
})
|
||||||
|
.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0));
|
||||||
|
|
||||||
|
let runningTotal = 0;
|
||||||
|
return filtered.map((row) => {
|
||||||
|
runningTotal += row.amount;
|
||||||
|
return { ...row, runningTotal };
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user