Merge pull request #9839 from rmehta/regional-decorators

[feature] override a function regionally by adding a decorator
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index ef9b89c..0761c1e 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
+import inspect
 import frappe
+from erpnext.hooks import regional_overrides
 
 __version__ = '8.4.2'
 
@@ -65,3 +67,34 @@
 			company, "enable_perpetual_inventory") or 0
 
 	return frappe.local.enable_perpetual_inventory[company]
+
+def get_region(company=None):
+	'''Return the default country based on flag, company or global settings
+
+	You can also set global company flag in `frappe.flags.company`
+	'''
+	if company or frappe.flags.company:
+		return frappe.db.get_value('Company',
+			company or frappe.flags.company, 'country')
+	elif frappe.flags.country:
+		return frappe.flags.country
+	else:
+		return frappe.get_system_settings('country')
+
+def allow_regional(fn):
+	'''Decorator to make a function regionally overridable
+
+	Example:
+	@erpnext.allow_regional
+	def myfunction():
+	  pass'''
+	def caller(*args, **kwargs):
+		region = get_region()
+		fn_name = inspect.getmodule(fn).__name__ + '.' + fn.__name__
+		if region in regional_overrides and fn_name in regional_overrides[region]:
+			return frappe.get_attr(regional_overrides[region][fn_name])(*args, **kwargs)
+		else:
+			return fn(*args, **kwargs)
+
+	return caller
+
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 0966485..6777a71 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -1,6 +1,5 @@
 from __future__ import unicode_literals
 from frappe import _
-from . import __version__ as app_version
 
 app_name = "erpnext"
 app_title = "ERPNext"
@@ -211,3 +210,9 @@
 get_site_info = 'erpnext.utilities.get_site_info'
 
 payment_gateway_enabled = "erpnext.accounts.utils.create_payment_gateway_account"
+
+regional_overrides = {
+	'India': {
+		'erpnext.tests.test_regional.test_method': 'erpnext.regional.india.utils.test_method'
+	}
+}
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 84e5f3e..437465a 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -22,3 +22,8 @@
 			if doc.gstin != "NA" and doc.gst_state_number != doc.gstin[:2]:
 				frappe.throw(_("First 2 digits of GSTIN should match with State number {0}")
 					.format(doc.gst_state_number))
+
+# don't remove this function it is used in tests
+def test_method():
+	'''test function'''
+	return 'overridden'
\ No newline at end of file
diff --git a/erpnext/tests/test_regional.py b/erpnext/tests/test_regional.py
new file mode 100644
index 0000000..5d9628f
--- /dev/null
+++ b/erpnext/tests/test_regional.py
@@ -0,0 +1,13 @@
+import unittest, frappe, erpnext
+
+@erpnext.allow_regional
+def test_method():
+	return 'original'
+
+class TestInit(unittest.TestCase):
+	def test_regional_overrides(self):
+		frappe.flags.country = 'India'
+		self.assertEqual(test_method(), 'overridden')
+
+		frappe.flags.country = 'Nepal'
+		self.assertEqual(test_method(), 'original')
\ No newline at end of file