Rechnungen: Rechnungsnummer buchen, AB abgerechnet, Storno und Löschen geben frei
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ab3e6938da
commit
88a7305e41
3 files changed
+125
-6
No files matched your search
@@ -85,6 +85,14 @@ export async function buche(was, fn) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Schlussrechnung finalisiert: die AB geht von versendet oder bestaetigt auf abgerechnet. Unter der Sperre der AB. */
|
||||
export async function setzeAbAbgerechnet(auftragId) {
|
||||
await withLocks(sperreAuftrag(auftragId), async () => {
|
||||
const ab = await getAuftrag(auftragId)
|
||||
if (ab && (ab.status === 'versendet' || ab.status === 'bestaetigt')) await updateAuftrag(ab.id, { status: 'abgerechnet' })
|
||||
})
|
||||
}
|
||||
|
||||
/** Schlussrechnung weg (storniert, geloescht): die AB geht von abgerechnet zurueck. Unter der Sperre der AB. */
|
||||
export async function setzeAbZurueck(auftragId) {
|
||||
await withLocks(sperreAuftrag(auftragId), async () => {
|
||||
|
||||
@@ -4,8 +4,8 @@ import { getStripe, getOrCreateStripeCustomer, stripeCustomerFields, subscriptio
|
||||
import { addDays, berlinDate, daysBetween } from './dates.js'
|
||||
import { RECHNUNGS_ARTEN, sperreRechnung } from '../auftraege/konstanten.js'
|
||||
import { withLocks } from '../keyedLock.js'
|
||||
import { baueRechnung, ladeFestenKunden } from '../auftraege/rechnungsbau.js'
|
||||
import { createProjektRechnung } from '../auftraege/projektRechnungenStore.js'
|
||||
import { baueRechnung, ladeFestenKunden, buche, gibProjektRechnungFrei, setzeAbAbgerechnet } from '../auftraege/rechnungsbau.js'
|
||||
import { createProjektRechnung, updateProjektRechnung } from '../auftraege/projektRechnungenStore.js'
|
||||
import { getSettings } from './settings.js'
|
||||
|
||||
/**
|
||||
@@ -221,12 +221,34 @@ async function readInvoice(invoiceId) {
|
||||
return mapInvoice(await retrieveInvoice(invoiceId), { today: berlinDate() })
|
||||
}
|
||||
|
||||
// Buchungen am Projektdatensatz nach einem Stripe-Erfolg: scheitert Appwrite, wird nur protokolliert (siehe buche)
|
||||
const PROJEKT_ARTEN = RECHNUNGS_ARTEN.filter((art) => art !== 'frei')
|
||||
|
||||
async function bucheNummer(invoiceId, number, art) {
|
||||
if (!PROJEKT_ARTEN.includes(art) || !number) return
|
||||
await buche('Rechnungsnummer buchen', () => updateProjektRechnung(invoiceId, { rechnungsnummer: number }))
|
||||
}
|
||||
|
||||
/** Finalisiert: Rechnungsnummer in den Datensatz; bei einer Schlussrechnung geht die AB auf abgerechnet. */
|
||||
async function bucheFinalisiert(invoiceId, number, art, auftragId) {
|
||||
if (!PROJEKT_ARTEN.includes(art)) return
|
||||
await bucheNummer(invoiceId, number, art)
|
||||
if (art === 'schluss' && auftragId) await buche('AB abrechnen', () => setzeAbAbgerechnet(auftragId))
|
||||
}
|
||||
|
||||
/** Storniert oder geloescht: Datensatz inaktiv (Blaetter frei); bei einer Schlussrechnung geht die AB zurueck. */
|
||||
async function bucheStorniert(invoiceId, art) {
|
||||
if (!PROJEKT_ARTEN.includes(art)) return
|
||||
await buche('Rechnung freigeben', () => gibProjektRechnungFrei(invoiceId))
|
||||
}
|
||||
|
||||
// Finalisieren ohne Auto-Advance (die Rechnung bleibt in unserer Hand), dann per E-Mail senden. Scheitert nur das
|
||||
// Senden, ist die Rechnung schon festgeschrieben (offen, mit Nummer): der Fehler (status 502) nennt sie, damit der
|
||||
// Aufrufer sie erneut senden kann (sendInvoiceNow), statt eine zweite anzulegen.
|
||||
async function finalizeAndSend(stripe, invoiceId) {
|
||||
async function finalizeAndSend(stripe, invoiceId, { art = 'frei', auftragId = '' } = {}) {
|
||||
const finalized = await stripe.invoices.finalizeInvoice(invoiceId, { auto_advance: false })
|
||||
const number = finalized.number || ''
|
||||
await bucheFinalisiert(invoiceId, number, art, auftragId)
|
||||
try {
|
||||
await stripe.invoices.sendInvoice(invoiceId)
|
||||
} catch (cause) {
|
||||
@@ -402,7 +424,7 @@ async function createInvoice({ eingabe, daysUntilDue, note, senden, key }) {
|
||||
}
|
||||
|
||||
try {
|
||||
const number = senden ? await finalizeAndSend(stripe, invoiceId) : ''
|
||||
const number = senden ? await finalizeAndSend(stripe, invoiceId, { art: entwurf.art, auftragId: entwurf.auftrag?.id ?? '' }) : ''
|
||||
try {
|
||||
return await readInvoice(invoiceId)
|
||||
} catch (cause) {
|
||||
@@ -448,8 +470,12 @@ export async function sendInvoiceNow(invoiceId) {
|
||||
// (Name, E-Mail, Anschrift stehen danach unveraenderlich auf der Rechnung). Ohne Kennung (z. B. Entwurf aus dem
|
||||
// Stripe-Dashboard) gibt es keine WOMS-Daten zum Abgleichen.
|
||||
if (customer) await syncStripeCustomer(getStripe(), stripeCustomerIdOf(raw), customer)
|
||||
await finalizeAndSend(getStripe(), invoice.id)
|
||||
} else await getStripe().invoices.sendInvoice(invoice.id)
|
||||
await finalizeAndSend(getStripe(), invoice.id, { art: invoice.art, auftragId: invoice.auftragId })
|
||||
} else {
|
||||
// Selbstheilung nach einem ausgefallenen Buchen: eine fehlende Rechnungsnummer im Datensatz nachtragen
|
||||
await bucheNummer(invoice.id, invoice.number, invoice.art)
|
||||
await getStripe().invoices.sendInvoice(invoice.id)
|
||||
}
|
||||
return await readInvoice(invoice.id)
|
||||
} finally {
|
||||
invalidateFinanceCache()
|
||||
@@ -463,6 +489,7 @@ export async function voidInvoice(invoiceId) {
|
||||
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()
|
||||
@@ -476,6 +503,7 @@ export async function deleteDraft(invoiceId) {
|
||||
if (invoice.status !== 'entwurf') throw httpError(409, 'Nur Entwürfe können gelöscht werden.')
|
||||
try {
|
||||
await getStripe().invoices.del(invoice.id)
|
||||
await bucheStorniert(invoice.id, invoice.art)
|
||||
} finally {
|
||||
invalidateFinanceCache()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user