Skip to content

2.0.0

Released: TBD

Highlights

  • Tax-rate GL accounts now drive the books. A tax rate's accounts are no longer inert placeholders — supplier and customer invoices post their ITBMS to the account configured on the rate, and customer invoices with retention now record the ITBMS the client withheld as a recoverable credit.
  • Tax-account naming fixed. The confusing accountPayable / accountReceivable / withholding fields on a tax rate were renamed to say what they are: output tax, input tax, and a directional withholding pair.

Added

  • Sales-side ITBMS retention posting. When a client is a withholding agent (invoice_retention + a codigo_retencion), the customer invoice now nets Accounts Receivable by the retained ITBMS and debits it to the rate's withholding asset account (ITBMS retenido — a recoverable credit). See Tax-Rate GL-Account Wiring.
  • Directional withholding accounts on TaxRate. withholdingPayableAccountId (you withheld as agent → liability) and withholdingAssetAccountId (withheld from you → asset) replace the single withholdingAccountId.
  • Automatic account defaults. Creating a tax rate without explicit accounts now defaults them via get_or_create_account (output/withholding-payable → TAX_PAYABLE; input/withholding-asset → OTHER_CURRENT_ASSET).
  • Accounting Placeholder Audit — a documented sweep of accounting fields/enums/methods that are declared but unconsumed.

Changed

  • Transaction-time ITBMS account sourcing. Customer-invoice and supplier-invoice ledger postings now source the tax line's GL account from the line's tax rate (output_tax_account_id / input_tax_account_id), falling back to client.sales_taxes_account_id / supplier.tax_account_id so lines without a rate still post.
  • Breaking — TaxRate GraphQL fields renamed. accountPayableIdoutputTaxAccountId, accountReceivableIdinputTaxAccountId; withholdingAccountId removed in favor of the pair above. Resolver fields accountPayable / accountReceivable / withholdingoutputTaxAccount / inputTaxAccount / withholdingPayableAccount / withholdingAssetAccount. All four account inputs are now optional on create/update.

Fixed

  • Supplier input-tax account mis-classification. New suppliers seeded their tax account as TAX_PAYABLE (a liability) even though purchase ITBMS is debited as a recoverable asset. It now seeds as OTHER_CURRENT_ASSET. (supplier_invoice_service.py, supplier_tabular_service.py)
  • updateTaxRate no longer nulls configured accounts. Update only overwrites an account when a non-null value is supplied.

Migrations

  • rename_tax_rate_account_columns (down_revision = add_dates_to_items) — renames account_payable_idoutput_tax_account_id, account_receivable_idinput_tax_account_id on tax_rates; drops withholding_account_id; adds withholding_payable_account_id + withholding_asset_account_id (+ FKs). upgrade and downgrade both implemented; offline SQL verified in both directions. Data-preserving rename — no backfill required.

Frontend impact

  • The TaxRate type and TaxRateCreateInput / TaxRateUpdateInput change (breaking):
  • accountPayableIdoutputTaxAccountId, accountReceivableIdinputTaxAccountId
  • withholdingAccountId removedwithholdingPayableAccountId + withholdingAssetAccountId
  • resolver fields → outputTaxAccount / inputTaxAccount / withholdingPayableAccount / withholdingAssetAccount
  • all four account inputs are now optional; the backend defaults them on create, so client-side defaulting can be dropped.

Versioning notes

  • Bumped with uv run python scripts/bump_version.py 2.0.0 --changelog-stub. MAJOR because of the breaking TaxRate GraphQL field rename — deploy the backend and frontend together, and apply the rename_tax_rate_account_columns migration as part of the release.