Files
FEDEO/frontend/utils/cashDiscount.ts
florianfederspiel af4a283eb5 feat(accounting): Skonto bei Zahlungen auf Ausgangs- und Eingangsbelege buchen
Skonto wird an der Bankzuordnung gespeichert und gleicht den Beleg zusammen
mit der Zahlung aus. Der Betrag wird anteilig nach Steuersätzen auf die
Skontokonten verteilt (Standard SKR03, je Mandant einstellbar), im
DATEV-Export als eigene Buchung je Steuersatz ausgegeben und in der
USt-Auswertung im Zeitraum der Zahlung berücksichtigt. Schlussrechnungen
verteilen den Skonto auf die Restforderung nach Abzug der Abschläge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 11:31:47 +01:00

62 lines
2.5 KiB
TypeScript

// Skonto wird an der Bankzuordnung gespeichert (cashDiscountAmount, gleiches Vorzeichen wie amount).
// Zahlbetrag und Skonto gleichen den Beleg gemeinsam aus; der Bankumsatz selbst enthält nur amount.
export const CASH_DISCOUNT_RATES = ["19", "7", "0"] as const
export const DEFAULT_CASH_DISCOUNT_ACCOUNTS = {
received: { "19": "3736", "7": "3731", "0": "3730" },
granted: { "19": "8736", "7": "8731", "0": "8730" },
}
export const getAllocationCashDiscount = (allocation: any) => Number(allocation?.cashDiscountAmount || 0)
export const getAllocationSettledAmount = (allocation: any) =>
Number(allocation?.amount || 0) + getAllocationCashDiscount(allocation)
export const sumSettledAllocations = (allocations: any[] = []) =>
(allocations || []).reduce((sum, allocation) => sum + getAllocationSettledAmount(allocation), 0)
export const hasCashDiscount = (allocation: any) =>
!allocation?.archived && Array.isArray(allocation?.cashDiscountSplit) && allocation.cashDiscountSplit.length > 0
// Steuerkorrektur nach § 17 UStG im Zeitraum der Zahlung.
// Ausgangsbelege mindern die Umsatzsteuer, Eingangsbelege die Vorsteuer (Vorzeichen aus der Zahlung).
export function getCashDiscountTaxBreakdown(allocation: any) {
const output = { net19: 0, tax19: 0, net7: 0, tax7: 0, net0: 0 }
const input = { ...output }
if (!hasCashDiscount(allocation)) return { output, input }
const isOutgoing = Boolean(allocation.createddocument || allocation.cd_id)
const target = isOutgoing ? output : input
const sign = isOutgoing ? -1 : 1
for (const item of allocation.cashDiscountSplit) {
const net = Number(item?.net || 0) * sign
const tax = Number(item?.tax || 0) * sign
if (Number(item?.rate) === 19) {
target.net19 += net
target.tax19 += tax
} else if (Number(item?.rate) === 7) {
target.net7 += net
target.tax7 += tax
} else {
target.net0 += net
}
}
return { output, input }
}
export const resolveCashDiscountAccounts = (config: any) => {
const result = {
received: { ...DEFAULT_CASH_DISCOUNT_ACCOUNTS.received },
granted: { ...DEFAULT_CASH_DISCOUNT_ACCOUNTS.granted },
}
for (const side of ["received", "granted"] as const) {
for (const rate of CASH_DISCOUNT_RATES) {
const value = config?.[side]?.[rate]
if (value !== undefined && value !== null && String(value).trim()) result[side][rate] = String(value).trim()
}
}
return result
}