feat(crm): toggle for VAT on late fees (jurisdiction-dependent)

Mahngebühr VAT differs by country (CH: liable; DE/AT: not), so it's now a
toggle (crm_invoices_late_fee_vat_enabled, seeded into migration 143 in place
since it isn't deployed yet — no compensation migration). When on, VAT is
added on top of the net fee at the org's default rate
(business_profile.vat_rate_default). Gated so it's a NO-OP when the org doesn't
charge VAT (default rate 0/unset) — i.e. enabling the toggle on a non-VAT org
adds nothing, as required. Settings UI: a self-documenting checkbox.

The fee is treated as net + VAT-on-top; the tax-report VAT breakdown for the
fee is part of the deferred dunning-document rework. tsc 0, build green,
9/9 workflow tests.
This commit is contained in:
Luca
2026-06-23 12:41:44 +02:00
parent b78bd979dc
commit eaceb7e71c
3 changed files with 25 additions and 3 deletions
@@ -34,6 +34,7 @@ const SETTING_KEYS = [
'crm_invoices_late_fee_type',
'crm_invoices_late_fee_minor',
'crm_invoices_late_fee_percent',
'crm_invoices_late_fee_vat_enabled',
'crm_invoices_late_fee_label',
'crm_invoices_skonto_business_days',
'crm_invoices_skonto_percent_default',
@@ -249,6 +250,7 @@ export const CrmSettingsPage: React.FC = () => {
<p className="font-medium">{t('crmSettings.lateFeeAgb.title', 'Late fees must be itemised in your terms (AGB)')}</p>
<p className="mt-1">{t('crmSettings.lateFeeAgb.body', 'Vertragliche Pflicht: Sätze wie „Es werden Mahnspesen erhoben“ reichen nicht aus. In den AGB muss die konkrete Gebühr klar beziffert sein (z.B. „CHF 20 ab der 2. Mahnung“). Mit dem Treuhänder prüfen.')}</p>
</div>
{checkbox('crm_invoices_late_fee_vat_enabled', 'Charge VAT on late fees (Switzerland — leave off for DE/AT; no effect if your organisation has no VAT rate)')}
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 mt-3">
<Input type="number" min={1} max={365}
label={t('crmSettings.crm_invoices_reminder_first_days.label', 'First reminder after (days past due)') as string}