Skip to main content
Odoo Community Association

US Sales Tax Engine

Alpha License: LGPL-3 OCA/l10n-usa Translate me on Weblate Try me on Runboat

Hybrid US Sales Tax engine for Odoo: resolves the combined state + county + city + district rate for a ZIP code, applies it to sale orders and invoices, and keeps a full audit trail of every calculation.

Resolution order: local rate database first, external API providers as fallback (configured independently in their own l10n_us_sales_tax_provider_* modules — this module never imports their code directly). Tracks nexus per state, taxability per product category, and caches external API responses to control call volume.

Table of contents

Usage

Loading local rate data

The local provider needs three things before it can resolve a tax rate for a ZIP code: a Jurisdiction (county/city), a ZIP Mapping pointing that ZIP to the jurisdiction, and a Tax Rate on that jurisdiction. None of these ship pre-loaded — only product categories and the provider registry are seeded on install.

Import Tax Rates wizard

US Sales Tax → Rate Database → Import Rates always requires a file upload — there is no “instant, no file” option in the wizard.

  • Florida DOR: upload the official Master Address List CSV from https://pointmatch.floridarevenue.com/General/AddressFiles.aspx (select a county and effective date, then download). This creates the jurisdiction, the rate, and the ZIP mapping for every row — full ZIP-level resolution.

  • Generic CSV (any state): any file with ZIP, COUNTY/CITY, and a rate column works the same way.

Manual setup (quick test, single ZIP)

For testing a single ZIP without downloading a file, create the three records by hand under US Sales Tax → Rate Database:

  1. Jurisdictions — name, type (county/city), state.

  2. ZIP Mappings — the ZIP code, pointing to that jurisdiction.

  3. Tax Rates — the actual rate values on that jurisdiction.

Nexus is independent of rate data

Creating a Nexus record (US Sales Tax → Configuration → Nexus) only marks that the company has a legal obligation to collect tax in that state — it does not create or require any jurisdiction, ZIP mapping, or rate. Both are needed independently before a sale order calculates a non-zero local tax.

Zero-tax lines always carry an explicit tax, never an empty tax field

When a line ends up with 0% tax, the engine always assigns a real account.tax record rather than leaving the line untaxed:

  • Exempt product category or no nexus in that state — assigned the shared US Sales Tax - Exempt (0%) tax (one tax for both reasons, across all states).

  • A state with a genuine 0% combined rate (e.g. Oregon, Montana — states with no sales tax at all) — assigned US Sales Tax {state} 0%, the same per-state-and-rate tax used for any other rate.

This is deliberate: an empty tax_id is indistinguishable from “tax was never calculated” on an invoice or in a tax report filtered/grouped by account.tax/account.tax.group. An explicit 0% tax record shows that the line was evaluated and a deliberate “no tax due” determination was made — the kind of audit trail a sales-tax-exempt line should leave behind.

The specific reason for a 0% line (which product category, which state, nexus or no nexus) is not encoded in the tax’s name — that level of detail already lives on us.tax.calculation.log, one record per calculation. The tax itself only needs to answer “was this evaluated, and is it taxable” at a glance.

Migration note: if you had ad-hoc reports filtering for lines with no tax_id/tax_ids at all to flag “missing tax” cases, those lines will stop matching once they carry the explicit 0% tax — this is the intended effect of this change, not a regression.

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed feedback.

Do not contact contributors directly about support or help with technical issues.

Credits

Authors

  • Binhex

Contributors

Maintainers

This module is maintained by the OCA.

Odoo Community Association

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

Current maintainer:

crrodrigueztrujillo

This module is part of the OCA/l10n-usa project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

Release files for odoo-addon-l10n-us-sales-tax-engine 18.0.1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for odoo-addon-l10n-us-sales-tax-engine 18.0.1.2.0
File Interpreter ABI Platform
odoo_addon_l10n_us_sales_tax_engine-18.0.1.2.0-py3-none-any.whl Python 3 none any Details

Release files / odoo_addon_l10n_us_sales_tax_engine-18.0.1.2.0-py3-none-any.whl

Download URL odoo_addon_l10n_us_sales_tax_engine-18.0.1.2.0-py3-none-any.whl
Size 3.1 MB
Tags Python 3
SHA-256 checksum
How to use checksums
4a9b80453fa4956c21cb5e9c9357225f0293b770b1a51736d029a412859ed532
BLAKE2b-256 checksum
How to use checksums
c7ad14891960abad61b8e3b75eb93dd6235e4b853047b81e0a7763f696edb94d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.3

Release history Release notifications | RSS feed

This release

18.0.1.2.0 This release

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page