diff --git a/server/services/finance/stripeFinance.js b/server/services/finance/stripeFinance.js index d3f2a6c..5bb49e5 100644 --- a/server/services/finance/stripeFinance.js +++ b/server/services/finance/stripeFinance.js @@ -197,10 +197,14 @@ function assertAddressForSend(customer, totalCents) { if (!complete && totalCents > SMALL_INVOICE_MAX_CENTS) throw httpError(409, ADDRESS_INCOMPLETE_MESSAGE) } -/** Rechnungsfuss: Kleinunternehmer-Hinweis, Bankdaten (falls gesetzt), Hinweis auf den Verwendungszweck. */ -export function invoiceFooter(bankDetailsText = '') { +/** + * Rechnungsfuss: Kleinunternehmer-Hinweis, Steuernummer (falls gesetzt, § 34a UStDV), Bankdaten (falls gesetzt), Hinweis + * auf den Verwendungszweck. + */ +export function invoiceFooter(bankDetailsText = '', steuernummer = '') { const bank = typeof bankDetailsText === 'string' ? bankDetailsText.trim() : '' - return [config.stripe.invoiceFooter, bank, PAYMENT_REFERENCE_NOTE].filter(Boolean).join('\n') + const nummer = typeof steuernummer === 'string' ? steuernummer.trim() : '' + return [config.stripe.invoiceFooter, nummer ? `Steuernummer: ${nummer}` : '', bank, PAYMENT_REFERENCE_NOTE].filter(Boolean).join('\n') } const isWholeNumberBetween = (value, min, max) => Number.isInteger(value) && value >= min && value <= max @@ -382,7 +386,7 @@ async function createInvoice({ eingabe, daysUntilDue, note, senden, key }) { if (senden && !String(customer.email || '').trim()) throw httpError(400, NO_EMAIL_MESSAGE) if (senden) assertAddressForSend(customer, entwurf.summeCents) - const { bankDetailsText } = await getSettings() + const { bankDetailsText, steuernummer } = await getSettings() const stripe = getStripe() const stripeCustomerId = await getOrCreateStripeCustomer(customer) // Vor dem ersten Rechnungsaufruf: scheitert das, gibt es keine halbfertige Rechnung @@ -397,7 +401,7 @@ async function createInvoice({ eingabe, daysUntilDue, note, senden, key }) { auto_advance: false, pending_invoice_items_behavior: 'exclude', currency: 'eur', - footer: invoiceFooter(bankDetailsText), + footer: invoiceFooter(bankDetailsText, steuernummer), description: note || undefined, metadata: entwurf.metadata, custom_fields: entwurf.customFields.length ? entwurf.customFields : undefined, diff --git a/test/financeSettings.test.js b/test/financeSettings.test.js index 6bd8900..cf07228 100644 --- a/test/financeSettings.test.js +++ b/test/financeSettings.test.js @@ -604,6 +604,18 @@ test('Rechnung: ohne Einstellungsdokument nur Kleinunternehmer-Hinweis und Verwe assert.equal(aw.docs(SETTINGS()).size, 0) }) +test('Rechnung: mit gesetzter Steuernummer steht sie als Zeile direkt nach dem § 19-Satz im Rechnungsfuss, ohne Wert bleibt der Fuss unveraendert', async () => { + assert.equal((await patchSettings({ steuernummer: '123/456/78901', bankDetailsText: 'IBAN DE00 0000' })).status, 200) + await createProjectInvoice({ customerId: 'c1', items: ITEMS, createdBy: 'kenso@example.invalid' }) + assert.equal(footerOfLastDraft(), `${config.stripe.invoiceFooter}\nSteuernummer: 123/456/78901\nIBAN DE00 0000\n${PAYMENT_NOTE}`) + + // geloescht: wieder wie vorher + assert.equal((await patchSettings({ steuernummer: '', bankDetailsText: '' })).status, 200) + resetInvoiceCreateGuardForTests() + await createProjectInvoice({ customerId: 'c1', items: ITEMS, createdBy: 'kenso@example.invalid' }) + assert.equal(footerOfLastDraft(), `${config.stripe.invoiceFooter}\n${PAYMENT_NOTE}`) +}) + test('Rechnung: sind die Einstellungen nicht lesbar, scheitert sie vor jedem Stripe-Aufruf', async () => { aw.failNext({ collection: SETTINGS(), method: 'GET', status: 500 }) await assert.rejects(createProjectInvoice({ customerId: 'c1', items: ITEMS, createdBy: 'k' }))