Feat: Quartalsbericht — Stornozeilen mit Minusbeträgen, nach Datum sortiert, Summen netten sich heraus (SW by-v976)

This commit is contained in:
rene 2026-05-15 13:27:05 +02:00
parent b10b3140eb
commit 6104132714
5 changed files with 81 additions and 32 deletions

View file

@ -4355,21 +4355,19 @@ window.Page_admin = (() => {
const fmtDate = iso => iso ? new Date(iso).toLocaleDateString('de-DE') : '';
const escape = v => `"${String(v || '').replace(/"/g, '""')}"`;
const header = 'Nummer;Stornonummer;Empfaenger;E-Mail;Rechnungsdatum;Leistungszeitraum;Nettobetrag;Bruttobetrag;Eingegangener Betrag;Status;Versendet am;Zahlungseingang\n';
const csvRows = data.invoices.map(inv => {
const cancelled = inv.status === 'cancelled';
return [
const statusLabel = { paid: 'Bezahlt', sent: 'Versendet', cancelled: 'Storniert (Original)', storno: 'Stornorechnung' };
const header = 'Nummer;Empfaenger;E-Mail;Datum;Leistungszeitraum;Nettobetrag;Bruttobetrag;Eingegangener Betrag;Status;Versendet am;Zahlungseingang\n';
const csvRows = data.invoices.map(inv => [
inv.invoice_number,
inv.cancellation_number || '',
inv.recipient_name, inv.recipient_email || '',
fmtDate(inv.created_at), inv.service_period || '',
cancelled ? '0.00' : fmtEur(inv.amount_net),
cancelled ? '0.00' : fmtEur(inv.amount_gross),
cancelled ? '0.00' : (inv.paid_amount != null ? fmtEur(inv.paid_amount) : ''),
cancelled ? 'Storniert' : inv.status,
fmtEur(inv.amount_net),
fmtEur(inv.amount_gross),
inv.paid_amount != null ? fmtEur(inv.paid_amount) : '',
statusLabel[inv.status] || inv.status,
fmtDate(inv.sent_at), fmtDate(inv.paid_at)
].map(escape).join(';');
}).join('\n');
].map(escape).join(';')
).join('\n');
const blob = new Blob(['' + header + csvRows], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
@ -4398,15 +4396,19 @@ window.Page_admin = (() => {
}
const _fmtE = v => v != null ? Number(v).toLocaleString('de-DE',{minimumFractionDigits:2}) + ' €' : '—';
const _fmtD = iso => iso ? new Date(iso).toLocaleDateString('de-DE') : '—';
const sL = { draft:'Entwurf',sent:'Versendet',paid:'Bezahlt',cancelled:'Storniert' };
const rows2 = data.invoices.map((inv, i) => `
const sL = { draft:'Entwurf', sent:'Versendet', paid:'Bezahlt', cancelled:'Storniert (Orig.)', storno:'Stornorechnung' };
const rows2 = data.invoices.map((inv, i) => {
const isStorno = inv.status === 'storno';
const amtColor = isStorno ? 'color:var(--c-danger)' : (inv.amount_gross < 0 ? 'color:var(--c-danger)' : '');
return `
<tr style="${i%2===1?'background:var(--c-surface-2)':''}">
<td class="adm-td" style="font-family:monospace;font-size:var(--text-xs)">${_esc(inv.invoice_number)}</td>
<td class="adm-td" style="font-family:monospace;font-size:var(--text-xs);${isStorno?'color:var(--c-danger)':''}">${_esc(inv.invoice_number)}</td>
<td class="adm-td">${_esc(inv.recipient_name)}</td>
<td class="adm-td" style="text-align:right;font-weight:600">${_fmtE(inv.amount_gross)}</td>
<td class="adm-td">${sL[inv.status]||inv.status}</td>
<td class="adm-td" style="text-align:right;font-weight:600;${amtColor}">${_fmtE(inv.amount_gross)}</td>
<td class="adm-td" style="${isStorno?'color:var(--c-danger)':''}">${sL[inv.status]||inv.status}</td>
<td class="adm-td" style="font-size:var(--text-xs);color:var(--c-text-muted)">${_fmtD(inv.created_at)}</td>
</tr>`).join('');
</tr>`;
}).join('');
resultEl.innerHTML = `
<div style="font-size:var(--text-xs);font-weight:700;color:var(--c-text-secondary);margin-bottom:var(--space-2)">
${_esc(data.period || `Q${q} ${year}`)} ${data.count} Rechnung(en) · Brutto: ${_fmtE(data.total_gross)}