fix(accounting): address the-luap PR #636 review
- #1 resolveTaxTreatment: an unconfigured (empty) reclaim-countries list no longer auto-classifies every supplier — incl. the admin's own domestic one — as foreign; defer auto-classification until the setting is set (+ test). - #2 pending re-bills on customer erase: eraseCustomer now returns the customer's not-yet-billed inbound docs to the inbox (null customer + unsorted) so they aren't billable to an anonymized account. (NB: picpeak has no hard customer delete — erase anonymizes in place — so the orphan/404 premise can't occur; this is hardening.) - #4 VatRateSelect: when >1 configured code shares the same rate, fall through to the legacy "(not configured)" option instead of silently picking the first. - #5 unwindBilledLine: delete the (mutable, never-issued) invoice when the unwound re-bill was its only line, instead of leaving a net-zero survivor. - #6 isInvoiceMutable: clarify in a comment that invoices have no 'draft' status (the editable state is 'scheduled' w/o send-at) — no behaviour change. - nit: collapse normalizeCurrency's tautological ternary. - Fix VAT picker i18n: t('vat.legacyRate') → 'ledger.vat.legacyRate' (the key's real home), so the legacy label localizes instead of always showing English. - Remove dead i18n keys left by the settings refactor (businessProfile.field VAT /hourly + profileFields.title/savedToast).
This commit is contained in:
@@ -38,10 +38,15 @@ export const VatRateSelect: React.FC<Props> = ({ rate, code, onChange, label, di
|
||||
});
|
||||
|
||||
// Selected option: prefer the snapshotted code; else a code whose rate matches
|
||||
// (legacy rows / no code stored); else the document's value is "off-list".
|
||||
// (legacy rows / no code stored) — BUT only when that rate is unambiguous. If
|
||||
// two configured codes share the rate (e.g. two 8.1% codes), rate-matching
|
||||
// could silently swap one for the other on the next save, so fall through to
|
||||
// the legacy "(not configured)" option and make the admin pick explicitly
|
||||
// (PR #636 review #4).
|
||||
const matched: VatCodeOption | undefined =
|
||||
(code ? codes.find((c) => c.code === code) : undefined)
|
||||
|| (!code ? codes.find((c) => Number(c.rate) === Number(rate)) : undefined);
|
||||
|| (!code && codes.filter((c) => Number(c.rate) === Number(rate)).length === 1
|
||||
? codes.find((c) => Number(c.rate) === Number(rate)) : undefined);
|
||||
const showLegacy = !matched;
|
||||
|
||||
return (
|
||||
@@ -61,7 +66,7 @@ export const VatRateSelect: React.FC<Props> = ({ rate, code, onChange, label, di
|
||||
>
|
||||
{showLegacy && (
|
||||
<option value={LEGACY}>
|
||||
{t('vat.legacyRate', '{{rate}}% (not configured)', { rate: Number(rate || 0).toFixed(1) })}
|
||||
{t('ledger.vat.legacyRate', '{{rate}}% (not configured)', { rate: Number(rate || 0).toFixed(1) })}
|
||||
</option>
|
||||
)}
|
||||
{codes.map((c) => (
|
||||
|
||||
@@ -17,9 +17,7 @@ export const CURRENCY_CODES: string[] = [
|
||||
* extra option so nothing is lost), or '' for empty input.
|
||||
*/
|
||||
export function normalizeCurrency(value: string | null | undefined): string {
|
||||
const cleaned = (value || '').trim().toUpperCase();
|
||||
if (!cleaned) return '';
|
||||
return CURRENCY_CODES.includes(cleaned) ? cleaned : cleaned;
|
||||
return (value || '').trim().toUpperCase();
|
||||
}
|
||||
|
||||
/** Build the option list, prepending an unknown-but-set value so it's preserved. */
|
||||
|
||||
@@ -1737,13 +1737,11 @@
|
||||
"disclaimer": "Sätze und MwSt-/Steuerbehandlung dienen nur als Orientierung — mit Ihrem Treuhänder prüfen.",
|
||||
"savedToast": "Buchhaltungseinstellungen gespeichert.",
|
||||
"profileFields": {
|
||||
"title": "MwSt-Bezeichnung & Stundensatz",
|
||||
"vatLabel": "MwSt-Bezeichnung (z. B. MwSt., VAT)",
|
||||
"vatLabelHint": "Wird als Bezeichnung der MwSt-Zeile auf Rechnungs-/Angebots-PDFs gedruckt. Leer lassen, um die Standardbezeichnung der Dokumentsprache zu verwenden.",
|
||||
"hourlyRate": "Standard-Stundensatz",
|
||||
"hourlyRatePlaceholder": "z. B. 120.00",
|
||||
"hourlyRateHint": "Verrechnungs-Fallback, wenn ein Kunde keinen eigenen Satz hat (Stundenerfassung). In {{currency}}, in Hauptwährungseinheiten. Leer lassen, um einen Satz pro Kunde oder pro Eintrag zu verlangen.",
|
||||
"savedToast": "Gespeichert."
|
||||
"hourlyRateHint": "Verrechnungs-Fallback, wenn ein Kunde keinen eigenen Satz hat (Stundenerfassung). In {{currency}}, in Hauptwährungseinheiten. Leer lassen, um einen Satz pro Kunde oder pro Eintrag zu verlangen."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4373,11 +4371,6 @@
|
||||
"defaultCurrency": "Standardwährung",
|
||||
"defaultLocale": "Standardsprache",
|
||||
"timezone": "Zeitzone (IANA)",
|
||||
"vatLabel": "MwSt-Bezeichnung",
|
||||
"vatRateDefault": "Standard-MwSt-Satz %",
|
||||
"defaultHourlyRate": "Standard-Stundensatz",
|
||||
"defaultHourlyRatePlaceholder": "z. B. 120.00",
|
||||
"defaultHourlyRateHint": "Fallback, wenn ein Kunde keinen eigenen Satz hat. In {{currency}}, in ganzen Einheiten. Leer lassen, um einen Satz pro Kunde oder pro Eintrag zu verlangen.",
|
||||
"defaultQrFormat": "Standard-QR-Format",
|
||||
"footerLine": "Fusszeile"
|
||||
},
|
||||
|
||||
@@ -1295,13 +1295,11 @@
|
||||
"disclaimer": "Rates and VAT/tax treatment are guidance only — verify with your Treuhänder.",
|
||||
"savedToast": "Accounting settings saved.",
|
||||
"profileFields": {
|
||||
"title": "VAT label & hourly rate",
|
||||
"vatLabel": "VAT label (e.g. MwSt., VAT)",
|
||||
"vatLabelHint": "Printed as the VAT-line label on invoice / quote PDFs. Leave blank to use the document language default.",
|
||||
"hourlyRate": "Default hourly rate",
|
||||
"hourlyRatePlaceholder": "e.g. 120.00",
|
||||
"hourlyRateHint": "Billing fallback used when a customer has no own rate (hours logging). In {{currency}}, major units. Leave blank to require a per-customer or per-entry rate.",
|
||||
"savedToast": "Saved."
|
||||
"hourlyRateHint": "Billing fallback used when a customer has no own rate (hours logging). In {{currency}}, major units. Leave blank to require a per-customer or per-entry rate."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4371,11 +4369,6 @@
|
||||
"defaultCurrency": "Default currency",
|
||||
"defaultLocale": "Default locale",
|
||||
"timezone": "Timezone (IANA)",
|
||||
"vatLabel": "VAT label (e.g. MwSt., VAT)",
|
||||
"vatRateDefault": "Default VAT rate %",
|
||||
"defaultHourlyRate": "Default hourly rate",
|
||||
"defaultHourlyRatePlaceholder": "e.g. 120.00",
|
||||
"defaultHourlyRateHint": "Fallback used when a customer has no own rate. In {{currency}}, major units. Leave blank to require a per-customer or per-entry rate.",
|
||||
"defaultQrFormat": "Default invoice QR",
|
||||
"footerLine": "PDF footer line"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user