Finanzen: Rueckfrage vor dem Senden, Anschrift des Kunden pruefen, Fehlermeldungen im Bild

Senden einer Rechnung (Dialog und Entwurf in der Tabelle) fragt vorher: "Rechnung fuer
<Kunde> ueber <Betrag> jetzt festschreiben und an <E-Mail> senden? Danach laesst sie sich
nur noch stornieren."

Fehlt dem Kunden Strasse, PLZ oder Ort, zeigen Rechnungsdialog und Kundenansicht die
Warnung "Adresse des Kunden unvollstaendig ..." mit Weg zur Uebersicht, und Senden ist
ueber 250 € gesperrt (§ 14 Abs. 4 Nr. 1 UStG, Kleinbetragsrechnung); Entwuerfe bleiben
moeglich. Reine Helfer addressComplete und invoiceSendGate samt Tests.

Fehlermeldungen in Rechnungs- und Zuordnen-Dialog werden per scrollIntoView
({ block: 'nearest' }) in den sichtbaren Bereich geholt.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-01 15:22:00 +00:00
1 parent 1e72faf97c
commit 72f07410fe
9 files changed
+240 -15

No files matched your search

+45 -1
View File
@@ -542,13 +542,31 @@ export function invoiceRows(invoices) {
})
}
/** Rückfrage vor dem Löschen eines Entwurfs, dem Stornieren und der Erinnerung; '' für Aktionen ohne Rückfrage. */
/**
* Rückfrage vor dem Festschreiben und Senden einer Rechnung (Dialog „Senden“ und Entwurf in der Tabelle):
* „Rechnung für <Kunde> über <Betrag> jetzt festschreiben und an <E-Mail> senden? Danach lässt sie sich nur noch
* stornieren.“ Fehlen Name oder E-Mail, steht dort „den Kunden“.
*/
export function invoiceSendConfirmText({ name, email, totalCents } = {}) {
const mail = text(email)
const who = text(name) || mail || 'den Kunden'
return `Rechnung für ${who} über ${euro(totalCents)} jetzt festschreiben und an ${mail || 'den Kunden'} senden? Danach lässt sie sich nur noch stornieren.`
}
/**
* Rückfrage vor dem Senden eines Entwurfs, dem Löschen eines Entwurfs, dem Stornieren und der Erinnerung; '' für
* Aktionen ohne Rückfrage (auch „send“ bei einer Rechnung, die kein Entwurf mehr ist).
*/
export function invoiceConfirmText(actionKey, invoice) {
const inv = obj(invoice)
const who = text(inv.customerName) || text(inv.customerEmail) || 'den Kunden'
const total = euro(inv.totalCents)
const number = text(inv.number) || 'ohne Nummer'
switch (actionKey) {
case 'send':
return inv.status === 'entwurf'
? invoiceSendConfirmText({ name: inv.customerName, email: inv.customerEmail, totalCents: inv.totalCents })
: ''
case 'delete':
return `Den Entwurf für ${who} über ${total} wirklich löschen? Das lässt sich nicht rückgängig machen.`
case 'void':
@@ -778,6 +796,32 @@ export function customerInvoicesGate(customer) {
return { ok: false, title: check.reason, hint: '' }
}
// --- Anschrift des Rechnungsempfängers (§ 14 Abs. 4 Nr. 1 UStG) --------------------------------------------
/** Kleinbetragsrechnung (§ 33 UStDV): bis 250 € brauchen Rechnungen keine Anschrift des Empfängers. */
export const SMALL_INVOICE_MAX_CENTS = 25_000
export const ADDRESS_INCOMPLETE_TEXT =
'Adresse des Kunden unvollständig (Straße, PLZ, Ort). Für den Versand bitte zuerst ergänzen.'
/** Hat der Kunde (Appwrite-Dokument) Straße, PLZ und Ort? Leere und nur aus Leerzeichen bestehende Felder zählen nicht. */
export function addressComplete(customer) {
const c = obj(customer)
return Boolean(text(c.street) && text(c.postalCode) && text(c.city))
}
/**
* Darf diese Rechnung gesendet werden? { warn, sendAllowed, text }. warn: die Anschrift des Kunden ist unvollständig
* (Hinweis mit `text` zeigen). sendAllowed ist nur dann false, wenn zugleich die Anschrift fehlt und die Summe über
* 250 € liegt (genau 250,00 € ist noch eine Kleinbetragsrechnung); Entwürfe sind nie betroffen (sie haben keinen
* Versand). Ohne Kunden gibt es keine Warnung, die Formularprüfung meldet das fehlende Feld.
*/
export function invoiceSendGate(customer, totalCents) {
if (!customer || typeof customer !== 'object') return { warn: false, sendAllowed: true, text: '' }
const complete = addressComplete(customer)
const small = isNum(totalCents) && totalCents <= SMALL_INVOICE_MAX_CENTS // unbekannte Summe gilt nicht als klein
return { warn: !complete, sendAllowed: complete || small, text: complete ? '' : ADDRESS_INCOMPLETE_TEXT }
}
// --- Buchungen (Block 7) -----------------------------------------------------------------------------------
const RANK = { hoch: 0, mittel: 1, niedrig: 2 }