Kontrolle: Storno einer Anzahlung bei bestehendem Schluss-Entwurf der AB abgewiesen

409 'Erst den Entwurf der Schlussrechnung löschen.'; das Storno einer Anzahlung laeuft
unter der Kundensperre.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-07 15:18:46 +00:00
1 parent e3ae152f12
commit 1bb172ad91
3 files changed
+38 -7

No files matched your search

@@ -409,3 +409,10 @@ export async function pruefeVorSenden({ invoiceId, art, auftragIdMetadaten = '',
}
}
}
/** Gibt es zur AB (aus dem Projektdatensatz der Rechnung, ersatzweise aus den Stripe-Metadaten) einen Entwurf der Schlussrechnung? */
export async function hatSchlussEntwurf({ invoiceId, auftragIdMetadaten = '' }) {
const auftragId = (await getProjektRechnung(invoiceId))?.auftragId || auftragIdMetadaten
if (!auftragId) return false
return (await ladeFruehere(auftragId)).some((f) => f.art === 'schluss' && f.status === 'draft')
}
+15 -7
View File
@@ -4,7 +4,7 @@ import { getStripe, getOrCreateStripeCustomer, stripeCustomerFields, subscriptio
import { addDays, berlinDate, daysBetween } from './dates.js'
import { RECHNUNGS_ARTEN, AB_RECHNUNGS_ARTEN, sperreRechnung } from '../auftraege/konstanten.js'
import { withLocks } from '../keyedLock.js'
import { baueRechnung, ladeFestenKunden, buche, gibProjektRechnungFrei, setzeAbAbgerechnet, pruefeVorSenden } from '../auftraege/rechnungsbau.js'
import { baueRechnung, ladeFestenKunden, buche, gibProjektRechnungFrei, setzeAbAbgerechnet, pruefeVorSenden, hatSchlussEntwurf } from '../auftraege/rechnungsbau.js'
import { createProjektRechnung, updateProjektRechnung } from '../auftraege/projektRechnungenStore.js'
import { getSettings } from './settings.js'
@@ -507,13 +507,21 @@ export async function voidInvoice(invoiceId) {
const invoice = await readInvoice(invoiceId)
if (invoice.source !== 'projekt') throw httpError(409, ABO_MESSAGE)
if (invoice.status !== 'offen' && invoice.status !== 'ueberfaellig') throw httpError(409, 'Nur offene Rechnungen können storniert werden.')
try {
await getStripe().invoices.voidInvoice(invoice.id)
await bucheStorniert(invoice.id, invoice.art)
return await readInvoice(invoice.id)
} finally {
invalidateFinanceCache()
const stornieren = async () => {
// Der Schluss-Entwurf enthaelt die Minuszeile dieser Anzahlung: wuerde sie storniert, ginge er zu niedrig hinaus
if (invoice.art === 'anzahlung' && (await hatSchlussEntwurf({ invoiceId: invoice.id, auftragIdMetadaten: invoice.auftragId }))) {
throw httpError(409, 'Erst den Entwurf der Schlussrechnung löschen.')
}
try {
await getStripe().invoices.voidInvoice(invoice.id)
await bucheStorniert(invoice.id, invoice.art)
return await readInvoice(invoice.id)
} finally {
invalidateFinanceCache()
}
}
// Eine Anzahlung unter der Sperre des Kunden: das Anlegen und Senden einer Schlussrechnung laeuft nie zwischen Pruefung und Storno
return invoice.art === 'anzahlung' && invoice.webklarCustomerId ? withLocks(sperreRechnung(invoice.webklarCustomerId), stornieren) : stornieren()
}
/** Projekt-Entwurf bei Stripe loeschen, sonst status 409 (nur Entwuerfe lassen sich loeschen). */