Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into bootstraped_gst_setup
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index 1be2fbf..645f49b 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -28,6 +28,12 @@
from erpnext.accounts.utils import get_autoname_with_number
self.name = get_autoname_with_number(self.account_number, self.account_name, None, self.company)
+ def before_insert(self):
+ # Update Bank account name if conflicting with any other account
+ if frappe.flags.in_install and self.account_type == 'Bank':
+ if frappe.db.get_value('Account', {'account_name': self.account_name}):
+ self.account_name = self.account_name + '-1'
+
def validate(self):
from erpnext.accounts.utils import validate_field_number
if frappe.local.flags.allow_unverified_charts:
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
index 927adc7..9b6842d 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
@@ -189,24 +189,6 @@
build_account_tree(tree[child.account_name], child, all_accounts)
@frappe.whitelist()
-def validate_bank_account(coa, bank_account):
- accounts = []
- chart = get_chart(coa)
-
- if chart:
- def _get_account_names(account_master):
- for account_name, child in iteritems(account_master):
- if account_name not in ["account_number", "account_type",
- "root_type", "is_group", "tax_rate"]:
- accounts.append(account_name)
-
- _get_account_names(child)
-
- _get_account_names(chart)
-
- return (bank_account in accounts)
-
-@frappe.whitelist()
def build_tree_from_json(chart_template, chart_data=None):
''' get chart template from its folder and parse the json to be rendered as tree '''
chart = chart_data or get_chart(chart_template)
diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js
index ef03b01..a304572 100644
--- a/erpnext/public/js/setup_wizard.js
+++ b/erpnext/public/js/setup_wizard.js
@@ -139,36 +139,10 @@
},
validate: function () {
- let me = this;
- let exist;
-
if (!this.validate_fy_dates()) {
return false;
}
- // Validate bank name
- if(me.values.bank_account){
- frappe.call({
- async: false,
- method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.validate_bank_account",
- args: {
- "coa": me.values.chart_of_accounts,
- "bank_account": me.values.bank_account
- },
- callback: function (r) {
- if(r.message){
- exist = r.message;
- me.get_field("bank_account").set_value("");
- let message = __('Account {0} already exists. Please enter a different name for your bank account.',
- [me.values.bank_account]
- );
- frappe.msgprint(message);
- }
- }
- });
- return !exist; // Return False if exist = true
- }
-
return true;
},
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.py b/erpnext/regional/doctype/gst_settings/gst_settings.py
index bc956e9..af3d92e 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.py
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.py
@@ -19,6 +19,21 @@
from tabAddress where country = "India" and ifnull(gstin, '')!='' ''')
self.set_onload('data', data)
+ def validate(self):
+ # Validate duplicate accounts
+ self.validate_duplicate_accounts()
+
+ def validate_duplicate_accounts(self):
+ account_list = []
+ for account in self.get('gst_accounts'):
+ for fieldname in ['cgst_account', 'sgst_account', 'igst_account', 'cess_account']:
+ if account.get(fieldname) in account_list:
+ frappe.throw(_("Account {0} appears multiple times").format(
+ frappe.bold(account.get(fieldname))))
+
+ if account.get(fieldname):
+ account_list.append(account.get(fieldname))
+
@frappe.whitelist()
def send_reminder():
frappe.has_permission('GST Settings', throw=True)
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 229e0c0..28f49e0 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -15,6 +15,7 @@
setup_company_independent_fixtures(patch=patch)
if not patch:
make_fixtures(company)
+ setup_gst_settings(company)
# TODO: for all countries
def setup_company_independent_fixtures(patch=False):
@@ -25,6 +26,7 @@
frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test)
create_gratuity_rule()
add_print_formats()
+ update_accounts_settings_for_taxes()
def add_hsn_sac_codes():
# HSN codes
@@ -674,7 +676,7 @@
def make_fixtures(company=None):
docs = []
- company = company.name if company else frappe.db.get_value("Global Defaults", None, "default_company")
+ company = company or frappe.db.get_value("Global Defaults", None, "default_company")
set_salary_components(docs)
set_tds_account(docs, company)
@@ -692,6 +694,57 @@
# create records for Tax Withholding Category
set_tax_withholding_category(company)
+def setup_gst_settings(company):
+ # Will only add default GST accounts if present
+ input_account_names = ['Input Tax CGST', 'Input Tax SGST', 'Input Tax IGST']
+ output_account_names = ['Output Tax CGST', 'Output Tax SGST', 'Output Tax IGST']
+ gst_settings = frappe.get_single('GST Settings')
+ existing_account_list = []
+
+ for account in gst_settings.get('gst_accounts'):
+ for key in ['cgst_account', 'sgst_account', 'igst_account']:
+ existing_account_list.append(account.get(key))
+
+ gst_accounts = frappe._dict(frappe.get_all("Account",
+ {'company': company, 'name': ('like', "%GST%")}, ['account_name', 'name'], as_list=1))
+
+ all_input_account_exists = 0
+ all_output_account_exists = 0
+
+ for account in input_account_names:
+ if not gst_accounts.get(account):
+ all_input_account_exists = 1
+
+ # Check if already added in GST Settings
+ if gst_accounts.get(account) in existing_account_list:
+ all_input_account_exists = 1
+
+ for account in output_account_names:
+ if not gst_accounts.get(account):
+ all_output_account_exists = 1
+
+ # Check if already added in GST Settings
+ if gst_accounts.get(account) in existing_account_list:
+ all_output_account_exists = 1
+
+ if not all_input_account_exists:
+ gst_settings.append('gst_accounts', {
+ 'company': company,
+ 'cgst_account': gst_accounts.get(input_account_names[0]),
+ 'sgst_account': gst_accounts.get(input_account_names[1]),
+ 'igst_account': gst_accounts.get(input_account_names[2])
+ })
+
+ if not all_output_account_exists:
+ gst_settings.append('gst_accounts', {
+ 'company': company,
+ 'cgst_account': gst_accounts.get(output_account_names[0]),
+ 'sgst_account': gst_accounts.get(output_account_names[1]),
+ 'igst_account': gst_accounts.get(output_account_names[2])
+ })
+
+ gst_settings.save()
+
def set_salary_components(docs):
docs.extend([
{'doctype': 'Salary Component', 'salary_component': 'Professional Tax',
@@ -725,12 +778,13 @@
docs = get_tds_details(accounts, fiscal_year)
for d in docs:
- try:
+ if not frappe.db.exists("Tax Withholding Category", d.get("name")):
doc = frappe.get_doc(d)
+ doc.flags.ignore_validate = True
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
doc.insert()
- except frappe.DuplicateEntryError:
+ else:
doc = frappe.get_doc("Tax Withholding Category", d.get("name"))
if accounts:
@@ -743,11 +797,12 @@
doc.append("rates", d.get('rates')[0])
doc.flags.ignore_permissions = True
+ doc.flags.ignore_validdate = True
doc.flags.ignore_mandatory = True
+ doc.flags.ignore_links = True
doc.save()
def set_tds_account(docs, company):
- abbr = frappe.get_value("Company", company, "abbr")
parent_account = frappe.db.get_value("Account", filters = {"account_name": "Duties and Taxes", "company": company})
if parent_account:
docs.extend([
@@ -906,7 +961,6 @@
]
def create_gratuity_rule():
-
# Standard Indain Gratuity Rule
if not frappe.db.exists("Gratuity Rule", "Indian Standard Gratuity Rule"):
rule = frappe.new_doc("Gratuity Rule")
@@ -924,3 +978,7 @@
rule.flags.ignore_mandatory = True
rule.save()
+
+def update_accounts_settings_for_taxes():
+ if frappe.db.count('Company') == 1:
+ frappe.db.set_value('Accounts Settings', None, "add_taxes_from_item_tax_template", 0)
\ No newline at end of file
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
index 4deb073..d0000ad 100644
--- a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
+++ b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
@@ -11,7 +11,7 @@
"is_standard": "Yes",
"json": "{}",
"letter_head": "Logo",
- "modified": "2021-03-12 12:36:48.689413",
+ "modified": "2021-03-13 12:36:48.689413",
"modified_by": "Administrator",
"module": "Regional",
"name": "E-Invoice Summary",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 077538d..61d63a3 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -110,7 +110,7 @@
self.create_default_warehouses()
if frappe.flags.country_change:
- install_country_fixtures(self.name)
+ install_country_fixtures(self.name, self.country)
self.create_default_tax_template()
if not frappe.db.get_value("Department", {"company": self.name}):
@@ -435,16 +435,15 @@
return " - ".join(parts)
-def install_country_fixtures(company):
- company_doc = frappe.get_doc("Company", company)
- path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(company_doc.country))
+def install_country_fixtures(company, country):
+ path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
if os.path.exists(path.encode("utf-8")):
try:
- module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(company_doc.country))
- frappe.get_attr(module_name)(company_doc, False)
+ module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(country))
+ frappe.get_attr(module_name)(company, False)
except Exception as e:
frappe.log_error()
- frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(company_doc.country)))
+ frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(country)))
def update_company_current_month_sales(company):
diff --git a/erpnext/setup/setup_wizard/data/country_wise_tax.json b/erpnext/setup/setup_wizard/data/country_wise_tax.json
index 5876488..d21ef03 100644
--- a/erpnext/setup/setup_wizard/data/country_wise_tax.json
+++ b/erpnext/setup/setup_wizard/data/country_wise_tax.json
@@ -818,31 +818,189 @@
"India": {
"chart_of_accounts": {
"*": {
+ "tax_categories": [
+ {
+ "title": "In-State",
+ "is_inter_state": 0,
+ "gst_state": ""
+ },
+ {
+ "title": "Out-State",
+ "is_inter_state": 1,
+ "gst_state": ""
+ },
+ {
+ "title": "Reverse Charge",
+ "is_inter_state": 0,
+ "gst_state": ""
+ },
+ {
+ "title": "Registered Composition",
+ "is_inter_state": 0,
+ "gst_state": ""
+ }
+ ],
"item_tax_templates": [
{
- "title": "In State GST",
+ "title": "GST 9%",
"taxes": [
{
"tax_type": {
- "account_name": "SGST",
+ "account_name": "Output Tax SGST",
"tax_rate": 9.00
}
},
{
"tax_type": {
- "account_name": "CGST",
+ "account_name": "Output Tax CGST",
"tax_rate": 9.00
}
+ },
+ {
+ "tax_type": {
+ "account_name": "Output Tax IGST",
+ "tax_rate": 18.00
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax SGST",
+ "tax_rate": 9.00
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax CGST",
+ "tax_rate": 9.00
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax IGST",
+ "tax_rate": 18.00
+ }
}
]
},
{
- "title": "Out of State GST",
+ "title": "GST 5%",
"taxes": [
{
"tax_type": {
- "account_name": "IGST",
- "tax_rate": 18.00
+ "account_name": "Output Tax SGST",
+ "tax_rate": 2.5
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Output Tax CGST",
+ "tax_rate": 2.5
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Output Tax IGST",
+ "tax_rate": 5.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax SGST",
+ "tax_rate": 2.5
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax CGST",
+ "tax_rate": 2.5
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax IGST",
+ "tax_rate": 5.0
+ }
+ }
+ ]
+ },
+ {
+ "title": "GST 12%",
+ "taxes": [
+ {
+ "tax_type": {
+ "account_name": "Output Tax SGST",
+ "tax_rate": 6.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Output Tax CGST",
+ "tax_rate": 6.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Output Tax IGST",
+ "tax_rate": 12.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax SGST",
+ "tax_rate": 6.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax CGST",
+ "tax_rate": 6.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax IGST",
+ "tax_rate": 12.0
+ }
+ }
+ ]
+ },
+ {
+ "title": "GST 28%",
+ "taxes": [
+ {
+ "tax_type": {
+ "account_name": "Output Tax SGST",
+ "tax_rate": 14.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Output Tax CGST",
+ "tax_rate": 14.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Output Tax IGST",
+ "tax_rate": 28.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax SGST",
+ "tax_rate": 14.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax CGST",
+ "tax_rate": 14.0
+ }
+ },
+ {
+ "tax_type": {
+ "account_name": "Input Tax IGST",
+ "tax_rate": 28.0
}
}
]
@@ -881,36 +1039,81 @@
]
}
],
- "*": [
+ "sales_tax_templates": [
{
- "title": "In State GST",
+ "title": "Output GST In-state",
"taxes": [
{
"account_head": {
- "account_name": "SGST",
- "tax_rate": 9.00
+ "account_name": "Output Tax SGST",
+ "tax_rate": 9.00,
+ "account_type": "Tax"
}
},
{
"account_head": {
- "account_name": "CGST",
- "tax_rate": 9.00
+ "account_name": "Output Tax CGST",
+ "tax_rate": 9.00,
+ "account_type": "Tax"
}
}
- ]
+ ],
+ "tax_category": "In-State"
},
{
- "title": "Out of State GST",
+ "title": "Output GST Out-state",
"taxes": [
{
"account_head": {
- "account_name": "IGST",
- "tax_rate": 18.00
+ "account_name": "Output Tax IGST",
+ "tax_rate": 18.00,
+ "account_type": "Tax"
}
}
- ]
+ ],
+ "tax_category": "Out-State"
+ }
+ ],
+ "purchase_tax_templates": [
+ {
+ "title": "Input GST In-state",
+ "taxes": [
+ {
+ "account_head": {
+ "account_name": "Input Tax SGST",
+ "tax_rate": 9.00,
+ "root_type": "Asset",
+ "account_type": "Tax"
+ }
+ },
+ {
+ "account_head": {
+ "account_name": "Input Tax CGST",
+ "tax_rate": 9.00,
+ "root_type": "Asset",
+ "account_type": "Tax"
+ }
+ }
+ ],
+ "tax_category": "In-State"
},
{
+ "title": "Input GST Out-state",
+ "taxes": [
+ {
+ "account_head": {
+ "account_name": "Input Tax IGST",
+ "tax_rate": 18.00,
+ "root_type": "Asset",
+ "account_type": "Tax"
+ }
+ }
+ ],
+ "tax_category": "Out-State"
+ }
+ ],
+ "*": [
+ {
"title": "VAT 5%",
"taxes": [
{
@@ -1001,7 +1204,7 @@
"Italy VAT 4%":{
"account_name": "IVA 4%",
"tax_rate": 4.00
- }
+ }
},
"Ivory Coast": {
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index 429a558..a644da9 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -78,6 +78,11 @@
sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*')
purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*')
item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*')
+ tax_categories = tax_templates.get('tax_categories')
+
+ if tax_categories:
+ for tax_category in tax_categories:
+ make_tax_category(tax_category)
if sales_tax_templates:
for template in sales_tax_templates:
@@ -118,8 +123,11 @@
if fieldname not in tax_row:
tax_row[fieldname] = default_value
- return frappe.get_doc(template).insert(ignore_permissions=True)
-
+ doc = frappe.get_doc(template)
+ doc.flags.ignore_links = True
+ doc.flags.ignore_validate = True
+ doc.insert(ignore_permissions=True)
+ return doc
def make_item_tax_template(company_name, template):
"""Create an Item Tax Template.
@@ -144,8 +152,21 @@
if 'tax_rate' not in tax_row:
tax_row['tax_rate'] = account_data.get('tax_rate')
- return frappe.get_doc(template).insert(ignore_permissions=True)
+ doc = frappe.get_doc(template)
+ doc.flags.ignore_links = True
+ doc.flags.ignore_validate = True
+ doc.insert(ignore_permissions=True)
+ return doc
+def make_tax_category(tax_category):
+ """ Make tax category based on title if not already created """
+ doctype = 'Tax Category'
+ if not frappe.db.exists(doctype, tax_category):
+ tax_category['doctype'] = doctype
+ doc = frappe.get_doc(tax_category)
+ doc.flags.ignore_links = True
+ doc.flags.ignore_validate = True
+ doc.insert(ignore_permissions=True)
def get_or_create_account(company_name, account):
"""
@@ -157,12 +178,14 @@
existing_accounts = frappe.get_list('Account',
filters={
- 'company': company_name,
- 'root_type': root_type
+ 'account_name': account.get('account_name'),
+ 'account_number': account.get('account_number', ''),
+ 'company': company_name
},
or_filters={
- 'account_name': account.get('account_name'),
- 'account_number': account.get('account_number')
+ 'company': company_name,
+ 'root_type': root_type,
+ 'is_group': 0
}
)
@@ -179,8 +202,11 @@
account['root_type'] = root_type
account['is_group'] = 0
- return frappe.get_doc(account).insert(ignore_permissions=True, ignore_mandatory=True)
-
+ doc = frappe.get_doc(account)
+ doc.flags.ignore_links = True
+ doc.flags.ignore_validate = True
+ doc.insert(ignore_permissions=True, ignore_mandatory=True)
+ return doc
def get_or_create_tax_group(company_name, root_type):
# Look for a group account of type 'Tax'
@@ -225,7 +251,11 @@
'account_type': 'Tax',
'account_name': account_name,
'parent_account': root_account.name
- }).insert(ignore_permissions=True)
+ })
+
+ tax_group_account.flags.ignore_links = True
+ tax_group_account.flags.ignore_validate = True
+ tax_group_account.insert(ignore_permissions=True)
tax_group_name = tax_group_account.name