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):
Combined Tax (2+ components):
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
Related Models¶
| 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:
"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:
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.