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
@@ -19,6 +19,10 @@ exports.up = async function (knex) {
const seeds = [
{ setting_key: 'crm_invoices_late_fee_type', setting_value: JSON.stringify('flat'), setting_type: 'crm' },
{ setting_key: 'crm_invoices_late_fee_percent', setting_value: JSON.stringify(0), setting_type: 'crm' },
// VAT on the late fee is jurisdiction-dependent (CH: yes; DE/AT: no), so it's
// a toggle. Default OFF (preserve current no-VAT behaviour). No-op anyway
// when the org doesn't charge VAT (business_profile.vat_rate_default = 0).
{ setting_key: 'crm_invoices_late_fee_vat_enabled', setting_value: JSON.stringify(false), setting_type: 'crm' },
];
for (const s of seeds) {
const exists = await knex('app_settings').where({ setting_key: s.setting_key }).first();
@@ -29,6 +33,6 @@ exports.up = async function (knex) {
exports.down = async function (knex) {
if (!(await knex.schema.hasTable('app_settings'))) return;
await knex('app_settings')
.whereIn('setting_key', ['crm_invoices_late_fee_type', 'crm_invoices_late_fee_percent'])
.whereIn('setting_key', ['crm_invoices_late_fee_type', 'crm_invoices_late_fee_percent', 'crm_invoices_late_fee_vat_enabled'])
.del();
};