Skip to content

Tax Rate Documentation

Overview

The Tax Rate module (account.tax.rate) manages tax configurations for sales and purchases, supporting complex multi-component tax structures including VAT/SST, withholding tax (WHT), deferred VAT, and tax exemptions. Tax rates are built from individual tax components, allowing flexible and accurate tax calculations for various business scenarios and compliance requirements.


Model Information

Model Name: account.tax.rate Display Name: Tax Rate Name Field: name Key Fields: name (tax rate name must be unique)

Features

  • ✅ Audit logging enabled
  • ✅ Multi-component tax support
  • ✅ VAT/SST calculation
  • ✅ Withholding tax (WHT) support
  • ✅ Deferred and pending VAT handling
  • ✅ Tax-inclusive and tax-exclusive pricing
  • ✅ E-invoicing integration
  • ✅ UUID for external system integration

Understanding Tax Rates

What is a Tax Rate?

A Tax Rate is a named collection of tax components that together define the complete tax treatment for a transaction. Examples: - "VAT 7%" - Standard VAT - "VAT 0%" - Zero-rated VAT - "WHT 3%" - Withholding tax - "VAT 7% + WHT 3%" - Combined taxes

Tax Components

Tax rates are composed of one or more components (account.tax.component), each representing: - A specific tax type (VAT, WHT, exempt, deferred) - A tax percentage - GL accounts for posting - Transaction type (sale/purchase)

Example Structure:

Tax Rate: "VAT 7%"
├─ Component 1: "SST" (type: vat, rate: 7%)

Tax Rate: "VAT 7% + WHT 3%"
├─ Component 1: "SST" (type: vat, rate: 7%)
└─ Component 2: "WHT" (type: wht, rate: 3%)

Tax Types

Type Description Usage
vat Standard VAT/SST Normal taxable sales/purchases
vat_exempt VAT Exempt Exempt goods/services
vat_defer Deferred VAT Cash basis accounting
vat_pending Pending VAT Tax date accounting
wht Withholding Tax Income tax withholding

Tax Computation Timing

The when parameter controls when taxes are computed:

When Description Use Case
invoice Invoice creation Standard VAT on invoice
invoice_tax_date Tax date reached Pending VAT recognition
invoice_payment Payment received Deferred VAT on cash basis
invoice_payment_inv Reverse deferred VAT Clear deferred account
invoice_payment_pmt Payment VAT Recognize deferred VAT
direct_payment Direct payment Payment without invoice
payment Payment withholding WHT deduction

Understanding Key Fields

What are Key Fields?

For the account.tax.rate model, the key field is:

_key = ["name"]

This means the tax rate name must be unique.

Examples:

"VAT 7%"        Valid
"VAT 0%"        Valid
"VAT 7%"        ERROR: Tax rate name already exists!


Key Fields Reference

Core Fields

Field Type Required Description
name Char Unique tax rate name (e.g., "VAT 7%")
code Char Short code for reporting
uuid Char UUID for external system integration (auto-generated)
active Boolean Active status (default: True)
instructions Text Payment instructions for tax payments

Computed Fields

Field Type Description
rate Decimal Total VAT/SST rate (sum of VAT components)
wht_rate Decimal Total withholding tax rate (sum of WHT components)

Relationship Fields

Field Type Description
components One2Many Tax components (account.tax.component)
comments One2Many Comments and notes
logs One2Many Audit log entries

API Methods

1. Create Tax Rate

Method: create(vals, context)

Creates a new tax rate with components.

Parameters:

vals = {
    "name": "VAT 7%",              # Required, unique
    "code": "VAT7",                # Optional
    "active": True,                # Default: True
    "components": [                # Tax components
        ("create", {
            "name": "SST",
            "type": "vat",
            "rate": 7.0,
            "account_id": vat_account_id,
            "trans_type": "out"
        })
    ]
}

Returns: int - New tax rate ID

Example:

# Create standard VAT rate
vat_rate_id = get_model("account.tax.rate").create({
    "name": "VAT 7%",
    "code": "VAT7",
    "components": [
        ("create", {
            "name": "Standard SST",
            "type": "vat",
            "rate": 7.0,
            "account_id": vat_output_account_id,
            "trans_type": "out"
        })
    ]
})

# Create combined VAT + WHT rate
combined_id = get_model("account.tax.rate").create({
    "name": "VAT 7% + WHT 3%",
    "code": "VAT7WHT3",
    "components": [
        ("create", {
            "name": "SST",
            "type": "vat",
            "rate": 7.0,
            "account_id": vat_output_account_id,
            "trans_type": "out"
        }),
        ("create", {
            "name": "WHT",
            "type": "wht",
            "rate": 3.0,
            "account_id": wht_account_id,
            "trans_type": "out"
        })
    ]
})


2. Get Rate (Computed)

Method: get_rate(ids, context={})

Computes total VAT and WHT rates from components.

Parameters: - ids (list): Tax rate IDs - context (dict): Optional context

Returns: dict - Rates for each tax rate

{
    tax_rate_id: {
        "rate": 7.0,      # Total VAT rate
        "wht_rate": 3.0   # Total WHT rate
    }
}

Example:

tax_rate = get_model("account.tax.rate").browse([tax_rate_id])[0]
print(f"VAT Rate: {tax_rate.rate}%")
print(f"WHT Rate: {tax_rate.wht_rate}%")


3. Compute Base Amount

Method: compute_base(tax_id, amt, tax_type="tax_ex")

Calculates base amount from total amount based on tax treatment.

Parameters: - tax_id (int or BrowseRecord): Tax rate ID or object - amt (Decimal): Total amount - tax_type (str): "tax_ex" (exclusive), "tax_in" (inclusive), or "no_tax"

Returns: Decimal - Base amount before tax

Formula:

# Tax exclusive (default)
base = amount  # Tax not included in amount

# Tax inclusive
base = amount / (1 + rate/100)  # Extract base from tax-inclusive amount

# No tax
base = amount  # No tax adjustment

Example:

# Tax-exclusive: $100 + 7% = $107
base = get_model("account.tax.rate").compute_base(
    tax_id=vat_7_id,
    amt=100.00,
    tax_type="tax_ex"
)
print(base)  # Output: 100.00

# Tax-inclusive: $107 includes 7% tax
base = get_model("account.tax.rate").compute_base(
    tax_id=vat_7_id,
    amt=107.00,
    tax_type="tax_in"
)
print(base)  # Output: 100.00


4. Compute Taxes

Method: compute_taxes(tax_id, base, when="invoice")

Computes tax amounts for all components based on timing.

Parameters: - tax_id (int or BrowseRecord): Tax rate ID or object - base (Decimal): Base amount (before tax) - when (str): When to compute taxes (see Tax Computation Timing table)

Returns: dict - Tax amount for each component

{
    component_id: tax_amount,
    ...
}

Example:

# Compute invoice VAT
taxes = get_model("account.tax.rate").compute_taxes(
    tax_id=vat_rate_id,
    base=100.00,
    when="invoice"
)

for comp_id, tax_amt in taxes.items():
    comp = get_model("account.tax.component").browse([comp_id])[0]
    print(f"{comp.name}: ${tax_amt:.2f}")
# Output: Standard SST: $7.00

# Compute withholding tax on payment
wht_taxes = get_model("account.tax.rate").compute_taxes(
    tax_id=wht_rate_id,
    base=100.00,
    when="payment"
)
# Only returns WHT components


5. Has Deferred VAT

Method: has_defer_vat(ids, context={})

Checks if tax rate has deferred VAT component.

Parameters: - ids (list): Tax rate IDs - context (dict): Optional context

Returns: bool - True if has deferred VAT

Example:

if get_model("account.tax.rate").has_defer_vat([tax_rate_id]):
    print("This tax uses cash basis accounting")
    # Apply different accounting treatment


6. Update Total (UI Event)

Method: update_total(context={})

Recalculates total rate when components change in UI.

Behavior: - Sums rates from all components - Updates in real-time during data entry


Search Functions

Find Tax Rates by Name

# Find VAT 7% rate
tax_rates = get_model("account.tax.rate").search_browse([
    ["name", "=", "VAT 7%"]
])

if tax_rates:
    rate = tax_rates[0]
    print(f"{rate.name}: {rate.rate}% VAT, {rate.wht_rate}% WHT")

Find Active Tax Rates

# Get all active tax rates
active_rates = get_model("account.tax.rate").search_browse([
    ["active", "=", True]
], order="name")

print("Active Tax Rates:")
for rate in active_rates:
    print(f"  {rate.name}: {rate.rate}%")

Find Rates with WHT

# Find rates that include withholding tax
wht_rates = get_model("account.tax.rate").search_browse([
    ["components.type", "=", "wht"]
])

for rate in wht_rates:
    print(f"{rate.name}: WHT {rate.wht_rate}%")

Search by Code

# Find rate by code
rates = get_model("account.tax.rate").search_browse([
    ["code", "=", "VAT7"]
])

Best Practices

1. Use Descriptive Names

# Good: Clear and descriptive
name = "VAT 7% (Standard Rate)"
name = "VAT 0% (Zero-Rated)"
name = "VAT Exempt"

# Less clear: Ambiguous
name = "Tax 1"
name = "Standard"

2. Organize by Transaction Type

# Create separate rates for sales and purchases
sales_vat = get_model("account.tax.rate").create({
    "name": "Output VAT 7%",
    "components": [
        ("create", {
            "name": "SST Output",
            "type": "vat",
            "rate": 7.0,
            "account_id": vat_output_account_id,
            "trans_type": "out"  # For sales
        })
    ]
})

purchase_vat = get_model("account.tax.rate").create({
    "name": "Input VAT 7%",
    "components": [
        ("create", {
            "name": "SST Input",
            "type": "vat",
            "rate": 7.0,
            "account_id": vat_input_account_id,
            "trans_type": "in"  # For purchases
        })
    ]
})

3. Use Correct Tax Type

# Invoice creation - use "invoice"
taxes = get_model("account.tax.rate").compute_taxes(
    tax_id,
    base=1000.00,
    when="invoice"
)

# Payment withholding - use "payment"
wht = get_model("account.tax.rate").compute_taxes(
    tax_id,
    base=1000.00,
    when="payment"
)

4. Handle Tax-Inclusive Pricing

# Customer sees $107 (tax-inclusive)
total_amount = 107.00

# Extract base and tax
base = get_model("account.tax.rate").compute_base(
    vat_7_id,
    total_amount,
    tax_type="tax_in"
)

taxes = get_model("account.tax.rate").compute_taxes(
    vat_7_id,
    base,
    when="invoice"
)

print(f"Total: ${total_amount:.2f}")
print(f"Base: ${base:.2f}")
print(f"Tax: ${sum(taxes.values()):.2f}")
# Output:
# Total: $107.00
# Base: $100.00
# Tax: $7.00

Database Constraints

Unique Tax Rate Name

-- Enforced by _key field
UNIQUE (name)

Implications: - Cannot create duplicate tax rate names - Use descriptive, unique names


Model Relationship Description
account.tax.component One2Many Individual tax components
account.invoice.line Referenced Invoice lines use tax rates
account.invoice.tax Referenced Computed invoice taxes
product Referenced Products have default tax rates

Common Use Cases

Use Case 1: Setup Standard VAT System

# 1. Create VAT output account
vat_output_id = get_model("account.account").create({
    "code": "2310",
    "name": "VAT Output",
    "type": "payable"
})

# 2. Create VAT input account
vat_input_id = get_model("account.account").create({
    "code": "1410",
    "name": "VAT Input",
    "type": "receivable"
})

# 3. Create output VAT 7% (for sales)
get_model("account.tax.rate").create({
    "name": "Output VAT 7%",
    "code": "OVAT7",
    "components": [
        ("create", {
            "name": "SST Output",
            "type": "vat",
            "rate": 7.0,
            "account_id": vat_output_id,
            "trans_type": "out"
        })
    ]
})

# 4. Create input VAT 7% (for purchases)
get_model("account.tax.rate").create({
    "name": "Input VAT 7%",
    "code": "IVAT7",
    "components": [
        ("create", {
            "name": "SST Input",
            "type": "vat",
            "rate": 7.0,
            "account_id": vat_input_id,
            "trans_type": "in"
        })
    ]
})

# 5. Create zero-rated
get_model("account.tax.rate").create({
    "name": "VAT 0% (Zero-Rated)",
    "code": "VAT0",
    "components": [
        ("create", {
            "name": "Zero-Rated SST",
            "type": "vat",
            "rate": 0.0,
            "account_id": vat_output_id,
            "trans_type": "out"
        })
    ]
})

# 6. Create exempt
get_model("account.tax.rate").create({
    "name": "VAT Exempt",
    "code": "EXEMPT",
    "components": [
        ("create", {
            "name": "SST Exempt",
            "type": "vat_exempt",
            "rate": 0.0,
            "trans_type": "out"
        })
    ]
})

print("✓ VAT system configured")

Use Case 2: Calculate Invoice Tax

# Calculate tax for invoice line

line_total = 1000.00  # Line total
tax_type = "tax_ex"   # Tax exclusive pricing
tax_rate_id = vat_7_id

# 1. Compute base amount
base = get_model("account.tax.rate").compute_base(
    tax_rate_id,
    line_total,
    tax_type
)

# 2. Compute taxes
taxes = get_model("account.tax.rate").compute_taxes(
    tax_rate_id,
    base,
    when="invoice"
)

# 3. Calculate totals
tax_total = sum(taxes.values())
grand_total = base + tax_total

print(f"Base Amount: ${base:.2f}")
print(f"Tax Amount: ${tax_total:.2f}")
print(f"Total: ${grand_total:.2f}")
# Output:
# Base Amount: $1000.00
# Tax Amount: $70.00
# Total: $1070.00

Use Case 3: Handle Withholding Tax

# Setup WHT for service payments

# 1. Create WHT account
wht_account_id = get_model("account.account").create({
    "code": "1420",
    "name": "WHT Receivable",
    "type": "receivable"
})

# 2. Create WHT 3% rate
wht_rate_id = get_model("account.tax.rate").create({
    "name": "WHT 3%",
    "code": "WHT3",
    "components": [
        ("create", {
            "name": "Withholding Tax 3%",
            "type": "wht",
            "rate": 3.0,
            "account_id": wht_account_id,
            "trans_type": "out",
            "contact_type": "company"
        })
    ]
})

# 3. Calculate WHT on payment
payment_amount = 10000.00

wht_taxes = get_model("account.tax.rate").compute_taxes(
    wht_rate_id,
    payment_amount,
    when="payment"
)

wht_amount = sum(wht_taxes.values())
net_payment = payment_amount + wht_amount  # WHT is negative

print(f"Gross Amount: ${payment_amount:.2f}")
print(f"WHT Withheld: ${abs(wht_amount):.2f}")
print(f"Net Payment: ${net_payment:.2f}")
# Output:
# Gross Amount: $10000.00
# WHT Withheld: $300.00
# Net Payment: $9700.00

Use Case 4: Deferred VAT (Cash Basis)

# Setup deferred VAT for cash basis accounting

# Create deferred VAT account
defer_vat_id = get_model("account.account").create({
    "code": "1411",
    "name": "Deferred VAT",
    "type": "receivable"
})

# Create deferred VAT rate
defer_rate_id = get_model("account.tax.rate").create({
    "name": "VAT 7% (Deferred)",
    "code": "DVAT7",
    "components": [
        ("create", {
            "name": "Deferred SST",
            "type": "vat_defer",
            "rate": 7.0,
            "account_id": defer_vat_id,
            "trans_type": "out"
        })
    ]
})

# Invoice creation - deferred
base = 1000.00
invoice_taxes = get_model("account.tax.rate").compute_taxes(
    defer_rate_id,
    base,
    when="invoice"
)
print(f"At invoice: ${sum(invoice_taxes.values()):.2f}")  # $70 to deferred

# Payment receipt - recognize VAT
payment_taxes = get_model("account.tax.rate").compute_taxes(
    defer_rate_id,
    base,
    when="invoice_payment"
)
print(f"At payment: ${sum(payment_taxes.values()):.2f}")  # Recognize VAT

Use Case 5: Tax Rate Report

# Generate tax rate configuration report

def generate_tax_report():
    rates = get_model("account.tax.rate").search_browse([
        ["active", "=", True]
    ], order="name")

    print("TAX RATE CONFIGURATION REPORT")
    print("=" * 80)

    for rate in rates:
        print(f"\n{rate.name} ({rate.code or 'N/A'})")
        print(f"  Total VAT Rate: {rate.rate}%")
        if rate.wht_rate:
            print(f"  Total WHT Rate: {rate.wht_rate}%")

        print(f"  Components:")
        for comp in rate.components:
            print(f"    - {comp.name}")
            print(f"      Type: {comp.type}")
            print(f"      Rate: {comp.rate}%")
            print(f"      Account: {comp.account_id.code} - {comp.account_id.name}")
            print(f"      Trans Type: {comp.trans_type or 'N/A'}")

generate_tax_report()

Performance Tips

1. Use Browse Cache

# Good: Pass BrowseRecord for speed
tax_rate = get_model("account.tax.rate").browse([tax_rate_id])[0]

for i in range(1000):
    base = get_model("account.tax.rate").compute_base(
        tax_rate,  # BrowseRecord (uses cache)
        100.00
    )

# Slower: Pass ID (looks up each time)
for i in range(1000):
    base = get_model("account.tax.rate").compute_base(
        tax_rate_id,  # int (no cache)
        100.00
    )

2. Index on Common Fields

CREATE INDEX idx_tax_rate_name ON account_tax_rate(name);
CREATE INDEX idx_tax_rate_code ON account_tax_rate(code);
CREATE INDEX idx_tax_rate_active ON account_tax_rate(active);

Troubleshooting

"Tax rate name already exists"

Cause: Duplicate tax rate name Solution: Use unique names or update existing rate

"Tax calculation returns 0"

Cause: No components or wrong tax type Solution: Verify components exist and use correct when parameter

"WHT not being calculated"

Cause: Using when="invoice" instead of when="payment" Solution: Use correct timing:

# Wrong
taxes = compute_taxes(wht_rate_id, 1000, when="invoice")  # Returns {}

# Correct
taxes = compute_taxes(wht_rate_id, 1000, when="payment")  # Returns WHT

"Deferred VAT not working"

Cause: Using wrong when value Solution: Use proper sequence:

# Invoice - to deferred account
invoice_taxes = compute_taxes(rate_id, base, when="invoice")

# Payment - reverse deferred, recognize VAT
payment_taxes = compute_taxes(rate_id, base, when="invoice_payment")


Version History

Last Updated: 2025-12-16 Model Version: account_tax_rate.py Framework: Netforce


Additional Resources

  • Tax Component Documentation: account.tax.component
  • Invoice Tax Documentation: account.invoice.tax
  • Invoice Line Documentation: account.invoice.line
  • E-Invoice Tax Type Documentation: account.einvoice.taxtype

This documentation is generated for developer onboarding and reference purposes.