Skip to content

2.6.0

Released: TBD

Highlights

  • Tax rates now store the whole percent (7) instead of the decimal fraction (0.07), so the stored value matches the percent everyone actually reads — and the old "100× under-tax" footgun is gone.

Added

  • TaxRate.rate_fraction — a read-only rate / 100 view for fraction consumers, the symmetric counterpart to rate_percent. See Tax Rate Stored as Percent.
  • scripts/backfill_tax_rate_percent.py — idempotent, dry-run-by-default backfill that multiplies existing tax_rates.rate rows by 100.

Changed

  • tax_filings.TaxRate.rate is now a whole percent (7.0000) instead of a decimal fraction (0.0700). rate_percent is now an identity alias of rate (kept for back-compat). This supersedes the "rate stays the canonical fraction" design in Unify Tax-Rate Models.
  • items.ITBMSTaxRate.rate and the per-line InvoiceDetail.tax_rate / SupplierInvoiceDetail.tax_rate snapshots are unchanged — they were already whole percents; the two halves of the in-flight unification now agree.

Fixed

  • Removes the fraction/percent scale gap on the unified TaxRate that made any consumer reading rate (instead of rate_percent) compute 0.07% of the base.

Migrations

  • tax_rate_store_percent (20260619_tax_rate_store_percent.py, down_revision drop_employee_bank_account_id) — updates the tax_rates.rate column comment and converts existing rows × 100 (idempotent guard 0 < rate < 1; covers both existing tenants and fresh tenants seeded as fractions by expand_unified_tax_rate).
  • One-off script (optional pre-deploy audit / manual repair): scripts/backfill_tax_rate_percent.py — same idempotent conversion, dry run by default; --apply to commit, --tenant <id> to scope.

Frontend impact

  • The gated, accountant-only taxRates management surface changes meaning (breaking for callers that send/read the fraction):
  • createTaxRate / updateTaxRate input rate now expects a whole percent (7 for 7%), not 0.07.
  • TaxRate.rate in responses now returns the whole percent; TaxRate.ratePercent is unchanged in meaning and now equals rate.
  • Item-picker and invoicing surfaces are unaffected — they already consume ratePercent / the per-line percent snapshot.

Versioning notes

  • Bumped with uv run python scripts/bump_version.py 2.6.0 --changelog-stub.
  • Deploy order: ship the migration (it converts every tenant automatically); optionally dry-run scripts/backfill_tax_rate_percent.py first to preview the change. Frontend callers of the taxRates write/read path must switch to the percent convention in lockstep.