101 lines
2.5 KiB
TypeScript
101 lines
2.5 KiB
TypeScript
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 ?? []) {
|
|
if (!transaction.type) continue;
|
|
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 || 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 };
|
|
});
|
|
}
|
|
|
|
const TYPE_LABELS: Record<string, string> = {
|
|
payment: 'Zahlung',
|
|
credit: 'Guthaben',
|
|
expense: 'Ausgabe',
|
|
};
|
|
|
|
function formatGermanAmount(value: number): string {
|
|
return value.toFixed(2).replace('.', ',');
|
|
}
|
|
|
|
function escapeCsvField(value: string): string {
|
|
if (/[;"\n\r]/.test(value)) {
|
|
return `"${value.replace(/"/g, '""')}"`;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
export function buildCsv(rows: CashboxExportRow[]): string {
|
|
const lines = ['Datum;Typ;Wer;Notiz;Betrag;Periodensaldo'];
|
|
if (rows.length === 0) {
|
|
lines.push('Keine Buchungen im gewählten Zeitraum');
|
|
} else {
|
|
for (const row of rows) {
|
|
lines.push(
|
|
[
|
|
row.date.slice(0, 10),
|
|
TYPE_LABELS[row.type] ?? row.type,
|
|
escapeCsvField(row.who),
|
|
escapeCsvField(row.note),
|
|
formatGermanAmount(row.amount),
|
|
formatGermanAmount(row.runningTotal),
|
|
].join(';'),
|
|
);
|
|
}
|
|
}
|
|
return lines.join('\r\n');
|
|
}
|