Feat: Cashflow auf paid_amount, Differenz-Badge, Kulanz-Abschreibung im Bezahlt-Modal (SW by-v984)

This commit is contained in:
rene 2026-05-15 16:06:08 +02:00
parent 0f6b5afd6a
commit e714580d77
5 changed files with 70 additions and 14 deletions

View file

@ -3797,6 +3797,14 @@ window.Page_admin = (() => {
</td>
<td class="adm-td" style="text-align:right;font-weight:700;white-space:nowrap">
${_fmtEur(inv.amount_gross)}
${inv.status === 'paid' && inv.paid_amount != null && Math.abs(inv.paid_amount - inv.amount_gross) >= 0.01
? `<div style="font-size:10px;color:var(--c-warning,#d97706);font-weight:500">
erhalten: ${_fmtEur(inv.paid_amount)}
${inv.paid_amount < inv.amount_gross
? `<span style="color:var(--c-danger)">-${_fmtEur(inv.amount_gross - inv.paid_amount)}</span>`
: ''}
</div>`
: ''}
</td>
<td class="adm-td">${_statusBadge(inv.status)}</td>
<td class="adm-td" style="font-size:var(--text-xs);color:var(--c-text-muted);white-space:nowrap">
@ -4091,10 +4099,13 @@ window.Page_admin = (() => {
<input class="form-control" name="paid_at" type="date" value="${today}" required>
</div>
<div>
<label class="form-label" style="font-size:var(--text-xs)">Betrag () *</label>
<input class="form-control" name="paid_amount" type="number" min="0" step="0.01"
<label class="form-label" style="font-size:var(--text-xs)">Eingegangener Betrag () *</label>
<input class="form-control" name="paid_amount" id="${id}-amt" type="number" min="0" step="0.01"
value="${defaultAmount.toFixed(2)}" required>
</div>
<div id="${id}-diff" style="display:none;padding:var(--space-2) var(--space-3);
border-radius:var(--radius-md);background:#fff8f0;border:1px solid #f0a060;
font-size:var(--text-xs);color:#c05000;line-height:1.6"></div>
</form>
`,
footer: `
@ -4103,18 +4114,51 @@ window.Page_admin = (() => {
`,
});
// Differenz live anzeigen
const amtEl = document.getElementById(`${id}-amt`);
const diffEl = document.getElementById(`${id}-diff`);
const _checkDiff = () => {
const entered = parseFloat(amtEl?.value) || 0;
const diff = defaultAmount - entered;
if (Math.abs(diff) < 0.01) { diffEl.style.display = 'none'; return; }
diffEl.style.display = 'block';
if (diff > 0) {
diffEl.innerHTML = `Differenz: <strong>-${diff.toFixed(2)} €</strong> weniger als fakturiert.<br>
<label style="display:flex;align-items:center;gap:6px;margin-top:4px;cursor:pointer">
<input type="checkbox" id="${id}-kulanz">
<span>Als Kulanz/Forderungsverlust abschreiben (Notiz wird automatisch eingetragen)</span>
</label>`;
} else {
diffEl.innerHTML = `Überzahlung: <strong>+${(-diff).toFixed(2)} €</strong> mehr eingegangen.`;
diffEl.style.background = '#f0fff8';
diffEl.style.borderColor = '#34d399';
diffEl.style.color = '#065f46';
}
};
amtEl?.addEventListener('input', _checkDiff);
document.getElementById(id)?.addEventListener('submit', async e => {
e.preventDefault();
const fd = new FormData(e.target);
const fd = new FormData(e.target);
const paidAmount = parseFloat(fd.get('paid_amount'));
const diff = defaultAmount - paidAmount;
const kulanz = diff > 0.01 && document.getElementById(`${id}-kulanz`)?.checked;
const submitBtn = document.querySelector(`button[form="${id}"]`);
if (submitBtn) submitBtn.disabled = true;
try {
const kulanzNote = kulanz
? `Forderungsverlust/Kulanz: ${diff.toFixed(2)} EUR nicht eingegangen (${fd.get('paid_at')}). Als Kulanz abgeschrieben.`
: null;
await API.post(`/admin/invoices/${invoiceId}/pay`, {
paid_at: fd.get('paid_at'),
paid_amount: parseFloat(fd.get('paid_amount')),
paid_amount: paidAmount,
...(kulanzNote ? { notes: kulanzNote } : {}),
});
UI.modal.close();
UI.toast.success('Rechnung als bezahlt markiert.');
UI.toast.success(kulanz
? `Bezahlt (${paidAmount.toFixed(2)} €) · ${diff.toFixed(2)} € als Kulanz notiert.`
: 'Rechnung als bezahlt markiert.');
reload();
} catch (err) {
UI.toast.error(err.message || 'Fehler.');