Skip to content

Tax Component Documentation

Overview

The Tax Component module (account.tax.component) represents individual tax elements that make up a tax rate. Components allow flexible multi-tier tax structures, supporting VAT/SST, withholding tax (WHT), deferred VAT, tax exemptions, and compound taxes. Each component specifies its type, rate, GL accounts, and applicable transaction scenarios.


Model Information

Model Name: account.tax.component Display Name: Tax Component Name Field: name (custom name_get returns "TaxRate - Component") Key Fields: None (auto-increment ID)

Features

  • ✅ Multi-component tax support
  • ✅ VAT/SST types (standard, exempt, deferred, pending)
  • ✅ Withholding tax support
  • ✅ Compound tax calculation
  • ✅ Transaction type filtering (sale/purchase)
  • ✅ Contact type filtering (company/individual)
  • ✅ E-invoicing integration
  • ✅ Cascade delete with parent tax rate

Understanding Tax Components

What is a Tax Component?

A Tax Component is a single tax element within a tax rate. Tax rates can have multiple components to handle complex tax scenarios:

Simple Tax (1 component):

Tax Rate: "VAT 7%"
└─ Component: "Standard SST" (7%)

Combined Tax (2+ components):

Tax Rate: "VAT 7% + WHT 3%"
├─ Component: "SST" (7% VAT)
└─ Component: "WHT" (3% withholding)

Compound Tax:

Tax Rate: "Compound Tax"
├─ Component 1: "Base Tax" (5%)
└─ Component 2: "Additional Tax" (2%, compound)
   Calculated on: Base + Component 1

Component Types

Type Code Description When Applied
vat SST Standard VAT/SST Invoice creation
vat_exempt SST Exempt VAT exempt goods Invoice (0% rate)
vat_defer Deferred SST Cash basis VAT Payment receipt
vat_pending Pending VAT Tax date accounting Tax date reached
wht Withholding Tax Income tax withholding Payment processing

Transaction Types

Type Description Usage
out Sale Transaction Customer invoices, sales
in Purchase Transaction Supplier bills, purchases
NULL Both Applies to all transactions

Contact Types

Type Description Usage
company Company/Business B2B transactions
individual Individual Person B2C transactions
NULL Both Applies to all contacts

Key Fields Reference

Core Component Fields

Field Type Required Description
tax_rate_id Many2One Parent tax rate (cascade delete)
name Char Component name (e.g., "Standard SST")
rate Decimal Tax percentage (default: 0)
type Selection Tax type (vat, vat_exempt, vat_defer, wht)
code Char Component code for reporting
description Text Detailed description

Accounting Fields

Field Type Description
account_id Many2One GL account for posting this component
account_pending_id Many2One Pending account (for deferred/pending VAT)

Tax Behavior Fields

Field Type Description
compound Boolean Is this compound tax (applies to base + previous components)?
trans_type Selection Transaction type: "out" (sale) or "in" (purchase)
contact_type Selection Contact type: "company" or "individual"

Integration Fields

Field Type Description
einvoice_tax_type_id Many2One E-invoice tax type mapping

API Methods

1. Create Tax Component

Method: create(vals, context)

Creates a new tax component (usually within tax rate creation).

Parameters:

vals = {
    "tax_rate_id": tax_rate_id,     # Required
    "name": "Standard SST",          # Required
    "type": "vat",                   # Optional
    "rate": 7.0,                     # Required
    "account_id": account_id,        # Optional
    "trans_type": "out",             # Optional
    "contact_type": "company"        # Optional
}

Returns: int - New component ID

Example:

# Usually created with parent tax rate
tax_rate_id = get_model("account.tax.rate").create({
    "name": "VAT 7%",
    "components": [
        ("create", {
            "name": "Standard SST",
            "type": "vat",
            "rate": 7.0,
            "account_id": vat_account_id,
            "trans_type": "out"
        })
    ]
})

# Create component separately
component_id = get_model("account.tax.component").create({
    "tax_rate_id": tax_rate_id,
    "name": "Additional Tax",
    "type": "vat",
    "rate": 2.0,
    "compound": True,
    "account_id": vat_account_id
})


2. Name Get (Display Name)

Method: name_get(ids, context={})

Returns formatted display name.

Format: "Tax Rate Name - Component Name"

Example:

component = get_model("account.tax.component").browse([component_id])[0]
display_name = component.name_get()[0][1]
print(display_name)
# Output: "VAT 7% - Standard SST"


Search Functions

Find Components by Type

# Find all VAT components
vat_comps = get_model("account.tax.component").search_browse([
    ["type", "=", "vat"]
])

# Find all WHT components
wht_comps = get_model("account.tax.component").search_browse([
    ["type", "=", "wht"]
])

Find Components for Transaction Type

# Find sales tax components
sales_comps = get_model("account.tax.component").search_browse([
    ["trans_type", "=", "out"]
])

# Find purchase tax components
purchase_comps = get_model("account.tax.component").search_browse([
    ["trans_type", "=", "in"]
])

Find Components by Tax Rate

# Get all components for specific tax rate
components = get_model("account.tax.component").search_browse([
    ["tax_rate_id", "=", tax_rate_id]
])

for comp in components:
    print(f"{comp.name}: {comp.rate}% ({comp.type})")

Find Deferred VAT Components

# Find all deferred VAT components
deferred = get_model("account.tax.component").search_browse([
    ["type", "=", "vat_defer"]
])

Best Practices

1. Use Clear Component Names

# Good: Descriptive names
name = "Standard SST (7%)"
name = "Withholding Tax - Services"
name = "Deferred VAT Output"

# Less clear: Ambiguous names
name = "Tax 1"
name = "Component A"

2. Set Appropriate Accounts

# Create component with correct GL accounts
{
    "name": "Output VAT",
    "type": "vat",
    "rate": 7.0,
    "account_id": vat_output_account_id,  # Liability account
    "trans_type": "out"
}

{
    "name": "Input VAT",
    "type": "vat",
    "rate": 7.0,
    "account_id": vat_input_account_id,   # Asset account
    "trans_type": "in"
}

3. Use Transaction Type Filters

# Separate components for sales and purchases
# Sales component
{
    "name": "VAT Output 7%",
    "type": "vat",
    "rate": 7.0,
    "trans_type": "out",  # Only for sales
    "account_id": vat_output_id
}

# Purchase component
{
    "name": "VAT Input 7%",
    "type": "vat",
    "rate": 7.0,
    "trans_type": "in",  # Only for purchases
    "account_id": vat_input_id
}

4. Use Contact Type for WHT

# Different WHT rates for companies vs individuals
{
    "name": "WHT - Company",
    "type": "wht",
    "rate": 3.0,
    "contact_type": "company",
    "account_id": wht_company_id
}

{
    "name": "WHT - Individual",
    "type": "wht",
    "rate": 5.0,
    "contact_type": "individual",
    "account_id": wht_individual_id
}

Database Constraints

Foreign Key Constraints

-- Tax rate reference with cascade delete
FOREIGN KEY (tax_rate_id)
    REFERENCES account_tax_rate(id)
    ON DELETE CASCADE

-- Account references
FOREIGN KEY (account_id)
    REFERENCES account_account(id)

FOREIGN KEY (account_pending_id)
    REFERENCES account_account(id)

Implications: - Deleting tax rate deletes all components - Components cannot exist without parent tax rate


Model Relationship Description
account.tax.rate Many2One Parent tax rate
account.account Many2One GL accounts for posting
account.einvoice.taxtype Many2One E-invoice tax type
account.invoice.tax Referenced Computed invoice taxes

Common Use Cases

Use Case 1: Setup Standard VAT Components

# Create output VAT for sales
output_comp = {
    "name": "Output VAT 7%",
    "type": "vat",
    "rate": 7.0,
    "account_id": vat_output_account_id,
    "trans_type": "out",
    "description": "Standard VAT on sales"
}

# Create input VAT for purchases
input_comp = {
    "name": "Input VAT 7%",
    "type": "vat",
    "rate": 7.0,
    "account_id": vat_input_account_id,
    "trans_type": "in",
    "description": "VAT on purchases"
}

# Create tax rates
get_model("account.tax.rate").create({
    "name": "Output VAT 7%",
    "components": [("create", output_comp)]
})

get_model("account.tax.rate").create({
    "name": "Input VAT 7%",
    "components": [("create", input_comp)]
})

Use Case 2: Multi-Component Tax

# Tax rate with VAT + WHT

get_model("account.tax.rate").create({
    "name": "VAT 7% + WHT 3%",
    "code": "VAT7WHT3",
    "components": [
        ("create", {
            "name": "SST 7%",
            "type": "vat",
            "rate": 7.0,
            "account_id": vat_output_id,
            "trans_type": "out"
        }),
        ("create", {
            "name": "WHT 3%",
            "type": "wht",
            "rate": 3.0,
            "account_id": wht_id,
            "trans_type": "out"
        })
    ]
})

# Usage:
# Base amount: $1000
# VAT (7%): +$70
# WHT (3%): -$30
# Total: $1040

Use Case 3: Deferred VAT Component

# Setup cash basis VAT

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

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

# Create deferred VAT component
get_model("account.tax.rate").create({
    "name": "VAT 7% (Cash Basis)",
    "components": [
        ("create", {
            "name": "Deferred SST",
            "type": "vat_defer",
            "rate": 7.0,
            "account_id": defer_account_id,        # Temporary account
            "account_pending_id": vat_output_id,    # Final account
            "trans_type": "out"
        })
    ]
})

# Accounting flow:
# 1. Invoice: Dr Deferred VAT
# 2. Payment: Cr Deferred VAT, Dr VAT Output

Use Case 4: Zero-Rated and Exempt

# Setup zero-rated and exempt components

# Zero-rated (0% but still taxable)
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",
            "description": "Zero-rated exports and essential goods"
        })
    ]
})

# Exempt (not taxable)
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",
            "description": "Exempt services (education, healthcare)"
        })
    ]
})

Use Case 5: Compound Tax

# Setup compound tax (tax on tax)

get_model("account.tax.rate").create({
    "name": "Compound Tax (5% + 2%)",
    "components": [
        ("create", {
            "name": "Base Tax",
            "type": "vat",
            "rate": 5.0,
            "compound": False,
            "account_id": tax_account_id
        }),
        ("create", {
            "name": "Additional Tax",
            "type": "vat",
            "rate": 2.0,
            "compound": True,  # Applied to base + first component
            "account_id": tax_account_id
        })
    ]
})

# Calculation:
# Base: $100
# Tax 1 (5%): $5
# Tax 2 (2% on $105): $2.10
# Total: $107.10

Use Case 6: Contact-Specific WHT

# Different WHT rates for companies vs individuals

get_model("account.tax.rate").create({
    "name": "WHT - Services",
    "components": [
        ("create", {
            "name": "WHT Company 3%",
            "type": "wht",
            "rate": 3.0,
            "contact_type": "company",
            "account_id": wht_company_id,
            "description": "Withholding tax on services to companies"
        }),
        ("create", {
            "name": "WHT Individual 5%",
            "type": "wht",
            "rate": 5.0,
            "contact_type": "individual",
            "account_id": wht_individual_id,
            "description": "Withholding tax on services to individuals"
        })
    ]
})

# System automatically selects correct component based on contact type

Performance Tips

1. Index on Common Fields

CREATE INDEX idx_tax_comp_rate ON account_tax_component(tax_rate_id);
CREATE INDEX idx_tax_comp_type ON account_tax_component(type);
CREATE INDEX idx_tax_comp_trans ON account_tax_component(trans_type);

2. Minimize Components

# Good: Simple structure
components = 1-3 per tax rate

# Avoid: Over-complicated
components = 10+ per tax rate (hard to manage)

Troubleshooting

"Component tax not being calculated"

Cause: Wrong when parameter for component type Solution: Verify component type matches timing:

# VAT components - use "invoice"
taxes = compute_taxes(rate_id, base, when="invoice")

# WHT components - use "payment"
taxes = compute_taxes(rate_id, base, when="payment")

# Deferred VAT - use "invoice_payment"
taxes = compute_taxes(rate_id, base, when="invoice_payment")

"Wrong GL account being used"

Cause: Account not set on component Solution: Set account_id on component:

get_model("account.tax.component").write([comp_id], {
    "account_id": correct_account_id
})

"Component shows wrong name"

Cause: Using default name instead of name_get Solution: Use name_get for display:

# Wrong
name = component.name  # Just "Standard SST"

# Correct
name = component.name_get()[0][1]  # "VAT 7% - Standard SST"

"Transaction type filter not working"

Cause: NULL trans_type applies to all Solution: Explicitly set trans_type:

{
    "trans_type": "out"  # Only sales, not purchases
}


Testing Examples

Unit Test: Component Creation

def test_tax_component():
    # Create tax rate with component
    rate_id = get_model("account.tax.rate").create({
        "name": "Test VAT 10%",
        "components": [
            ("create", {
                "name": "Test SST",
                "type": "vat",
                "rate": 10.0,
                "trans_type": "out"
            })
        ]
    })

    # Verify component
    rate = get_model("account.tax.rate").browse([rate_id])[0]
    assert len(rate.components) == 1

    comp = rate.components[0]
    assert comp.name == "Test SST"
    assert comp.type == "vat"
    assert comp.rate == 10.0
    assert comp.trans_type == "out"

    # Verify computed rate
    assert rate.rate == 10.0

    print("✓ Tax component test passed")

    # Cleanup
    get_model("account.tax.rate").delete([rate_id])

Security Considerations

Permission Model

  • Component creation/modification requires accounting admin access
  • Tax setup should be restricted to authorized personnel
  • Changes to components affect all transactions

Data Integrity

  • Components cannot exist without parent tax rate (cascade delete)
  • Rate and account changes affect historical reporting
  • Test thoroughly before modifying production tax components

Integration Points

Internal Modules

  • account.tax.rate: Parent container
  • account.account: GL accounts for posting
  • account.invoice.tax: Computed invoice taxes
  • account.einvoice.taxtype: E-invoice tax mapping

External Systems

  • E-invoicing systems (via einvoice_tax_type_id)
  • Tax authority reporting
  • Financial reporting systems

Version History

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


Additional Resources

  • Tax Rate Documentation: account.tax.rate
  • Invoice Tax Documentation: account.invoice.tax
  • Account Documentation: account.account
  • E-Invoice Tax Type Documentation: account.einvoice.taxtype

This documentation is generated for developer onboarding and reference purposes.