From 79f230ea2c845845a6a74d6381b7b513e1ea060c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 7 Oct 2026 15:19:17 +0000 Subject: [PATCH] =?UTF-8?q?Kontrolle:=20Rechnungsdialog=20zeigt=20bei=20St?= =?UTF-8?q?ripe-Ausfall=20keine=20Betr=C3=A4ge=20und=20keine=20Vorgabe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bei stripeOk false steht 'Stripe nicht erreichbar – Beträge unbekannt' statt 'bereits berechnet 0,00 €', und es wird keine Rechnungsart vorgeschlagen. Co-Authored-By: Claude Sonnet 5.5 --- src/components/auftraege/RechnungDialog.jsx | 12 +++++------ src/lib/rechnungDialogView.js | 15 +++++++++++-- tests/unit/rechnungDialogView.test.mjs | 24 +++++++++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/components/auftraege/RechnungDialog.jsx b/src/components/auftraege/RechnungDialog.jsx index d02bd79..ee9fecb 100644 --- a/src/components/auftraege/RechnungDialog.jsx +++ b/src/components/auftraege/RechnungDialog.jsx @@ -17,7 +17,7 @@ import { useWeitereBlaetter, } from '../../hooks/useRechnungDaten' import { createInvoice } from '../../lib/financeApi' -import { canInvoice, euro } from '../../lib/financeFormat' +import { canInvoice } from '../../lib/financeFormat' import { ADDRESS_INCOMPLETE_TEXT, INVOICE_LIMITS, @@ -33,8 +33,8 @@ import { abLabel, anzahlungStandard, artOptionen, + abSummenText, aufwandAuftrag, - berechnetFuerAuftrag, gueltigeFestpreisAbs, projektOptionen, standardArt, @@ -129,9 +129,10 @@ function RechnungFormular({ onClose, onCreated, projektId, auftraegeAktiv, onSho if (projekt === '') optionen = artOptionen({ projektId: '', auftraege: [], rechnungen: [], auftraegeAktiv: aktiv }) else if (projekt && detail.data) optionen = artOptionen({ projektId: projekt, auftraege, rechnungen, auftraegeAktiv: aktiv }) + const stripeOk = detail.data?.stripeOk const art = optionen.some((o) => o.key === state.art && o.enabled) ? state.art - : standardArt(optionen, { auftraege, rechnungen, offeneMinuten: detail.data?.konto?.zeit?.offeneMinuten || 0 }) + : standardArt(optionen, { auftraege, rechnungen, offeneMinuten: detail.data?.konto?.zeit?.offeneMinuten || 0, stripeOk }) const festpreisAbs = gueltigeFestpreisAbs(auftraege) const auftragId = @@ -268,7 +269,6 @@ function RechnungFormular({ onClose, onCreated, projektId, auftraegeAktiv, onSho const abBlock = () => { if (!ab) return null - const berechnet = berechnetFuerAuftrag(rechnungen, ab.id) return (
{festpreisAbs.length > 1 ? ( @@ -284,9 +284,7 @@ function RechnungFormular({ onClose, onCreated, projektId, auftraegeAktiv, onSho ) : (

{abLabel(ab)}

)} -

- Auftragssumme {euro(ab.summeCents)} · bereits berechnet {euro(berechnet)} · noch nicht berechnet {euro(Math.max(0, ab.summeCents - berechnet))} -

+

{abSummenText({ ab, rechnungen, stripeOk })}

) } diff --git a/src/lib/rechnungDialogView.js b/src/lib/rechnungDialogView.js index 771f736..91c2618 100644 --- a/src/lib/rechnungDialogView.js +++ b/src/lib/rechnungDialogView.js @@ -18,6 +18,7 @@ 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 STRIPE_UNBEKANNT = 'Stripe nicht erreichbar – Beträge unbekannt' export const STEUERNUMMER_RECHNUNG_HINWEIS = 'Die Steuernummer fehlt noch – sie gehört auf jede Rechnung (§ 34a UStDV). Eintragen unter Finanzen → Einstellungen.' @@ -109,6 +110,14 @@ export function hatEntwurfZurAuftrag(rechnungen, auftragId) { return Array.isArray(rechnungen) && rechnungen.some((r) => r && r.auftragId === auftragId && r.status === 'entwurf') } +/** Zeile unter der AB-Wahl. Ist Stripe nicht erreichbar, sind die berechneten Beträge unbekannt (nicht 0,00 €). */ +export function abSummenText({ ab, rechnungen, stripeOk }) { + const summe = `Auftragssumme ${euro(ab?.summeCents)}` + if (stripeOk === false) return `${summe} · ${STRIPE_UNBEKANNT}` + const berechnet = berechnetFuerAuftrag(rechnungen, ab?.id) + return `${summe} · bereits berechnet ${euro(berechnet)} · noch nicht berechnet ${euro(Math.max(0, (Number(ab?.summeCents) || 0) - berechnet))}` +} + // --- Rechnungsarten ----------------------------------------------------------------------------------------- /** Wählbare Arten mit Sperrgrund: [{ key, label, enabled, hint }]. */ @@ -127,11 +136,13 @@ export function artOptionen({ projektId, auftraege, rechnungen, auftraegeAktiv } ] } -/** Vorbelegte Art: Anzahlung, Schlussrechnung, Aufwand oder Festbetrag je nach Lage ('' ohne Wahl). */ -export function standardArt(optionen, { auftraege, rechnungen, offeneMinuten }) { +/** Vorbelegte Art: Anzahlung, Schlussrechnung, Aufwand oder Festbetrag je nach Lage ('' ohne Wahl, auch bei Stripe-Ausfall). */ +export function standardArt(optionen, { auftraege, rechnungen, offeneMinuten, stripeOk = true }) { const liste = Array.isArray(optionen) ? optionen : [] if (liste.length === 0) return '' if (liste.length === 1 && liste[0].key === 'frei') return 'frei' + // Ist Stripe nicht erreichbar, sind die berechneten Beträge unbekannt: keine Art vorschlagen + if (stripeOk === false) return '' const waehlbar = (key) => liste.some((o) => o.key === key && o.enabled) const jung = gueltigeFestpreisAbs(auftraege)[0] if ( diff --git a/tests/unit/rechnungDialogView.test.mjs b/tests/unit/rechnungDialogView.test.mjs index 98aa954..1bece4e 100644 --- a/tests/unit/rechnungDialogView.test.mjs +++ b/tests/unit/rechnungDialogView.test.mjs @@ -9,6 +9,7 @@ const { artOptionen, standardArt, standardAuftrag, anzahlungStandard, ausgewaehlteIds, blaetterSumme, blattAusAppwrite, ticketOptionen, zahlungszielVorschlag, STEUERNUMMER_RECHNUNG_HINWEIS, steuernummerHinweis, + STRIPE_UNBEKANNT, abSummenText, } = await loadModule('src/lib/rechnungDialogView.js') const ab = (over = {}) => ({ @@ -248,3 +249,26 @@ test('steuernummerHinweis: nicht sperrender Hinweis, solange die Steuernummer in assert.equal(steuernummerHinweis(null), '') assert.equal(steuernummerHinweis([]), '') }) + +test('abSummenText: Auftragssumme, berechnet und noch offen; bei Stripe-Ausfall „Beträge unbekannt“ statt 0,00 €', () => { + assert.equal( + abSummenText({ ab: ab(), rechnungen: [rk()], stripeOk: true }), + 'Auftragssumme 800,00 € · bereits berechnet 400,00 € · noch nicht berechnet 400,00 €' + ) + assert.equal(abSummenText({ ab: ab(), rechnungen: [], stripeOk: undefined }), 'Auftragssumme 800,00 € · bereits berechnet 0,00 € · noch nicht berechnet 800,00 €') + assert.equal(STRIPE_UNBEKANNT, 'Stripe nicht erreichbar – Beträge unbekannt') + const aus = abSummenText({ ab: ab(), rechnungen: [], stripeOk: false }) + assert.equal(aus, 'Auftragssumme 800,00 € · Stripe nicht erreichbar – Beträge unbekannt') + assert.ok(!aus.includes('bereits berechnet')) +}) + +test('standardArt: bei Stripe-Ausfall wird keine Art vorgeschlagen (Beträge unbekannt)', () => { + const lage = { auftraege: [ab()], rechnungen: [], offeneMinuten: 90 } + const o = optionen({ auftraege: [ab()] }) + assert.equal(standardArt(o, lage), 'anzahlung') + assert.equal(standardArt(o, { ...lage, stripeOk: true }), 'anzahlung') + assert.equal(standardArt(o, { ...lage, stripeOk: false }), '') + // ohne Projekt gibt es nichts zu raten: die freie Rechnung bleibt die Vorgabe + const frei = artOptionen({ projektId: '', auftraege: [], rechnungen: [], auftraegeAktiv: true }) + assert.equal(standardArt(frei, { ...lage, stripeOk: false }), 'frei') +})