69 lines
2.9 KiB
JavaScript
69 lines
2.9 KiB
JavaScript
import { withLocks } from '../keyedLock.js'
|
|
import { heute } from '../termine/zeit.js'
|
|
import { UkFehler } from './fehler.js'
|
|
import { naechte } from './datum.js'
|
|
import { ladeEinstellungen, ladeRechnung, ladeUnterkunft, legeRechnungAn, listeRechnungen as listeRe, speichereEinstellungen } from './speicher.js'
|
|
|
|
export { ladeRechnung }
|
|
export const listeRechnungen = (featureId) => listeRe(featureId)
|
|
|
|
const AUSSTELLER = ['firma', 'inhaber', 'strasse', 'plz', 'ort', 'email', 'telefon', 'steuernummer', 'ustId', 'iban', 'bic', 'bank']
|
|
|
|
/** Naechste lueckenlose Nummer '<Praefix>-<Jahr>-<NNNN>'; Jahr = Berliner Kalenderjahr, Jahreswechsel setzt den Zaehler auf 1. */
|
|
export function naechsteNummer(featureId, now = new Date()) {
|
|
return withLocks(['uk-rechnungsnr:' + featureId], async () => {
|
|
const einst = await ladeEinstellungen(featureId)
|
|
const jahr = Number(heute(now).slice(0, 4))
|
|
const z = einst?.rechnungZaehler
|
|
const n = z && z.jahr === jahr && Number.isInteger(z.n) ? z.n + 1 : 1
|
|
await speichereEinstellungen(featureId, { rechnungZaehler: { jahr, n } })
|
|
const praefix = (einst?.rechnungsPraefix || '').trim() || 'RE'
|
|
return `${praefix}-${jahr}-${String(n).padStart(4, '0')}`
|
|
})
|
|
}
|
|
|
|
const negiere = (summen) => ({
|
|
...summen,
|
|
bruttoCent: -summen.bruttoCent,
|
|
nettoCent: -summen.nettoCent,
|
|
ust: (summen.ust || []).map((u) => ({ ...u, nettoCent: -u.nettoCent, steuerCent: -u.steuerCent })),
|
|
})
|
|
|
|
/** Rechnung oder Stornorechnung zu einer Anfrage ausstellen (Nummer, Dokument). Die Sperre der Unterkunft haelt der Aufrufer. */
|
|
export async function stelleAus(ctx, anfrage, { art = 'rechnung', bezugNummer, now = new Date() } = {}) {
|
|
if (!Array.isArray(anfrage?.posten) || !anfrage.summen || typeof anfrage.summen.bruttoCent !== 'number') {
|
|
throw new UkFehler(409, 'status_ungueltig', { posten: 'Posten oder Summen fehlen.' })
|
|
}
|
|
const storno = art === 'storno'
|
|
const e = ctx.einstellungen || {}
|
|
const aussteller = Object.fromEntries(AUSSTELLER.map((k) => [k, e[k] ?? '']))
|
|
const unterkunft = await ladeUnterkunft(anfrage.unterkunftId)
|
|
const posten = anfrage.posten.map((p) => (storno ? { ...p, einzelCent: -p.einzelCent } : { ...p }))
|
|
const summen = storno ? negiere(anfrage.summen) : anfrage.summen
|
|
const nummer = await naechsteNummer(ctx.featureId, now)
|
|
const inhalt = {
|
|
aussteller,
|
|
empfaenger: anfrage.rechnungsadresse ?? null,
|
|
unterkunft: { name: unterkunft?.name ?? '' },
|
|
von: anfrage.von,
|
|
bis: anfrage.bis,
|
|
naechte: naechte(anfrage.von, anfrage.bis),
|
|
personen: anfrage.personen,
|
|
posten,
|
|
summen,
|
|
steuerart: anfrage.summen.steuerart ?? ((anfrage.summen.ust || []).length === 0 ? 'kleinunternehmer' : 'regel'),
|
|
zahlart: anfrage.zahlart || '',
|
|
bezugNummer: bezugNummer ?? '',
|
|
anfrageNummer: anfrage.nummer,
|
|
}
|
|
return legeRechnungAn({
|
|
featureId: ctx.featureId,
|
|
anfrageId: anfrage.$id,
|
|
art,
|
|
nummer,
|
|
datum: heute(now),
|
|
bruttoCent: summen.bruttoCent,
|
|
inhalt,
|
|
})
|
|
}
|