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 7894e77e2a5..e26e354b8c0 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 @@ -6,9 +6,8 @@ import frappe, os, json from frappe.utils import cstr from unidecode import unidecode -def create_charts(chart_name, company): - chart = get_chart(chart_name) - +def create_charts(company, chart_template=None, existing_company=None): + chart = get_chart(chart_template, existing_company) if chart: accounts = [] @@ -17,7 +16,7 @@ def create_charts(chart_name, company): if root_account: root_type = child.get("root_type") - if account_name not in ["account_type", "root_type", "is_group"]: + if account_name not in ["account_type", "root_type", "is_group", "tax_rate"]: account_name_in_db = unidecode(account_name.strip().lower()) if account_name_in_db in accounts: @@ -57,21 +56,21 @@ def create_charts(chart_name, company): def identify_is_group(child): if child.get("is_group"): is_group = child.get("is_group") - elif len(set(child.keys()) - set(["account_type", "root_type", "is_group"])): + elif len(set(child.keys()) - set(["account_type", "root_type", "is_group", "tax_rate"])): is_group = 1 else: is_group = 0 return is_group -def get_chart(chart_name): +def get_chart(chart_template, existing_company=None): chart = {} - if chart_name == "Standard Template": + if existing_company: + return get_account_tree_from_existing_company(existing_company) + + elif chart_template == "Standard": from erpnext.accounts.doctype.account.chart_of_accounts.verified import standard_chart_of_accounts return standard_chart_of_accounts.get() - - - else: folders = ("verified",) if frappe.local.flags.allow_unverified_charts: @@ -82,7 +81,7 @@ def get_chart(chart_name): if fname.endswith(".json"): with open(os.path.join(path, fname), "r") as f: chart = f.read() - if chart and json.loads(chart).get("name") == chart_name: + if chart and json.loads(chart).get("name") == chart_template: return json.loads(chart).get("tree") @frappe.whitelist() @@ -114,3 +113,47 @@ def get_charts_for_country(country): charts.append("Standard") return charts + + +def get_account_tree_from_existing_company(existing_company): + all_accounts = frappe.get_all('Account', + filters={'company': existing_company}, + fields = ["name", "account_name", "parent_account", "account_type", + "is_group", "root_type", "tax_rate"], + order_by="lft, rgt") + + account_tree = {} + + # fill in tree starting with root accounts (those with no parent) + build_account_tree(account_tree, None, all_accounts) + + return account_tree + +def build_account_tree(tree, parent, all_accounts): + # find children + parent_account = parent.name if parent else None + children = [acc for acc in all_accounts if acc.parent_account == parent_account] + + # if no children, but a group account + if not children and parent.is_group: + tree["is_group"] = 1 + + # build a subtree for each child + for child in children: + if child.account_type == "Stock" and not child.is_group: + tree["is_group"] = 1 + continue + + # start new subtree + tree[child.account_name] = {} + + # assign account_type and root_type + if child.account_type: + tree[child.account_name]["account_type"] = child.account_type + if child.tax_rate: + tree[child.account_name]["tax_rate"] = child.tax_rate + if not parent: + tree[child.account_name]["root_type"] = child.root_type + + # call recursively to build a subtree for current account + build_account_tree(tree[child.account_name], child, all_accounts) \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 3dedd53c5f9..338d10ab8d3 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -294,6 +294,34 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "default_currency", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Default Currency", + "length": 0, + "no_copy": 0, + "options": "Currency", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -349,61 +377,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, @@ -449,7 +422,7 @@ "in_standard_filter": 0, "label": "Chart Of Accounts Template", "length": 0, - "no_copy": 0, + "no_copy": 1, "options": "", "permlevel": 0, "precision": "", @@ -479,7 +452,7 @@ "in_standard_filter": 0, "label": "Existing Company ", "length": 0, - "no_copy": 0, + "no_copy": 1, "options": "Company", "permlevel": 0, "precision": "", @@ -1633,7 +1606,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2016-11-22 04:14:51.319655", + "modified": "2016-11-23 16:32:04.893315", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 00538cd4c9e..7f506d54402 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -30,6 +30,7 @@ class Company(Document): self.validate_abbr() self.validate_default_accounts() self.validate_currency() + self.validate_coa_input() def validate_abbr(self): if not self.abbr: @@ -113,16 +114,25 @@ class Company(Document): warehouse.insert() def create_default_accounts(self): - if not self.chart_of_accounts: - self.chart_of_accounts = "Standard" - from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts - create_charts(self.chart_of_accounts, self.name) + create_charts(self.name, self.chart_of_accounts, self.existing_company) frappe.db.set(self, "default_receivable_account", frappe.db.get_value("Account", {"company": self.name, "account_type": "Receivable", "is_group": 0})) frappe.db.set(self, "default_payable_account", frappe.db.get_value("Account", {"company": self.name, "account_type": "Payable", "is_group": 0})) + + def validate_coa_input(self): + if self.create_chart_of_accounts_based_on == "Existing Company": + self.chart_of_accounts = None + if not self.existing_company: + frappe.throw(_("Please select Existing Company for creating Chart of Accounts")) + + else: + self.existing_company = None + self.create_chart_of_accounts_based_on = "Standard Template" + if not self.chart_of_accounts: + self.chart_of_accounts = "Standard" def set_default_accounts(self): self._set_default_account("default_cash_account", "Cash") diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py index afcc3b149a9..52836a6b984 100644 --- a/erpnext/setup/doctype/company/test_company.py +++ b/erpnext/setup/doctype/company/test_company.py @@ -7,8 +7,40 @@ test_ignore = ["Account", "Cost Center"] import frappe import unittest -class TestCompany(unittest.TestCase): - pass - - test_records = frappe.get_test_records('Company') + +class TestCompany(unittest.TestCase): + def test_coa_based_on_existing_company(self): + make_company() + + expected_results = { + "Debtors - CFEC": { + "account_type": "Receivable", + "is_group": 0, + "root_type": "Asset", + "parent_account": "Accounts Receivable - CFEC", + }, + "_Test Cash - CFEC": { + "account_type": "Cash", + "is_group": 0, + "root_type": "Asset", + "parent_account": "Cash In Hand - CFEC" + } + } + + for account, acc_property in expected_results.items(): + acc = frappe.get_doc("Account", account) + for prop, val in acc_property.items(): + self.assertEqual(acc.get(prop), val) + + +def make_company(): + company = frappe.new_doc("Company") + company.company_name = "COA from Existing Company" + company.abbr = "CFEC" + company.default_currency = "INR" + company.create_chart_of_accounts_based_on = "Existing Company" + company.existing_company = "_Test Company" + company.save() + + diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py index 646aef187ea..395ea51d3ca 100644 --- a/erpnext/setup/setup_wizard/setup_wizard.py +++ b/erpnext/setup/setup_wizard/setup_wizard.py @@ -88,6 +88,7 @@ def create_fiscal_year_and_company(args): 'abbr':args.get('company_abbr'), 'default_currency':args.get('currency'), 'country': args.get('country'), + 'create_chart_of_accounts_based_on': 'Standard Template', 'chart_of_accounts': args.get(('chart_of_accounts')), 'domain': args.get('domain') }).insert()