refactor: taxes setup
Better structure of input data.
diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json
index 9ccbdb9..6305442 100644
--- a/erpnext/setup/setup_wizard/data/country_wise_tax.json
+++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json
@@ -487,23 +487,25 @@
{
"title": "Umsatzsteuer 19%",
"is_default": 1,
- "accounts": [
+ "taxes": [
{
- "type": "On Net Total",
- "account_name": "Umsatzsteuer 19%",
- "account_number": "3806",
- "rate": 19.00
+ "account_head": {
+ "account_name": "Umsatzsteuer 19%",
+ "account_number": "3806",
+ "tax_rate": 19.00
+ }
}
]
},
{
"title": "Umsatzsteuer 7%",
- "accounts": [
+ "taxes": [
{
- "type": "On Net Total",
- "account_name": "Umsatzsteuer 7%",
- "account_number": "3801",
- "tax_rate": 7.00
+ "account_head": {
+ "account_name": "Umsatzsteuer 7%",
+ "account_number": "3801",
+ "tax_rate": 7.00
+ }
}
]
}
@@ -512,41 +514,49 @@
{
"title": "Abziehbare Vorsteuer 19%",
"is_default": 1,
- "accounts": [
+ "taxes": [
{
- "account_name": "Abziehbare Vorsteuer 19%",
- "account_number": "1406",
- "root_type": "Asset",
- "tax_rate": 19.00
+ "account_head": {
+ "account_name": "Abziehbare Vorsteuer 19%",
+ "account_number": "1406",
+ "root_type": "Asset",
+ "tax_rate": 19.00
+ }
}
]
},
{
"title": "Abziehbare Vorsteuer 7%",
- "accounts": [
+ "taxes": [
{
- "account_name": "Abziehbare Vorsteuer 7%",
- "account_number": "1401",
- "root_type": "Asset",
- "tax_rate": 7.00
+ "account_head": {
+ "account_name": "Abziehbare Vorsteuer 7%",
+ "account_number": "1401",
+ "root_type": "Asset",
+ "tax_rate": 7.00
+ }
}
]
},
{
"title": "Innergemeinschaftlicher Erwerb 19% Umsatzsteuer und 19% Vorsteuer",
- "accounts": [
+ "taxes": [
{
- "account_name": "Abziehbare Vorsteuer nach § 13b UStG 19%",
- "account_number": "1407",
- "root_type": "Asset",
- "tax_rate": 19.00,
+ "account_head": {
+ "account_name": "Abziehbare Vorsteuer nach § 13b UStG 19%",
+ "account_number": "1407",
+ "root_type": "Asset",
+ "tax_rate": 19.00
+ },
"add_deduct_tax": "Add"
},
{
- "account_name": "Umsatzsteuer nach § 13b UStG 19%",
- "account_number": "3837",
- "root_type": "Liability",
- "tax_rate": 19.00,
+ "account_head": {
+ "account_name": "Umsatzsteuer nach § 13b UStG 19%",
+ "account_number": "3837",
+ "root_type": "Liability",
+ "tax_rate": 19.00
+ },
"add_deduct_tax": "Deduct"
}
]
@@ -558,23 +568,25 @@
{
"title": "Umsatzsteuer 19%",
"is_default": 1,
- "accounts": [
+ "taxes": [
{
- "type": "On Net Total",
- "account_name": "Umsatzsteuer 19%",
- "account_number": "1776",
- "rate": 19.00
+ "account_head": {
+ "account_name": "Umsatzsteuer 19%",
+ "account_number": "1776",
+ "tax_rate": 19.00
+ }
}
]
},
{
"title": "Umsatzsteuer 7%",
- "accounts": [
+ "taxes": [
{
- "type": "On Net Total",
- "account_name": "Umsatzsteuer 7%",
- "account_number": "1771",
- "tax_rate": 7.00
+ "account_head": {
+ "account_name": "Umsatzsteuer 7%",
+ "account_number": "1771",
+ "tax_rate": 7.00
+ }
}
]
}
@@ -583,23 +595,27 @@
{
"title": "Abziehbare Vorsteuer 19%",
"is_default": 1,
- "accounts": [
+ "taxes": [
{
- "account_name": "Abziehbare Vorsteuer 19%",
- "account_number": "1576",
- "root_type": "Asset",
- "tax_rate": 19.00
+ "account_head": {
+ "account_name": "Abziehbare Vorsteuer 19%",
+ "account_number": "1576",
+ "root_type": "Asset",
+ "tax_rate": 19.00
+ }
}
]
},
{
"title": "Abziehbare Vorsteuer 7%",
- "accounts": [
+ "taxes": [
{
- "account_name": "Abziehbare Vorsteuer 7%",
- "account_number": "1571",
- "root_type": "Asset",
- "tax_rate": 7.00
+ "account_head": {
+ "account_name": "Abziehbare Vorsteuer 7%",
+ "account_number": "1571",
+ "root_type": "Asset",
+ "tax_rate": 7.00
+ }
}
]
}
@@ -610,23 +626,25 @@
{
"title": "Umsatzsteuer 19%",
"is_default": 1,
- "accounts": [
+ "taxes": [
{
- "type": "On Net Total",
- "account_name": "Umsatzsteuer 19%",
- "account_number": "2301",
- "rate": 19.00
+ "account_head": {
+ "account_name": "Umsatzsteuer 19%",
+ "account_number": "2301",
+ "tax_rate": 19.00
+ }
}
]
},
{
"title": "Umsatzsteuer 7%",
- "accounts": [
+ "taxes": [
{
- "type": "On Net Total",
- "account_name": "Umsatzsteuer 7%",
- "account_number": "2302",
- "tax_rate": 7.00
+ "account_head": {
+ "account_name": "Umsatzsteuer 7%",
+ "account_number": "2302",
+ "tax_rate": 7.00
+ }
}
]
}
@@ -635,23 +653,27 @@
{
"title": "Abziehbare Vorsteuer 19%",
"is_default": 1,
- "accounts": [
+ "taxes": [
{
- "account_name": "Abziehbare Vorsteuer 19%",
- "account_number": "1501",
- "root_type": "Asset",
- "tax_rate": 19.00
+ "account_head": {
+ "account_name": "Abziehbare Vorsteuer 19%",
+ "account_number": "1501",
+ "root_type": "Asset",
+ "tax_rate": 19.00
+ }
}
]
},
{
"title": "Abziehbare Vorsteuer 7%",
- "accounts": [
+ "taxes": [
{
- "account_name": "Abziehbare Vorsteuer 7%",
- "account_number": "1502",
- "root_type": "Asset",
- "tax_rate": 7.00
+ "account_head": {
+ "account_name": "Abziehbare Vorsteuer 7%",
+ "account_number": "1502",
+ "root_type": "Asset",
+ "tax_rate": 7.00
+ }
}
]
}
@@ -662,21 +684,23 @@
{
"title": "Umsatzsteuer 19%",
"is_default": 1,
- "accounts": [
+ "taxes": [
{
- "type": "On Net Total",
- "account_name": "Umsatzsteuer 19%",
- "rate": 19.00
+ "account_head": {
+ "account_name": "Umsatzsteuer 19%",
+ "tax_rate": 19.00
+ }
}
]
},
{
"title": "Umsatzsteuer 7%",
- "accounts": [
+ "taxes": [
{
- "type": "On Net Total",
- "account_name": "Umsatzsteuer 7%",
- "tax_rate": 7.00
+ "account_head": {
+ "account_name": "Umsatzsteuer 7%",
+ "tax_rate": 7.00
+ }
}
]
}
@@ -685,21 +709,25 @@
{
"title": "Abziehbare Vorsteuer 19%",
"is_default": 1,
- "accounts": [
+ "taxes": [
{
- "account_name": "Abziehbare Vorsteuer 19%",
- "root_type": "Asset",
- "tax_rate": 19.00
+ "account_head": {
+ "account_name": "Abziehbare Vorsteuer 19%",
+ "tax_rate": 19.00,
+ "root_type": "Asset"
+ }
}
]
},
{
"title": "Abziehbare Vorsteuer 7%",
- "accounts": [
+ "taxes": [
{
- "account_name": "Abziehbare Vorsteuer 7%",
- "root_type": "Asset",
- "tax_rate": 7.00
+ "account_head": {
+ "account_name": "Abziehbare Vorsteuer 7%",
+ "root_type": "Asset",
+ "tax_rate": 7.00
+ }
}
]
}
@@ -798,54 +826,130 @@
"India": {
"chart_of_accounts": {
"*": {
- "*": [
+ "item_tax_templates": [
{
"title": "In State GST",
"is_default": 1,
- "accounts": [
+ "taxes": [
{
- "account_name": "SGST",
- "tax_rate": 9.00
+ "tax_type": {
+ "account_name": "SGST",
+ "tax_rate": 9.00
+ }
},
{
- "account_name": "CGST",
- "tax_rate": 9.00
+ "tax_type": {
+ "account_name": "CGST",
+ "tax_rate": 9.00
+ }
}
]
},
{
"title": "Out of State GST",
- "accounts": [
+ "taxes": [
{
- "account_name": "IGST",
- "tax_rate": 18.00
+ "tax_type": {
+ "account_name": "IGST",
+ "tax_rate": 18.00
+ }
}
]
},
{
"title": "VAT 5%",
- "accounts": [
+ "taxes": [
{
- "account_name": "VAT 5%",
- "tax_rate": 5.00
+ "tax_type": {
+ "account_name": "VAT 5%",
+ "tax_rate": 5.00
+ }
}
]
},
{
"title": "VAT 4%",
- "accounts": [
+ "taxes": [
{
- "account_name": "VAT 4%",
- "tax_rate": 4.00
+ "tax_type": {
+ "account_name": "VAT 4%",
+ "tax_rate": 4.00
+ }
}
]
},
{
"title": "VAT 14%",
- "accounts": [
+ "taxes": [
{
- "account_name": "VAT 14%",
- "tax_rate": 14.00
+ "tax_type": {
+ "account_name": "VAT 14%",
+ "tax_rate": 14.00
+ }
+ }
+ ]
+ }
+ ],
+ "*": [
+ {
+ "title": "In State GST",
+ "is_default": 1,
+ "taxes": [
+ {
+ "account_head": {
+ "account_name": "SGST",
+ "tax_rate": 9.00
+ }
+ },
+ {
+ "account_head": {
+ "account_name": "CGST",
+ "tax_rate": 9.00
+ }
+ }
+ ]
+ },
+ {
+ "title": "Out of State GST",
+ "taxes": [
+ {
+ "account_head": {
+ "account_name": "IGST",
+ "tax_rate": 18.00
+ }
+ }
+ ]
+ },
+ {
+ "title": "VAT 5%",
+ "taxes": [
+ {
+ "account_head": {
+ "account_name": "VAT 5%",
+ "tax_rate": 5.00
+ }
+ }
+ ]
+ },
+ {
+ "title": "VAT 4%",
+ "taxes": [
+ {
+ "account_head": {
+ "account_name": "VAT 4%",
+ "tax_rate": 4.00
+ }
+ }
+ ]
+ },
+ {
+ "title": "VAT 14%",
+ "taxes": [
+ {
+ "account_head": {
+ "account_name": "VAT 14%",
+ "tax_rate": 14.00
+ }
}
]
}
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index 81506c4..429a558 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -17,40 +17,62 @@
country_wise_tax = tax_data.get(country)
- if country_wise_tax:
- if 'chart_of_accounts' in country_wise_tax:
- from_detailed_data(company_name, country_wise_tax.get('chart_of_accounts'))
- else:
- from_simple_data(company_name, country_wise_tax)
+ if not country_wise_tax:
+ return
+
+ if 'chart_of_accounts' not in country_wise_tax:
+ country_wise_tax = simple_to_detailed(country_wise_tax)
+
+ from_detailed_data(company_name, country_wise_tax.get('chart_of_accounts'))
-def from_detailed_data(company_name, data):
+def simple_to_detailed(templates):
"""
- Create Taxes and Charges Templates from detailed data like this:
+ Convert a simple taxes object into a more detailed data structure.
+
+ Example input:
{
- "chart_of_accounts": {
- coa_name: {
- "sales_tax_templates": [
- {
- 'title': '',
- 'is_default': 1,
- 'accounts': [
- {
- 'account_name': '',
- 'account_number': '',
- 'root_type': '',
- }
- ]
- }
- ],
- "purchase_tax_templates": [ ... ],
- "item_tax_templates": [ ... ],
- "*": [ ... ]
- }
+ "France VAT 20%": {
+ "account_name": "VAT 20%",
+ "tax_rate": 20,
+ "default": 1
+ },
+ "France VAT 10%": {
+ "account_name": "VAT 10%",
+ "tax_rate": 10
}
}
"""
+ return {
+ 'chart_of_accounts': {
+ '*': {
+ 'item_tax_templates': [{
+ 'title': title,
+ 'taxes': [{
+ 'tax_type': {
+ 'account_name': data.get('account_name'),
+ 'tax_rate': data.get('tax_rate')
+ }
+ }]
+ } for title, data in templates.items()],
+ '*': [{
+ 'title': title,
+ 'is_default': data.get('default', 0),
+ 'taxes': [{
+ 'account_head': {
+ 'account_name': data.get('account_name'),
+ 'tax_rate': data.get('tax_rate')
+ }
+ }]
+ } for title, data in templates.items()]
+ }
+ }
+ }
+
+
+def from_detailed_data(company_name, data):
+ """Create Taxes and Charges Templates from detailed data."""
coa_name = frappe.db.get_value('Company', company_name, 'chart_of_accounts')
tax_templates = data.get(coa_name) or data.get('*')
sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*')
@@ -59,85 +81,44 @@
if sales_tax_templates:
for template in sales_tax_templates:
- make_tax_template(company_name, 'Sales Taxes and Charges Template', template)
+ make_taxes_and_charges_template(company_name, 'Sales Taxes and Charges Template', template)
if purchase_tax_templates:
for template in purchase_tax_templates:
- make_tax_template(company_name, 'Purchase Taxes and Charges Template', template)
+ make_taxes_and_charges_template(company_name, 'Purchase Taxes and Charges Template', template)
if item_tax_templates:
for template in item_tax_templates:
make_item_tax_template(company_name, template)
-def from_simple_data(company_name, data):
- """
- Create Taxes and Charges Templates from simple data like this:
+def make_taxes_and_charges_template(company_name, doctype, template):
+ template['company'] = company_name
+ template['doctype'] = doctype
- "Austria Tax": {
- "account_name": "VAT",
- "tax_rate": 20.00
- }
- """
- for template_name, tax_data in data.items():
- template = {
- 'title': template_name,
- 'is_default': tax_data.get('default'),
- 'accounts': [
- {
- 'account_name': tax_data.get('account_name'),
- 'tax_rate': tax_data.get('tax_rate')
- }
- ]
- }
- make_tax_template(company_name, 'Sales Taxes and Charges Template', template)
- make_tax_template(company_name, 'Purchase Taxes and Charges Template', template)
- make_item_tax_template(company_name, template)
-
-
-def make_tax_template(company_name, doctype, template):
if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}):
return
- accounts = get_or_create_accounts(company_name, template.get('accounts'))
-
- # Get all fields of the Taxes and Charges Template
- tax_template = {'doctype': doctype}
- tax_template_fields = frappe.get_meta(doctype).fields
- tax_template_fieldnames = [field.fieldname for field in tax_template_fields]
-
- # Get all fields of the taxes child table
- table_doctype = [field.options for field in tax_template_fields if field.fieldname=='taxes'][0]
- table_fields = frappe.get_meta(table_doctype).fields
- table_field_names = [field.fieldname for field in table_fields]
-
- # Check if field exists as a key in the import data and, if yes, set the
- # value accordingly
- for field in tax_template_fieldnames:
- if field in template:
- tax_template[field] = template.get(field)
-
- # However, company always fixed and taxes table must be empty to start with
- tax_template['company'] = company_name
- tax_template['taxes'] = []
-
- for account in accounts:
- row = {
+ for tax_row in template.get('taxes'):
+ account_data = tax_row.get('account_head')
+ tax_row_defaults = {
'category': 'Total',
- 'charge_type': 'On Net Total',
- 'account_head': account.get('name'),
- 'description': '{0} @ {1}'.format(account.get('account_name'), account.get('tax_rate')),
- 'rate': account.get('tax_rate')
+ 'charge_type': 'On Net Total'
}
- # Check if field exists as a key in the import data and, if yes, set the
- # value accordingly
- for field in table_field_names:
- if field in account:
- row[field] = account.get(field)
- tax_template['taxes'].append(row)
+ # if account_head is a dict, search or create the account and get it's name
+ if isinstance(account_data, dict):
+ tax_row_defaults['description'] = '{0} @ {1}'.format(account_data.get('account_name'), account_data.get('tax_rate'))
+ tax_row_defaults['rate'] = account_data.get('tax_rate')
+ account = get_or_create_account(company_name, account_data)
+ tax_row['account_head'] = account.name
- return frappe.get_doc(tax_template).insert(ignore_permissions=True)
+ # use the default value if nothing other is specified
+ for fieldname, default_value in tax_row_defaults.items():
+ if fieldname not in tax_row:
+ tax_row[fieldname] = default_value
+
+ return frappe.get_doc(template).insert(ignore_permissions=True)
def make_item_tax_template(company_name, template):
@@ -147,111 +128,105 @@
differently from Sales and Purchase Tax Templates.
"""
doctype = 'Item Tax Template'
+ template['company'] = company_name
+ template['doctype'] = doctype
+
if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}):
return
- accounts = get_or_create_accounts(company_name, template.get('accounts'))
+ for tax_row in template.get('taxes'):
+ account_data = tax_row.get('tax_type')
- item_tax_template = {
- 'doctype': doctype,
- 'title': template.get('title'),
- 'company': company_name,
- 'taxes': [{
- 'tax_type': account.get('name'),
- 'tax_rate': account.get('tax_rate')
- } for account in accounts]
- }
+ # if tax_type is a dict, search or create the account and get it's name
+ if isinstance(account_data, dict):
+ account = get_or_create_account(company_name, account_data)
+ tax_row['tax_type'] = account.name
+ if 'tax_rate' not in tax_row:
+ tax_row['tax_rate'] = account_data.get('tax_rate')
- return frappe.get_doc(item_tax_template).insert(ignore_permissions=True)
+ return frappe.get_doc(template).insert(ignore_permissions=True)
-def get_or_create_accounts(company: str, account_data: list):
- for account in account_data:
- if 'creation' in account:
- # Hack to check if account already contains a real Account doc
- # or just the attibutes from country_wise_tax.json
- continue
-
- # tax_rate should survive the following lines because it might not be
- # specified in an existing account or different rates might get booked
- # onto the same account.
- tax_rate = account.get('tax_rate')
- doc = get_or_create_account(company, account)
- account.update(doc.as_dict())
- account['tax_rate'] = tax_rate
-
- return account_data
-
-
-def get_or_create_account(company, account_data):
+def get_or_create_account(company_name, account):
"""
Check if account already exists. If not, create it.
Return a tax account or None.
"""
- root_type = account_data.get('root_type', 'Liability')
- account_name = account_data.get('account_name')
- account_number = account_data.get('account_number')
+ default_root_type = 'Liability'
+ root_type = account.get('root_type', default_root_type)
existing_accounts = frappe.get_list('Account',
filters={
- 'company': company,
+ 'company': company_name,
'root_type': root_type
},
or_filters={
- 'account_name': account_name,
- 'account_number': account_number
+ 'account_name': account.get('account_name'),
+ 'account_number': account.get('account_number')
}
)
if existing_accounts:
return frappe.get_doc('Account', existing_accounts[0].name)
- tax_group = get_or_create_tax_account_group(company, root_type)
- full_account_data = {
- 'doctype': 'Account',
- 'account_name': account_name,
- 'account_number': account_number,
- 'tax_rate': account_data.get('tax_rate'),
- 'company': company,
- 'parent_account': tax_group,
- 'is_group': 0,
- 'report_type': 'Balance Sheet',
- 'root_type': root_type,
- 'account_type': 'Tax'
- }
- return frappe.get_doc(full_account_data).insert(ignore_permissions=True, ignore_mandatory=True)
+ tax_group = get_or_create_tax_group(company_name, root_type)
+
+ account['doctype'] = 'Account'
+ account['company'] = company_name
+ account['parent_account'] = tax_group
+ account['report_type'] = 'Balance Sheet'
+ account['account_type'] = 'Tax'
+ account['root_type'] = root_type
+ account['is_group'] = 0
+
+ return frappe.get_doc(account).insert(ignore_permissions=True, ignore_mandatory=True)
-def get_or_create_tax_account_group(company, root_type):
- tax_group = frappe.db.get_value('Account', {
+def get_or_create_tax_group(company_name, root_type):
+ # Look for a group account of type 'Tax'
+ tax_group_name = frappe.db.get_value('Account', {
'is_group': 1,
'root_type': root_type,
'account_type': 'Tax',
- 'company': company
+ 'company': company_name
})
- if tax_group:
- return tax_group
+ if tax_group_name:
+ return tax_group_name
- root = frappe.get_list('Account', {
+ # Look for a group account named 'Duties and Taxes' or 'Tax Assets'
+ account_name = _('Duties and Taxes') if root_type == 'Liability' else _('Tax Assets')
+ tax_group_name = frappe.db.get_value('Account', {
'is_group': 1,
'root_type': root_type,
- 'company': company,
+ 'account_name': account_name,
+ 'company': company_name
+ })
+
+ if tax_group_name:
+ return tax_group_name
+
+ # Create a new group account named 'Duties and Taxes' or 'Tax Assets' just
+ # below the root account
+ root_account = frappe.get_list('Account', {
+ 'is_group': 1,
+ 'root_type': root_type,
+ 'company': company_name,
'report_type': 'Balance Sheet',
'parent_account': ('is', 'not set')
- }, limit=1)[0].name
+ }, limit=1)[0]
- doc = frappe.get_doc({
+ tax_group_account = frappe.get_doc({
'doctype': 'Account',
- 'company': company,
+ 'company': company_name,
'is_group': 1,
'report_type': 'Balance Sheet',
'root_type': root_type,
'account_type': 'Tax',
- 'account_name': _('Duties and Taxes') if root_type == 'Liability' else _('Tax Assets'),
- 'parent_account': root
+ 'account_name': account_name,
+ 'parent_account': root_account.name
}).insert(ignore_permissions=True)
- tax_group = doc.name
+ tax_group_name = tax_group_account.name
- return tax_group
+ return tax_group_name