Kontrolle: Rechnungsdialog weist auf die fehlende Steuernummer hin

Nicht sperrender Hinweis (§ 34a UStDV), solange financeSettings.steuernummer leer ist;
die Einstellungen werden einmal geladen, bei einem Ladefehler erscheint kein Hinweis.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-07 15:17:29 +00:00
1 parent dec7bea7d2
commit b556a459bb
4 files changed
+49 -1

No files matched your search

@@ -10,6 +10,7 @@ import { useCustomers } from '../../hooks/useCustomers'
import { useScrollIntoView } from '../../hooks/useScrollIntoView'
import {
useKundenTickets,
useEinstellungen,
useProjektkontoDetail,
useProjektkonten,
useRechnungVorschau,
@@ -39,6 +40,7 @@ import {
standardArt,
standardAuftrag,
startProjekt,
steuernummerHinweis,
ticketOptionen,
wirksamesProjekt,
zahlungszielVorschlag,
@@ -114,6 +116,7 @@ function RechnungFormular({ onClose, onCreated, projektId, auftraegeAktiv, onSho
const kunde = kundeDoc && canInvoice(kundeDoc).ok ? kundeDoc : null
const blockiert = Boolean(kundeDoc) && !kunde
const einstellungen = useEinstellungen()
const konten = useProjektkonten(kunde?.$id)
const projekt = wirksamesProjekt(state.projektId, konten.data?.konten)
const aktiv = konten.data ? Boolean(konten.data.auftraegeAktiv) : Boolean(auftraegeAktiv)
@@ -458,6 +461,11 @@ function RechnungFormular({ onClose, onCreated, projektId, auftraegeAktiv, onSho
<p className="rd-hint">Bitte einen Kunden aus der Liste wählen. Nur feste Kunden mit E-Mail-Adresse erscheinen dort.</p>
)}
{!festerKunde && kunde && kunde.email && <p className="rd-hint">Die Rechnung geht an {kunde.email}.</p>}
{steuernummerHinweis(einstellungen) && (
<p className="msg warn" role="status">
{steuernummerHinweis(einstellungen)}
</p>
)}
{kunde && gate.warn && (
<AddressWarning
customer={kunde}
+17 -1
View File
@@ -1,7 +1,7 @@
// Hooks des Rechnungsdialogs: Projektliste, Projektkonto, Vorschau, Tickets des Kunden und Arbeitsblätter weiterer Tickets.
// Überall zählt nur die jüngste Antwort; nach dem Ausbau eines Hooks zählt keine mehr.
import { useCallback, useEffect, useRef, useState } from 'react'
import { getProjektkonten, getProjektkonto, previewInvoice } from '../lib/financeApi'
import { getProjektkonten, getProjektkonto, getSettings, previewInvoice } from '../lib/financeApi'
import { getAbgerechnet } from '../lib/teamApi'
import { listKundenTickets, listTicketBlaetter } from '../lib/rechnungDialogApi'
import { blattAusAppwrite } from '../lib/rechnungDialogView'
@@ -61,6 +61,22 @@ function useGeladen(laden, schluessel, fallbackError) {
}
}
/** Finanzeinstellungen, einmal geladen: undefined = lädt, null = nicht ladbar, sonst das Objekt `einstellungen`. */
export function useEinstellungen() {
const [einstellungen, setEinstellungen] = useState(undefined)
useEffect(() => {
let aktuell = true
getSettings().then(
(d) => aktuell && setEinstellungen(d?.einstellungen ?? null),
() => aktuell && setEinstellungen(null)
)
return () => {
aktuell = false
}
}, [])
return einstellungen
}
/** Projektkonten eines Kunden: { auftraegeAktiv, kunde, konten, ohneProjekt, stripeOk }. */
export function useProjektkonten(kundeId) {
return useGeladen(() => getProjektkonten(kundeId), kundeId, 'Die Projekte konnten nicht geladen werden.')
+8
View File
@@ -3,6 +3,7 @@
// Arbeitsblätter (Auswahl, Summen, Umwandlung aus Appwrite-Dokumenten) und die Ticketliste für „Weiteres Ticket hinzunehmen“.
import { euro, dateDe, ymdBerlin } from './financeFormat'
import { INVOICE_LIMITS } from './financeView'
import { steuernummerFehlt } from './kundenFelder'
import { worksheetMinutes } from './support'
import { germanDateToIso } from './ticketForm'
import { ticketsText } from './projektkontoView'
@@ -17,6 +18,13 @@ export const ART_OPTION_LABEL = {
export const AB_HINWEIS = 'Anzahlung und Schlussrechnung brauchen eine Auftragsbestätigung.'
export const ENTWURF_HINWEIS = 'Zu dieser Auftragsbestätigung gibt es noch einen Entwurf. Erst senden oder löschen.'
export const OHNE_PROJEKT = 'Ohne Projekt'
export const STEUERNUMMER_RECHNUNG_HINWEIS =
'Die Steuernummer fehlt noch – sie gehört auf jede Rechnung (§ 34a UStDV). Eintragen unter Finanzen → Einstellungen.'
/** Hinweis im Rechnungsdialog, solange die Steuernummer fehlt; ohne Einstellungen (lädt, Ladefehler) kein Hinweis. */
export function steuernummerHinweis(einstellungen) {
return steuernummerFehlt(einstellungen) ? STEUERNUMMER_RECHNUNG_HINWEIS : ''
}
const GUELTIG = ['versendet', 'bestaetigt']
const FINALISIERT = ['offen', 'ueberfaellig', 'bezahlt', 'uneinbringlich']
+16
View File
@@ -8,6 +8,7 @@ const {
gueltigeFestpreisAbs, aufwandAuftrag, abLabel, berechnetFuerAuftrag, hatEntwurfZurAuftrag,
artOptionen, standardArt, standardAuftrag, anzahlungStandard,
ausgewaehlteIds, blaetterSumme, blattAusAppwrite, ticketOptionen, zahlungszielVorschlag,
STEUERNUMMER_RECHNUNG_HINWEIS, steuernummerHinweis,
} = await loadModule('src/lib/rechnungDialogView.js')
const ab = (over = {}) => ({
@@ -232,3 +233,18 @@ test('zahlungszielVorschlag: Anzahlung und Schluss übernehmen das Zahlungsziel
assert.equal(zahlungszielVorschlag({ art: 'frei', ab: dreissig }), '14')
assert.equal(zahlungszielVorschlag({ art: '', auftraege: [aufwand] }), '14')
})
test('steuernummerHinweis: nicht sperrender Hinweis, solange die Steuernummer in den Einstellungen leer ist', () => {
assert.equal(
STEUERNUMMER_RECHNUNG_HINWEIS,
'Die Steuernummer fehlt noch – sie gehört auf jede Rechnung (§ 34a UStDV). Eintragen unter Finanzen → Einstellungen.'
)
for (const leer of [{}, { steuernummer: '' }, { steuernummer: ' ' }, { steuernummer: null }]) {
assert.equal(steuernummerHinweis(leer), STEUERNUMMER_RECHNUNG_HINWEIS)
}
assert.equal(steuernummerHinweis({ steuernummer: '12/345/67890' }), '')
// noch nicht geladen (undefined) und Ladefehler (null) behaupten nichts
assert.equal(steuernummerHinweis(undefined), '')
assert.equal(steuernummerHinweis(null), '')
assert.equal(steuernummerHinweis([]), '')
})