For developers who need to extend behaviour beyond what data and settings can express, see the companion post Customizing Sales Tax Calculation in ERPNext.
The Big Picture
ERPNext picks the right tax in two stages:
- Resolution — for a given customer, address, item, and date, ERPNext decides which tax template applies and which per-item rate overrides kick in.
- Calculation — once the tax rows and per-item rates are stamped onto the document, the engine applies the formulas.
Five DocTypes drive resolution:
| DocType | Purpose |
|---|---|
| Tax Category | A label that groups customers, addresses, items, and templates by jurisdiction or scenario (e.g. “Domestic”, “EU B2B”, “Export”). |
| Sales Taxes and Charges Template | The reusable header-level tax table that gets copied onto a Sales Invoice, Sales Order, Quotation, or Delivery Note. |
| Item Tax Template | A per-item rate map that overrides individual rates from the header template (e.g. reduced VAT on books). |
| Tax Rule | A routing rule that maps (party, address, item, date) → template. |
| Accounts Settings | Global switches that change resolution behaviour. |
Recommended Setup Order
- Create your Tax Category records first.
- Build the GL Account records you need (under “Duties and Taxes” or “Tax Assets”).
- Create one or more Sales Taxes and Charges Template per scenario.
- Create Item Tax Template records for special-rate items (reduced VAT, exempt, zero-rated).
- Attach Item Tax Template rows to Item or Item Group masters.
- Optionally create Tax Rule records for automatic routing.
- Tune the global behaviour in Accounts Settings.
1. Tax Category
Open Tax Category and create one record per tax scenario you need to distinguish.
Typical entries for a German company:
- “Inland” (domestic sales)
- “EU B2B” (intra-community supply)
- “Export” (third-country export)
Fields:
- Title (
title) — the user-facing name, e.g. “EU B2B”. - Disabled (
disabled) — leave unchecked.
Tax Category is a pure tag with no logic of its own. It propagates from Customer → Address → document → tax routing.
2. Sales Taxes and Charges Template
This is the header-level table that gets copied onto a Sales Invoice.
Header fields
- Title (
title) — descriptive name, e.g. “Inland 19% — My Co”. - Company (
company) — the company this template belongs to. - Tax Category (
tax_category) — link to the Tax Category this template serves. Only one enabled template per(company, tax_category)is allowed. - Is Default (
is_default) — check exactly one template per company as the fallback when no Tax Rule matches. Saving with this checked clears the flag on every other template for the same company. - Disabled (
disabled) — mutually exclusive with Is Default.
Tax row fields (the Taxes child table)
Each row in Taxes (taxes) represents one tax line that will appear on the invoice.
- Type (
charge_type) — pick one of:- “On Net Total” — percentage VAT/GST. Use this for almost all standard tax rows.
- “On Previous Row Amount” — percentage applied to a prior tax row’s amount (surcharge on a surcharge).
- “On Previous Row Total” — percentage applied to net + all prior tax rows (compound tax, e.g. Canadian PST on top of GST).
- “Actual” — a fixed amount distributed proportionally across items.
- “On Item Quantity” — per-unit excise/duty.
- Account Head (
account_head) — the GL Account to post to. Must have Account Type set to “Tax”. - Rate (
rate) — the percentage (e.g. 19 for 19 %), or per-unit amount for “On Item Quantity”, or fixed sum for “Actual”. - Description (
description) — what appears on the printed invoice, e.g. “USt. 19%”. - Reference Row # (
row_id) — only for “On Previous Row Amount” / “On Previous Row Total”; the 1-based row number to reference. - Considered Tax Included in Basic Rate (
included_in_print_rate) — check when item prices are gross (tax-inclusive). The engine then back-extracts the net. Only the first tax row in a contiguous sequence may be inclusive. - Add or Deduct (
add_deduct_tax) — “Add” or “Deduct”. Mostly relevant on Purchase Taxes and Charges Templates for reverse-charge / innergemeinschaftlicher Erwerb scenarios, where you pair an “Add” row on the output VAT account with a “Deduct” row on the input VAT account at the same rate; the GL postings happen but the net P&L impact is zero. Rarely useful on the sales side — see the EU intra-community pattern below. - Consider Tax or Charge for (
category) — “Total”, “Valuation”, or “Valuation and Total”. For sales documents leave on “Total”. - Cost Center (
cost_center) — the cost center that receives the GL allocation.
Common patterns
Domestic 19 % VAT (Germany): one row, Type “On Net Total”, Rate “19”, Account Head “Umsatzsteuer 19 % - My Co”.
Tax-inclusive prices: same row, plus Considered Tax Included in Basic Rate checked.
EU intra-community supply — innergemeinschaftliche Lieferung (sales side): no tax rows at all. The supply is tax-free under §4 Nr. 1b UStG; the recipient self-accounts VAT in their own country. The invoice carries both VAT IDs and a reverse-charge note (“Steuerschuldnerschaft des Leistungsempfängers”) in the print format — not in the tax math. Optionally add a single 0 % row pointing at a “Tax-free EU sale” account head if your accountant prefers a visible reporting line.
Reverse-charge paired Add/Deduct rows belong on a Purchase Taxes and Charges Template for innergemeinschaftlicher Erwerb, where the buyer self-accounts VAT: one row “Add” on the output VAT account and one row “Deduct” on the input VAT account, same rate. Net P&L impact zero, GL audit trail preserved. This pattern does not apply to a Sales Taxes and Charges Template.
Compound tax (Canada GST + PST): row 1 GST “On Net Total” 5 %, row 2 PST “On Previous Row Total” 7 % with Reference Row # “1”.
3. Item Tax Template
Use this when some items inside an invoice need a different rate than the header template provides — e.g. books at reduced VAT, exempt services, zero-rated exports.
Header fields
- Title (
title) — e.g. “Reduced 7%” or “Exempt”. - Company (
company) — owning company. - Disabled (
disabled).
Tax row fields (the Taxes child table)
Each row maps a single GL Account to a rate:
- Tax Type (
tax_type) — link to a tax Account. Must match an Account Head that appears in the Sales Taxes and Charges Template. - Tax Rate (
tax_rate) — the rate to use for this item, e.g. “7” or “0”. - Is Not Applicable (
not_applicable) — check if this account head should be entirely skipped for the item (different from rate “0”, which still posts a zero-amount line).
How it overrides the header template
The header template defines which tax accounts exist on the invoice. The Item Tax Template defines which rate each account uses for the matched item. So if a German invoice has both “USt. 19 %” and “USt. 7 %” rows in the header template, an item bound to Item Tax Template “Ermäßigt 7 %” applies rate 0 to the 19 % row and rate 7 to the 7 % row.
Attaching to items
Open the Item master and scroll to the Item Tax table (or do the same on Item Group to apply to a whole group). Each row holds:
- Item Tax Template (
item_tax_template) — which template to apply. - Tax Category (
tax_category) — which Tax Category this binding is for. Leave empty to match any. - Valid From (
valid_from) — optional start date. - Minimum Net Rate (
minimum_net_rate) — optional price-band lower bound. - Maximum Net Rate (
maximum_net_rate) — optional price-band upper bound.
ERPNext walks the Item’s table first, then the Item Group hierarchy, picking the most specific row whose validity window and price band match.
4. Tax Rule
A Tax Rule automatically picks the right Sales Taxes and Charges Template when a Customer and Address are entered. Without rules, ERPNext falls back to the company default template.
Header fields
- Tax Type (
tax_type) — “Sales” or “Purchase”. - Sales Tax Template (
sales_tax_template) — for sales rules. - Purchase Tax Template (
purchase_tax_template) — for purchase rules. - Tax Category (
tax_category) — must match exactly. Leave empty to match documents without a tax category. - Use for Shopping Cart (
use_for_shopping_cart) — controls whether this rule applies to webshop checkout. Default checked. - Priority (
priority) — tie-breaker when multiple rules match equally specifically. Lower number wins. - Valid From (
from_date) and Valid Upto (to_date) — date window. - Company (
company) — restrict to one company.
Filter fields
You can scope the rule to any combination of:
- Customer (
customer), Customer Group (customer_group) - Supplier (
supplier), Supplier Group (supplier_group) (purchase rules only) - Item (
item), Item Group (item_group) - Billing address: Billing City (
billing_city), Billing County (billing_county), Billing State (billing_state), Billing Country (billing_country), Billing Zipcode (billing_zipcode) - Shipping address: Shipping City (
shipping_city), Shipping County (shipping_county), Shipping State (shipping_state), Shipping Country (shipping_country), Shipping Zipcode (shipping_zipcode)
How matching works
ERPNext picks the rule with the most populated filter fields that match the document. Specificity always beats Priority. Priority only breaks ties between rules with the same number of matching filters.
Two enabled rules with the same priority, identical filter values, and overlapping date ranges produce a ConflictingTaxRule error on save.
Practical examples
EU intra-community sale:
- Tax Type “Sales”
- Tax Category “EU B2B”
- Billing Country left empty (the Tax Category on the Address is what carries the routing)
- Sales Tax Template “EU Intra-Community — My Co”
Domestic invoice catch-all:
- Tax Type “Sales”
- Tax Category “Inland”
- Sales Tax Template “Inland 19% — My Co”
- Priority “10”
Specific large customer overrides everything:
- Tax Type “Sales”
- Customer “Acme GmbH”
- Tax Category “Inland”
- Sales Tax Template “Inland 19% — Acme — My Co”
- Priority “1”
5. Accounts Settings
Open Accounts Settings for the global switches that affect tax behaviour.
- Determine Address Tax Category From (
determine_address_tax_category_from) — “Billing Address” (default) or “Shipping Address”. Decides which Address the document inherits its Tax Category from when both are present. Important for shipped goods where the tax follows the destination. - Automatically Add Taxes and Charges from Item Tax Template (
add_taxes_from_item_tax_template) — default on. When an item references a tax account that isn’t yet in the document’s Taxes table, ERPNext auto-appends a row so the rate from the Item Tax Template can be applied. - Automatically Add Taxes and Charges from Master (
add_taxes_from_taxes_and_charges_template) — default off. When on, an empty taxes table is auto-populated from the resolved template. Mutually exclusive with the above setting. - Round Tax Amount Row-wise (
round_row_wise_tax) — default off. When on, each per-item tax contribution is rounded before being summed; useful where local rules require rounded line items but may cause sub-cent drift on totals. - Book Tax Loss on Early Payment Discount (
book_tax_discount_loss) — splits early-payment discount loss into income vs tax components. Tick if your jurisdiction requires the VAT portion of an early-payment discount to be reversed separately.
Also set on the Company record:
- Cost Center (
cost_center) — used as the default Cost Center on auto-appended tax rows. - Round Off Account (
round_off_account) and Round Off Cost Center (round_off_cost_center) — destination for the rounding adjustment GL entry. - Default Currency (
default_currency) — anchors all base-currency conversions.
How They Work Together
When a user creates a Sales Invoice:
- The user picks the Customer. ERPNext reads the Customer’s Tax Category, then checks the chosen Address (per Determine Address Tax Category From) and lets the Address’ Tax Category override the customer’s.
- ERPNext searches Tax Rule records for the most specific match given the customer, addresses, Tax Category, item group, and posting date. The winning rule’s Sales Tax Template is copied onto the invoice’s Sales Taxes and Charges Template field, and its Taxes rows are copied into the document.
- If no rule matches, ERPNext falls back to the company default template (the one with Is Default checked).
- For each item the user adds, ERPNext walks the Item’s Item Tax table (then the Item Group hierarchy) to find the matching Item Tax Template, filtered by the document’s Tax Category, the posting date, and the price band.
- The matched Item Tax Template rates are stamped onto the item row.
- On save, the calculation engine applies each tax row’s Type formula to each item, honouring Considered Tax Included in Basic Rate, Add or Deduct, and rounding settings.
Worked example: Reduced VAT on a specific item
Goal: customer “Acme GmbH” with Tax Category “Inland” should pay 7 % VAT on item “Reduced-Rate Widget” (reduced_rate_widget) instead of the standard 19 %.
This is a pure-data problem — no code, no scripts, no overrides:
- Confirm the standard Sales Taxes and Charges Template “Inland 19% — My Co” already includes a row for both Umsatzsteuer 19 % and Umsatzsteuer 7 %. If not, add the 7 % row with Type “On Net Total” and Rate “7”.
- Create an Item Tax Template with Title “DE Reduced 7%” and two rows in its Taxes child table:
- Tax Type “Umsatzsteuer 19 %”, Is Not Applicable checked.
- Tax Type “Umsatzsteuer 7 %”, Tax Rate “7”.
- Open the Item “reduced_rate_widget” and add an Item Tax row with Item Tax Template “DE Reduced 7%” and Tax Category “Inland”.
When you create a Sales Invoice for “Acme GmbH” with this item, ERPNext applies the standard “Inland 19% — My Co” template at the header level, then overrides per-item: the 19 % row is skipped entirely for this line, and the 7 % row uses rate 7. The breakdown shows Umsatzsteuer 7 % populated and Umsatzsteuer 19 % untouched by this item — exactly the behaviour you wanted, with no code written.
If you also need “Acme GmbH” to automatically default to a different header template than other customers, add a Tax Rule with Customer “Acme GmbH” and the alternative Sales Tax Template. Customer specificity beats the company-default fallback.
How the override actually works
Think of the Item Tax Template as a partial mask over the header template’s rates:
- The header template defines which tax accounts appear on the invoice and the default rate for each.
- For each item line, ERPNext overlays the matched Item Tax Template on top: any account head listed in the template uses the template’s rate; any account head not listed falls through to the header rate.
Without an entry for Umsatzsteuer 19 % in the Item Tax Template, the 19 % header rate would still apply to the line and you’d get both 19 % and 7 % VAT on this item. So you need a row that suppresses the default — and you have two ways to do it, with different reporting consequences:
| Approach | Engine behaviour | Tax breakup | 19 % taxable base |
|---|---|---|---|
| Is Not Applicable checked | Short-circuits the row for this item; no posting at all under that account head | No entry for the item under Umsatzsteuer 19 % | Item is excluded from the 19 % row’s net total |
| Tax Rate “0” | Runs the formula with rate 0 | A zero-amount entry under Umsatzsteuer 19 % | Item is included in the 19 % row’s net total |
For a reduced-rate or exempt item, Is Not Applicable is almost always the right choice: the 19 % tax simply does not apply, and you don’t want VAT reports or per-account net totals to count this revenue under the 19 % bucket. Reach for Tax Rate “0” only when the item really is taxable under that account but the rate happens to be zero (e.g. zero-rated exports tracked under the same account head as standard sales) — i.e. when the item should still appear in that account’s reporting base.
Two configuration patterns follow from the mask model:
- Header-as-default (used in the example above) — the header carries realistic default rates; Item Tax Templates list only the accounts they want to override or suppress.
- Header-as-skeleton — set every header row to rate “0” and let Item Tax Templates be the sole source of truth. Useful when essentially every item has a template; risky otherwise, because an item without a template silently gets zero tax.
If you enable Automatically Add Taxes and Charges from Item Tax Template (add_taxes_from_item_tax_template) in Accounts Settings, ERPNext also auto-appends any account head referenced by an item’s template that is missing from the header — as a row with Type “On Net Total” and Rate “0”, marked set_by_item_tax_template. The actual rate then comes entirely from the item template. This effectively materializes pattern 2 on demand.
Special Scenarios
Inclusive (gross) prices
Tick Considered Tax Included in Basic Rate on the relevant tax row in the Sales Taxes and Charges Template. Item prices entered on the invoice are then treated as gross. Only the first tax row in a sequence may be inclusive.
EU intra-community supply (Germany / France / etc.)
Two distinct flows that are often confused — handle them separately.
Sales side — innergemeinschaftliche Lieferung (you ship to an EU B2B customer):
- Create a dedicated Tax Category “EU B2B”.
- Build a Sales Taxes and Charges Template with no tax rows (or a single 0 % row pointing at a “Tax-free EU sale” account head for reporting hygiene).
- Create a Tax Rule that maps Tax Category “EU B2B” to this template.
- Tag EU customer Address records with Tax Category “EU B2B”.
- Add the reverse-charge note to the print format and ensure both VAT IDs are visible.
The Zusammenfassende Meldung (EC Sales List) is a separate reporting concern: it filters on Tax Category + customer VAT ID, not on tax rows.
Purchase side — innergemeinschaftlicher Erwerb (you receive from an EU supplier): build a Purchase Taxes and Charges Template with paired Add or Deduct rows on the same rate so the self-accounted VAT posts to both output and input VAT accounts and the grand total does not change. This is the only place the paired-row pattern applies.
Reduced rate items (books, food, medical)
Create an Item Tax Template “Reduced” containing the reduced-rate account at the lower rate and the standard-rate account at “0”. Attach this template to the relevant Item or Item Group records.
Tax-exempt items
Use Is Not Applicable on every account-head row inside the Item Tax Template so those tax rows are skipped entirely for the item.
Country-mandated integer tax amounts (India, etc.)
Configure a Round Off Tax Account record on the company so the engine rounds those tax accounts to integer values.
Recurring invoices via Subscription
Subscriptions snapshot the Sales Taxes and Charges Template when they are created. Changes to Tax Rule or default templates do not propagate to existing subscriptions — open the Subscription record and update Sales Tax Template manually if rates change.
Validation Errors and What They Mean
- “Conflicting Tax Rule” — two Tax Rule records have the same priority, the same filters, and overlapping date ranges. Adjust Priority or tighten one of the filter fields.
- “Only one default Sales Taxes and Charges Template per company allowed” — uncheck Is Default on the older template before saving the new one.
- “Cannot enable both Disabled and Is Default” — they are mutually exclusive.
- “First Row Type cannot be ‘On Previous Row Amount/Total’” — the first row of Taxes has nothing to reference; switch its Type to “On Net Total” or “Actual”.
- “Inclusive tax must be in the first row” — uncheck Considered Tax Included in Basic Rate on later rows or move the row to the top.
Quick Checklist
Before going live with sales tax in a new ERPNext company:
- All required GL Account records exist with Account Type “Tax”.
- Tax Category records cover every distinct tax scenario.
- At least one Sales Taxes and Charges Template has Is Default checked.
- Reduced-rate / exempt items have Item Tax Template rows attached at the Item or Item Group level.
- Tax Rule records cover the routing scenarios that cannot be expressed as company defaults.
- Accounts Settings Determine Address Tax Category From matches your jurisdiction’s place-of-supply rule.
- Company has Round Off Account and Round Off Cost Center set.
- A test Sales Invoice for each scenario produces the expected Taxes table and grand total.