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

> Learn how the allow regional decorator in ERPNext enables country-specific function overrides by detecting regions and dispatching to custom implementations. Enhance your ERPNext localization.

- Repository: [Frappe/erpnext](https://github.com/frappe/erpnext)
- Tags: internals
- Published: 2026-05-20

---

**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`](https://github.com/frappe/erpnext/blob/main/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`](https://github.com/frappe/erpnext/blob/main/erpnext/hooks.py) at lines 20‑33. Each entry in the `regional_overrides` dictionary associates a region with a set of function path mappings:

```python
"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`](https://github.com/frappe/erpnext/blob/main/erpnext/controllers/taxes_and_totals.py):

```python
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`](https://github.com/frappe/erpnext/blob/main/erpnext/regional/united_arab_emirates/utils.py):

```python
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`](https://github.com/frappe/erpnext/blob/main/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`](https://github.com/frappe/erpnext/blob/main/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`](https://github.com/frappe/erpnext/blob/main/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`](https://github.com/frappe/erpnext/blob/main/erpnext/regional/italy/utils.py), [`erpnext/regional/france/utils.py`](https://github.com/frappe/erpnext/blob/main/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`](https://github.com/frappe/erpnext/blob/main/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`](https://github.com/frappe/erpnext/blob/main/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`](https://github.com/frappe/erpnext/blob/main/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`](https://github.com/frappe/erpnext/blob/main/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`).