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:
Luca
2026-06-18 18:40:11 +02:00
parent 33d5408977
commit 707c5d0277
7 changed files with 45 additions and 22 deletions
@@ -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) => (