How the Allow Regional Decorator Enables Country-Specific Function Overrides in ERPNext

The allow_regional decorator in ERPNext makes any function regionally overridable by intercepting calls, detecting the current country, and dispatching to a country-specific implementation if one is registered in regional_overrides.

The allow_regional decorator provides a clean mechanism for implementing country-specific business logic without modifying core source files. By wrapping a function with this decorator, developers enable the framework to dynamically substitute the default implementation with a region-specific version based on the company's configured country. This article explains exactly how this dispatch mechanism works according to the ERPNext source code.

How the Allow Regional Decorator Works

The decorator is implemented in erpnext/__init__.py at lines 35‑55. When a decorated function is invoked, the wrapper performs four distinct steps before executing any business logic.

Region Detection via get_region()

First, the wrapper identifies the active region by calling erpnext.get_region(). This function reads the company's country field or a global setting to determine which regional rules apply to the current transaction.

Hook Lookup and Path Resolution

Next, the decorator queries the regional_overrides hooks dictionary using frappe.get_hooks("regional_overrides", {}).get(get_region()). It constructs the fully-qualified function path in the format module_name.function_name and checks whether this path exists as a key in the region's override mapping.

Dynamic Dispatch to Override Implementation

If an override is registered, the decorator loads the implementation using frappe.get_attr(overrides[function_path][-1]) and immediately executes it, passing through all original *args and **kwargs. The [-1] index ensures that when multiple apps register overrides, the last installed app takes precedence. If no override exists, the original function executes unchanged.

Configuration via Regional Overrides Hooks

The mapping between regions and their specific implementations is declared in erpnext/hooks.py at lines 20‑33. Each entry in the regional_overrides dictionary associates a region with a set of function path mappings:

"France": {
    "erpnext.tests.test_regional.test_method": "erpnext.regional.france.utils.test_method",
},
"United Arab Emirates": {
    "erpnext.controllers.taxes_and_totals.update_itemised_tax_data": "erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data",
}

This configuration tells ERPNext exactly which module path to import when a decorated function is called from a company based in that specific country.

Practical Implementation Example

Consider a tax calculation function that needs different logic for the United Arab Emirates. The core implementation remains in the main codebase, while the UAE-specific variant lives in the regional directory.

Default implementation in erpnext/controllers/taxes_and_totals.py:

import erpnext

@erpnext.allow_regional
def update_itemised_tax_data(doc):
    # Default tax calculation logic applicable to most countries

    for item in doc.items:
        item.tax_amount = calculate_standard_tax(item)

Regional override in erpnext/regional/united_arab_emirates/utils.py:

def update_itemised_tax_data(doc):
    # UAE-specific tax rules (e.g., VAT calculation nuances)

    for item in doc.items:
        item.tax_amount = calculate_uae_vat(item)

When the code calls update_itemised_tax_data(doc) on a document belonging to a UAE-based company, the decorator automatically routes execution to the function in erpnext/regional/united_arab_emirates/utils.py without any changes to the calling code.

Key Files and Their Roles

Understanding the allow_regional decorator requires familiarity with three specific file locations:

  • erpnext/__init__.py (lines 35‑55): Contains the decorator implementation that intercepts function calls, resolves regional mappings, and dispatches to the appropriate override.
  • erpnext/hooks.py (lines 20‑33): Defines the regional_overrides dictionary that maps country names to fully-qualified function paths, enabling the plugin architecture.
  • Regional utility modules (e.g., erpnext/regional/italy/utils.py, erpnext/regional/france/utils.py): House the actual country-specific business logic implementations referenced in the hooks configuration.

Summary

  • The allow_regional decorator wraps functions to make them conditionally replaceable based on geographic context.
  • Region detection relies on erpnext.get_region(), which inspects the company's country setting.
  • Override mappings are defined in erpnext/hooks.py under the regional_overrides hook, linking function paths to regional implementations.
  • The decorator uses frappe.get_attr() to dynamically load the last-installed override when a match is found, ensuring extensibility across multiple installed apps.
  • If no override exists for the current region, the original function executes normally, preserving backward compatibility.

Frequently Asked Questions

What is the allow_regional decorator in ERPNext?

The allow_regional decorator is a function wrapper defined in erpnext/__init__.py that enables country-specific function overrides. It intercepts calls to the decorated function, checks if a regional alternative exists for the current company's country, and dispatches execution to that alternative if found.

How does ERPNext determine which regional override to use?

ERPNext determines the override by calling erpnext.get_region() to identify the active country, then looking up the function's fully-qualified path in the regional_overrides hook dictionary defined in erpnext/hooks.py. If a mapping exists, it loads the corresponding implementation from the specified module path.

Can multiple apps override the same regional function?

Yes. When multiple installed apps register overrides for the same function and region, ERPNext selects the last installed app's implementation. The decorator specifically uses overrides[function_path][-1] to retrieve the final entry from the list of registered overrides, allowing apps to customize behavior without modifying core code.

Where are regional overrides configured?

Regional overrides are configured in erpnext/hooks.py within the regional_overrides dictionary. Each key is a country name, and each value is another dictionary mapping original function paths (e.g., erpnext.controllers.taxes_and_totals.update_itemised_tax_data) to their regional replacements (e.g., erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →