diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index e040b619d1..b7eb786354 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -296,6 +296,37 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "allow_cost_center_in_entry_of_bs_account", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Allow Cost Center In Entry of Balance Sheet Account", + "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, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -543,6 +574,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -575,6 +607,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -589,7 +622,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-02-21 16:47:38.043115", + "modified": "2018-05-14 15:58:27.638576", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", @@ -597,7 +630,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 0, @@ -617,7 +649,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -637,7 +668,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 4f1570c1e8..b834a5fc00 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -17,6 +17,7 @@ class AccountsSettings(Document): def validate(self): self.validate_stale_days() self.enable_payment_schedule_in_print() + self.enable_fields_for_cost_center_settings() def validate_stale_days(self): if not self.allow_stale and cint(self.stale_days) <= 0: @@ -28,4 +29,9 @@ class AccountsSettings(Document): show_in_print = cint(self.show_payment_schedule_in_print) for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"): make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check") - make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check") \ No newline at end of file + make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check") + + def enable_fields_for_cost_center_settings(self): + show_field = 0 if cint(self.allow_cost_center_in_entry_of_bs_account) else 1 + for doctype in ("Sales Invoice", "Purchase Invoice", "Payment Entry"): + make_property_setter(doctype, "cost_center", "hidden", show_field, "Check") diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py index dab50288c5..c4fad75375 100644 --- a/erpnext/accounts/doctype/cost_center/test_cost_center.py +++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py @@ -4,4 +4,23 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Cost Center') \ No newline at end of file +test_records = frappe.get_test_records('Cost Center') + + + +def create_cost_center(**args): + args = frappe._dict(args) + if args.cost_center_name: + company = args.company or "_Test Company" + company_abbr = frappe.db.get_value("Company", company, "abbr") + cc_name = args.cost_center_name + " - " + company_abbr + if not frappe.db.exists("Cost Center", cc_name): + cc = frappe.new_doc("Cost Center") + cc.company = args.company or "_Test Company" + cc.cost_center_name = args.cost_center_name + cc.is_group = args.is_group or 0 + cc.parent_cost_center = args.parent_cost_center or "_Test Company - _TC" + cc.insert() + + + diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 47e214e1b4..e6fe6caf14 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -67,7 +67,8 @@ class GLEntry(Document): frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.") .format(self.voucher_type, self.voucher_no, self.account)) else: - if self.cost_center: + from erpnext.accounts.utils import get_allow_cost_center_in_entry_of_bs_account + if not get_allow_cost_center_in_entry_of_bs_account() and self.cost_center: self.cost_center = None if self.project: self.project = None diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 6ad1df52cd..6aec6e94a1 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -414,37 +414,18 @@ frappe.ui.form.on("Journal Entry Account", { args: { company: frm.doc.company, party_type: d.party_type, - party: d.party + party: d.party, + cost_center: d.cost_center } }); } }, + cost_center: function(frm, dt, dn) { + erpnext.journal_entry.set_account_balance(frm, dt, dn); + }, account: function(frm, dt, dn) { - var d = locals[dt][dn]; - if(d.account) { - if(!frm.doc.company) frappe.throw(__("Please select Company first")); - if(!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first")); - - return frappe.call({ - method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type", - args: { - account: d.account, - date: frm.doc.posting_date, - company: frm.doc.company, - debit: flt(d.debit_in_account_currency), - credit: flt(d.credit_in_account_currency), - exchange_rate: d.exchange_rate - }, - callback: function(r) { - if(r.message) { - $.extend(d, r.message); - erpnext.journal_entry.set_debit_credit_in_company_currency(frm, dt, dn); - refresh_field('accounts'); - } - } - }); - } + erpnext.journal_entry.set_account_balance(frm, dt, dn); }, debit_in_account_currency: function(frm, cdt, cdn) { @@ -637,3 +618,33 @@ $.extend(erpnext.journal_entry, { cur_frm.reload_doc(); } }); + +$.extend(erpnext.journal_entry, { + set_account_balance: function(frm, dt, dn) { + var d = locals[dt][dn]; + if(d.account) { + if(!frm.doc.company) frappe.throw(__("Please select Company first")); + if(!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first")); + + return frappe.call({ + method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type", + args: { + account: d.account, + date: frm.doc.posting_date, + company: frm.doc.company, + debit: flt(d.debit_in_account_currency), + credit: flt(d.credit_in_account_currency), + exchange_rate: d.exchange_rate, + cost_center: d.cost_center + }, + callback: function(r) { + if(r.message) { + $.extend(d, r.message); + erpnext.journal_entry.set_debit_credit_in_company_currency(frm, dt, dn); + refresh_field('accounts'); + } + } + }); + } + }, +}); diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 84c165f3d0..259172e448 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -716,7 +716,7 @@ def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_cur def get_payment_entry(ref_doc, args): - cost_center = frappe.get_cached_value('Company', ref_doc.company, "cost_center") + cost_center = ref_doc.get("cost_center") or frappe.get_cached_value('Company', ref_doc.company, "cost_center") exchange_rate = 1 if args.get("party_account"): # Modified to include the posting date for which the exchange rate is required. @@ -849,14 +849,14 @@ def get_outstanding(args): } @frappe.whitelist() -def get_party_account_and_balance(company, party_type, party): +def get_party_account_and_balance(company, party_type, party, cost_center=None): if not frappe.has_permission("Account"): frappe.msgprint(_("No Permission"), raise_exception=1) account = get_party_account(party_type, party, company) - account_balance = get_balance_on(account=account) - party_balance = get_balance_on(party_type=party_type, party=party, company=company) + account_balance = get_balance_on(account=account, cost_center=cost_center) + party_balance = get_balance_on(party_type=party_type, party=party, company=company, cost_center=cost_center) return { "account": account, @@ -867,7 +867,7 @@ def get_party_account_and_balance(company, party_type, party): @frappe.whitelist() -def get_account_balance_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None): +def get_account_balance_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None, cost_center=None): """Returns dict of account balance and party type to be set in Journal Entry on selection of account.""" if not frappe.has_permission("Account"): frappe.msgprint(_("No Permission"), raise_exception=1) @@ -886,7 +886,7 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi party_type = "" grid_values = { - "balance": get_balance_on(account, date), + "balance": get_balance_on(account, date, cost_center=cost_center), "party_type": party_type, "account_type": account_details.account_type, "account_currency": account_details.account_currency or company_currency, diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 5495c93d95..6996c775b3 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -204,12 +204,72 @@ class TestJournalEntry(unittest.TestCase): self.assertEqual(jv.inter_company_journal_entry_reference, "") self.assertEqual(jv1.inter_company_journal_entry_reference, "") + def test_jv_for_enable_allow_cost_center_in_entry_of_bs_account(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center for BS Account - _TC" + create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") + jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False) + jv.voucher_type = "Bank Entry" + jv.multi_currency = 0 + jv.cheque_no = "112233" + jv.cheque_date = nowdate() + jv.insert() + jv.submit() + + expected_values = { + "_Test Cash - _TC": { + "cost_center": cost_center + }, + "_Test Bank - _TC": { + "cost_center": cost_center + } + } + + gl_entries = frappe.db.sql("""select account, cost_center, debit, credit + from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s + order by account asc""", jv.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + + def test_jv_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + from erpnext.accounts.utils import get_balance_on + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center for BS Account - _TC" + create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") + jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False) + account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center) + jv.voucher_type = "Bank Entry" + jv.multi_currency = 0 + jv.cheque_no = "112233" + jv.cheque_date = nowdate() + jv.insert() + jv.submit() + + expected_account_balance = account_balance - 100 + account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center) + self.assertEqual(expected_account_balance, account_balance) + + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None): if not cost_center: cost_center = "_Test Cost Center - _TC" jv = frappe.new_doc("Journal Entry") - jv.posting_date = posting_date or "2013-02-14" + jv.posting_date = posting_date or nowdate() jv.company = "_Test Company" jv.user_remark = "test" jv.multi_currency = 1 diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 490f2b421e..9215e5f366 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -245,7 +245,8 @@ frappe.ui.form.on('Payment Entry', { company: frm.doc.company, party_type: frm.doc.party_type, party: frm.doc.party, - date: frm.doc.posting_date + date: frm.doc.posting_date, + cost_center: frm.doc.cost_center }, callback: function(r, rt) { if(r.message) { @@ -317,7 +318,8 @@ frappe.ui.form.on('Payment Entry', { method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_account_details", args: { "account": account, - "date": frm.doc.posting_date + "date": frm.doc.posting_date, + "cost_center": frm.doc.cost_center }, callback: function(r, rt) { if(r.message) { @@ -505,7 +507,8 @@ frappe.ui.form.on('Payment Entry', { "party_type": frm.doc.party_type, "payment_type": frm.doc.payment_type, "party": frm.doc.party, - "party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to + "party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to, + "cost_center": frm.doc.cost_center } }, callback: function(r, rt) { @@ -859,3 +862,38 @@ frappe.ui.form.on('Payment Entry Deduction', { frm.events.set_unallocated_amount(frm); } }) +frappe.ui.form.on('Payment Entry', { + cost_center: function(frm){ + if (frm.doc.posting_date && (frm.doc.paid_from||frm.doc.paid_to)) { + return frappe.call({ + method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_and_account_balance", + args: { + company: frm.doc.company, + date: frm.doc.posting_date, + paid_from: frm.doc.paid_from, + paid_to: frm.doc.paid_to, + ptype: frm.doc.party_type, + pty: frm.doc.party, + cost_center: frm.doc.cost_center + }, + callback: function(r, rt) { + if(r.message) { + frappe.run_serially([ + () => { + frm.set_value("paid_from_account_balance", r.message.paid_from_account_balance); + frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance); + frm.set_value("party_balance", r.message.party_balance); + }, + () => { + if(frm.doc.payment_type != "Internal") { + frm.events.get_outstanding_documents(frm); + } + } + ]); + + } + } + }); + } + }, +}) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index bffe6690a1..1d5e13070f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -208,6 +208,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cost_center", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Cost Center", + "length": 0, + "no_copy": 0, + "options": "Cost Center", + "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, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -1962,4 +1995,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9ce7ecb6bc..6c814ad954 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -127,12 +127,12 @@ class PaymentEntry(AccountsController): self.party_account = party_account if self.paid_from and not (self.paid_from_account_currency or self.paid_from_account_balance): - acc = get_account_details(self.paid_from, self.posting_date) + acc = get_account_details(self.paid_from, self.posting_date, self.cost_center) self.paid_from_account_currency = acc.account_currency self.paid_from_account_balance = acc.account_balance if self.paid_to and not (self.paid_to_account_currency or self.paid_to_account_balance): - acc = get_account_details(self.paid_to, self.posting_date) + acc = get_account_details(self.paid_to, self.posting_date, self.cost_center) self.paid_to_account_currency = acc.account_currency self.paid_to_account_balance = acc.account_balance @@ -419,7 +419,8 @@ class PaymentEntry(AccountsController): "party_type": self.party_type, "party": self.party, "against": against_account, - "account_currency": self.party_account_currency + "account_currency": self.party_account_currency, + "cost_center": self.cost_center }) dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit" @@ -462,7 +463,8 @@ class PaymentEntry(AccountsController): "account_currency": self.paid_from_account_currency, "against": self.party if self.payment_type=="Pay" else self.paid_to, "credit_in_account_currency": self.paid_amount, - "credit": self.base_paid_amount + "credit": self.base_paid_amount, + "cost_center": self.cost_center }) ) if self.payment_type in ("Receive", "Internal Transfer"): @@ -472,7 +474,8 @@ class PaymentEntry(AccountsController): "account_currency": self.paid_to_account_currency, "against": self.party if self.payment_type=="Receive" else self.paid_from, "debit_in_account_currency": self.received_amount, - "debit": self.base_received_amount + "debit": self.base_received_amount, + "cost_center": self.cost_center }) ) @@ -549,6 +552,10 @@ def get_outstanding_reference_documents(args): condition = " and voucher_type='{0}' and voucher_no='{1}'"\ .format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])) + # Add cost center condition + if args.get("cost_center"): + condition += " and cost_center='%s'" % args.get("cost_center") + outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"), args.get("party_account"), condition=condition) @@ -573,7 +580,7 @@ def get_outstanding_reference_documents(args): return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed -def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency): +def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency, cost_center=None): if party_type == "Customer": voucher_type = 'Sales Order' elif party_type == "Supplier": @@ -581,6 +588,12 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre elif party_type == "Employee": voucher_type = None + # Add cost center condition + doc = frappe.get_doc({"doctype": voucher_type}) + condition = "" + if doc and hasattr(doc, 'cost_center'): + condition = " and cost_center='%s'" % cost_center + orders = [] if voucher_type: ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total" @@ -599,12 +612,14 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre and ifnull(status, "") != "Closed" and {ref_field} > advance_paid and abs(100 - per_billed) > 0.01 + {condition} order by transaction_date, name """.format(**{ "ref_field": ref_field, "voucher_type": voucher_type, - "party_type": scrub(party_type) + "party_type": scrub(party_type), + "condition": condition }), party, as_dict=True) order_list = [] @@ -616,7 +631,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre return order_list -def get_negative_outstanding_invoices(party_type, party, party_account, party_account_currency, company_currency): +def get_negative_outstanding_invoices(party_type, party, party_account, party_account_currency, company_currency, cost_center=None): voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" supplier_condition = "" if voucher_type == "Purchase Invoice": @@ -647,22 +662,23 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac "grand_total_field": grand_total_field, "voucher_type": voucher_type, "party_type": scrub(party_type), - "party_account": "debit_to" if party_type == "Customer" else "credit_to" + "party_account": "debit_to" if party_type == "Customer" else "credit_to", + "cost_center": cost_center }), (party, party_account), as_dict=True) @frappe.whitelist() -def get_party_details(company, party_type, party, date): +def get_party_details(company, party_type, party, date, cost_center=None): if not frappe.db.exists(party_type, party): frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) party_account = get_party_account(party_type, party, company) account_currency = get_account_currency(party_account) - account_balance = get_balance_on(party_account, date) + account_balance = get_balance_on(party_account, date, cost_center=cost_center) _party_name = "title" if party_type == "Student" else party_type.lower() + "_name" party_name = frappe.db.get_value(party_type, party, _party_name) - party_balance = get_balance_on(party_type=party_type, party=party) + party_balance = get_balance_on(party_type=party_type, party=party, cost_center=cost_center) return { "party_account": party_account, @@ -674,11 +690,11 @@ def get_party_details(company, party_type, party, date): @frappe.whitelist() -def get_account_details(account, date): +def get_account_details(account, date, cost_center=None): frappe.has_permission('Payment Entry', throw=True) return frappe._dict({ "account_currency": get_account_currency(account), - "account_balance": get_balance_on(account, date), + "account_balance": get_balance_on(account, date, cost_center=cost_center), "account_type": frappe.db.get_value("Account", account, "account_type") }) @@ -855,6 +871,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe = frappe.new_doc("Payment Entry") pe.payment_type = payment_type pe.company = doc.company + pe.cost_center = doc.get("cost_center") pe.posting_date = nowdate() pe.mode_of_payment = doc.get("mode_of_payment") pe.party_type = party_type @@ -912,4 +929,12 @@ def get_paid_amount(dt, dn, party_type, party, account, due_date): and {dr_or_cr} > 0 """.format(dr_or_cr=dr_or_cr), (dt, dn, party_type, party, account, due_date)) - return paid_amount[0][0] if paid_amount else 0 \ No newline at end of file + return paid_amount[0][0] if paid_amount else 0 + +@frappe.whitelist() +def get_party_and_account_balance(company, date, paid_from, paid_to=None, ptype=None, pty=None, cost_center=None): + return frappe._dict({ + "party_balance": get_balance_on(party_type=ptype, party=pty, cost_center=cost_center), + "paid_from_account_balance": get_balance_on(paid_from, date, cost_center=cost_center), + "paid_to_account_balance": get_balance_on(paid_to, date=date, cost_center=cost_center) + }) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 24082358d9..a7ab1754bf 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -8,8 +8,8 @@ import unittest from frappe.utils import flt, nowdate from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, InvalidPaymentEntry -from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice -from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice, create_sales_invoice_against_cost_center +from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice, make_purchase_invoice_against_cost_center from erpnext.hr.doctype.expense_claim.test_expense_claim import make_expense_claim test_dependencies = ["Item"] @@ -322,7 +322,7 @@ class TestPaymentEntry(unittest.TestCase): self.assertTrue(gl_entries) - for i, gle in enumerate(gl_entries): + for gle in gl_entries: self.assertEqual(expected_gle[gle.account][0], gle.account) self.assertEqual(expected_gle[gle.account][1], gle.debit) self.assertEqual(expected_gle[gle.account][2], gle.credit) @@ -394,3 +394,176 @@ class TestPaymentEntry(unittest.TestCase): outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 0) + + def test_payment_entry_against_sales_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center for BS Account - _TC" + create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") + + si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC") + + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") + self.assertEqual(pe.cost_center, si.cost_center) + + pe.reference_no = "112211-1" + pe.reference_date = nowdate() + pe.paid_to = "_Test Bank - _TC" + pe.paid_amount = si.grand_total + pe.insert() + pe.submit() + + expected_values = { + "_Test Bank - _TC": { + "cost_center": cost_center + }, + "Debtors - _TC": { + "cost_center": cost_center + } + } + + gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s + order by account asc""", pe.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + + def test_payment_entry_against_sales_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + si = create_sales_invoice(debit_to="Debtors - _TC") + + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") + + pe.reference_no = "112211-2" + pe.reference_date = nowdate() + pe.paid_to = "_Test Bank - _TC" + pe.paid_amount = si.grand_total + pe.insert() + pe.submit() + + gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s + order by account asc""", pe.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(gle.cost_center, None) + + def test_payment_entry_against_purchase_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center for BS Account - _TC" + create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") + + pi = make_purchase_invoice_against_cost_center(cost_center=cost_center, credit_to="Creditors - _TC") + + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") + self.assertEqual(pe.cost_center, pi.cost_center) + + pe.reference_no = "112222-1" + pe.reference_date = nowdate() + pe.paid_from = "_Test Bank - _TC" + pe.paid_amount = pi.grand_total + pe.insert() + pe.submit() + + expected_values = { + "_Test Bank - _TC": { + "cost_center": cost_center + }, + "Creditors - _TC": { + "cost_center": cost_center + } + } + + gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s + order by account asc""", pe.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + + def test_payment_entry_against_purchase_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + pi = make_purchase_invoice(credit_to="Creditors - _TC") + + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") + + pe.reference_no = "112222-2" + pe.reference_date = nowdate() + pe.paid_from = "_Test Bank - _TC" + pe.paid_amount = pi.grand_total + pe.insert() + pe.submit() + + gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s + order by account asc""", pe.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(gle.cost_center, None) + + def test_payment_entry_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + from erpnext.accounts.utils import get_balance_on + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center for BS Account - _TC" + create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") + + si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC") + + account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=si.cost_center) + party_balance = get_balance_on(party_type="Customer", party=si.customer, cost_center=si.cost_center) + party_account_balance = get_balance_on(si.debit_to, cost_center=si.cost_center) + + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") + pe.reference_no = "112211-1" + pe.reference_date = nowdate() + pe.paid_to = "_Test Bank - _TC" + pe.paid_amount = si.grand_total + pe.insert() + pe.submit() + + expected_account_balance = account_balance + si.grand_total + expected_party_balance = party_balance - si.grand_total + expected_party_account_balance = party_account_balance - si.grand_total + + account_balance = get_balance_on(account=pe.paid_to, cost_center=pe.cost_center) + party_balance = get_balance_on(party_type="Customer", party=pe.party, cost_center=pe.cost_center) + party_account_balance = get_balance_on(account=pe.paid_from, cost_center=pe.cost_center) + + self.assertEqual(pe.cost_center, si.cost_center) + self.assertEqual(expected_account_balance, account_balance) + self.assertEqual(expected_party_balance, party_balance) + self.assertEqual(expected_party_account_balance, party_account_balance) + + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 7a1a182331..2e82c017af 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -525,4 +525,4 @@ frappe.ui.form.on("Purchase Invoice", { } frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes"); } -}) \ No newline at end of file +}) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 2dd6e17892..6c0ee8b1bf 100755 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -379,6 +379,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cost_center", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Cost Center", + "length": 0, + "no_copy": 0, + "options": "Cost Center", + "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, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 266049063a..273a6e4686 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -389,6 +389,7 @@ class PurchaseInvoice(BuyingController): if self.party_account_currency==self.company_currency else grand_total, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, + "cost_center": self.cost_center }, self.party_account_currency) ) @@ -472,7 +473,8 @@ class PurchaseInvoice(BuyingController): "account": self.stock_received_but_not_billed, "against": self.supplier, "debit": flt(item.item_tax_amount, item.precision("item_tax_amount")), - "remarks": self.remarks or "Accounting Entry for Stock" + "remarks": self.remarks or "Accounting Entry for Stock", + "cost_center": self.cost_center }) ) @@ -500,7 +502,8 @@ class PurchaseInvoice(BuyingController): "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "debit": base_asset_amount, "debit_in_account_currency": (base_asset_amount - if asset_rbnb_currency == self.company_currency else asset_amount) + if asset_rbnb_currency == self.company_currency else asset_amount), + "cost_center": item.cost_center })) if item.item_tax_amount: @@ -526,7 +529,8 @@ class PurchaseInvoice(BuyingController): "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "debit": base_asset_amount, "debit_in_account_currency": (base_asset_amount - if cwip_account_currency == self.company_currency else asset_amount) + if cwip_account_currency == self.company_currency else asset_amount), + "cost_center": self.cost_center })) if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): @@ -626,6 +630,7 @@ class PurchaseInvoice(BuyingController): if self.party_account_currency==self.company_currency else self.paid_amount, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, + "cost_center": self.cost_center }, self.party_account_currency) ) @@ -635,7 +640,8 @@ class PurchaseInvoice(BuyingController): "against": self.supplier, "credit": self.base_paid_amount, "credit_in_account_currency": self.base_paid_amount \ - if bank_account_currency==self.company_currency else self.paid_amount + if bank_account_currency==self.company_currency else self.paid_amount, + "cost_center": self.cost_center }, bank_account_currency) ) @@ -656,6 +662,7 @@ class PurchaseInvoice(BuyingController): if self.party_account_currency==self.company_currency else self.write_off_amount, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, + "cost_center": self.cost_center }, self.party_account_currency) ) gl_entries.append( @@ -665,7 +672,7 @@ class PurchaseInvoice(BuyingController): "credit": flt(self.base_write_off_amount), "credit_in_account_currency": self.base_write_off_amount \ if write_off_account_currency==self.company_currency else self.write_off_amount, - "cost_center": self.write_off_cost_center + "cost_center": self.cost_center or self.write_off_cost_center }) ) @@ -680,7 +687,7 @@ class PurchaseInvoice(BuyingController): "against": self.supplier, "debit_in_account_currency": self.rounding_adjustment, "debit": self.base_rounding_adjustment, - "cost_center": round_off_cost_center, + "cost_center": self.cost_center or round_off_cost_center, } )) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 8cf6b1af3a..c8c23c74e9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -790,6 +790,66 @@ class TestPurchaseInvoice(unittest.TestCase): pi_doc = frappe.get_doc('Purchase Invoice', pi.name) self.assertEqual(pi_doc.outstanding_amount, 0) + def test_purchase_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center for BS Account - _TC" + create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") + + pi = make_purchase_invoice_against_cost_center(cost_center=cost_center, credit_to="Creditors - _TC") + self.assertEqual(pi.cost_center, cost_center) + + expected_values = { + "Creditors - _TC": { + "cost_center": cost_center + }, + "_Test Account Cost for Goods Sold - _TC": { + "cost_center": cost_center + } + } + + gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s + order by account asc""", pi.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + + def test_purchase_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + cost_center = "_Test Cost Center - _TC" + pi = make_purchase_invoice(credit_to="Creditors - _TC") + + expected_values = { + "Creditors - _TC": { + "cost_center": None + }, + "_Test Account Cost for Goods Sold - _TC": { + "cost_center": cost_center + } + } + + gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s + order by account asc""", pi.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + def unlink_payment_on_cancel_of_invoice(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") accounts_settings.unlink_payment_on_cancellation_of_invoice = enable @@ -839,4 +899,50 @@ def make_purchase_invoice(**args): pi.submit() return pi -test_records = frappe.get_test_records('Purchase Invoice') \ No newline at end of file +def make_purchase_invoice_against_cost_center(**args): + pi = frappe.new_doc("Purchase Invoice") + args = frappe._dict(args) + pi.posting_date = args.posting_date or today() + if args.posting_time: + pi.posting_time = args.posting_time + if args.update_stock: + pi.update_stock = 1 + if args.is_paid: + pi.is_paid = 1 + + if args.cash_bank_account: + pi.cash_bank_account=args.cash_bank_account + + pi.company = args.company or "_Test Company" + pi.cost_center = args.cost_center or "_Test Cost Center - _TC" + pi.supplier = args.supplier or "_Test Supplier" + pi.currency = args.currency or "INR" + pi.conversion_rate = args.conversion_rate or 1 + pi.is_return = args.is_return + pi.is_return = args.is_return + pi.credit_to = args.return_against or "Creditors - _TC" + pi.is_subcontracted = args.is_subcontracted or "No" + pi.supplier_warehouse = "_Test Warehouse 1 - _TC" + + pi.append("items", { + "item_code": args.item or args.item_code or "_Test Item", + "warehouse": args.warehouse or "_Test Warehouse - _TC", + "qty": args.qty or 5, + "received_qty": args.received_qty or 0, + "rejected_qty": args.rejected_qty or 0, + "rate": args.rate or 50, + "conversion_factor": 1.0, + "serial_no": args.serial_no, + "stock_uom": "_Test UOM", + "cost_center": args.cost_center or "_Test Cost Center - _TC", + "project": args.project, + "rejected_warehouse": args.rejected_warehouse or "", + "rejected_serial_no": args.rejected_serial_no or "" + }) + if not args.do_not_save: + pi.insert() + if not args.do_not_submit: + pi.submit() + return pi + +test_records = frappe.get_test_records('Purchase Invoice') diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 05b83005ad..a3f90cdf45 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -447,6 +447,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cost_center", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Cost Center", + "length": 0, + "no_copy": 0, + "options": "Cost Center", + "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, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 031d04a08d..4eeedac630 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -735,7 +735,8 @@ class SalesInvoice(SellingController): "debit_in_account_currency": grand_total_in_company_currency \ if self.party_account_currency==self.company_currency else grand_total, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, - "against_voucher_type": self.doctype + "against_voucher_type": self.doctype, + "cost_center": self.cost_center }, self.party_account_currency) ) @@ -796,13 +797,14 @@ class SalesInvoice(SellingController): "against": "Expense account - " + cstr(self.loyalty_redemption_account) + " for the Loyalty Program", "credit": self.loyalty_amount, "against_voucher": self.return_against if cint(self.is_return) else self.name, - "against_voucher_type": self.doctype + "against_voucher_type": self.doctype, + "cost_center": self.cost_center }) ) gl_entries.append( self.get_gl_dict({ "account": self.loyalty_redemption_account, - "cost_center": self.loyalty_redemption_cost_center, + "cost_center": self.cost_center or self.loyalty_redemption_cost_center, "against": self.customer, "debit": self.loyalty_amount, "remark": "Loyalty Points redeemed by the customer" @@ -826,6 +828,7 @@ class SalesInvoice(SellingController): else payment_mode.amount, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, + "cost_center": self.cost_center }, self.party_account_currency) ) @@ -837,7 +840,8 @@ class SalesInvoice(SellingController): "debit": payment_mode.base_amount, "debit_in_account_currency": payment_mode.base_amount \ if payment_mode_account_currency==self.company_currency \ - else payment_mode.amount + else payment_mode.amount, + "cost_center": self.cost_center }, payment_mode_account_currency) ) @@ -854,7 +858,8 @@ class SalesInvoice(SellingController): "debit_in_account_currency": flt(self.base_change_amount) \ if self.party_account_currency==self.company_currency else flt(self.change_amount), "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, - "against_voucher_type": self.doctype + "against_voucher_type": self.doctype, + "cost_center": self.cost_center }, self.party_account_currency) ) @@ -862,7 +867,8 @@ class SalesInvoice(SellingController): self.get_gl_dict({ "account": self.account_for_change_amount, "against": self.customer, - "credit": self.base_change_amount + "credit": self.base_change_amount, + "cost_center": self.cost_center }) ) else: @@ -884,7 +890,8 @@ class SalesInvoice(SellingController): "credit_in_account_currency": self.base_write_off_amount \ if self.party_account_currency==self.company_currency else self.write_off_amount, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, - "against_voucher_type": self.doctype + "against_voucher_type": self.doctype, + "cost_center": self.cost_center }, self.party_account_currency) ) gl_entries.append( @@ -894,7 +901,7 @@ class SalesInvoice(SellingController): "debit": self.base_write_off_amount, "debit_in_account_currency": self.base_write_off_amount \ if write_off_account_currency==self.company_currency else self.write_off_amount, - "cost_center": self.write_off_cost_center or default_cost_center + "cost_center": self.cost_center or self.write_off_cost_center or default_cost_center }, write_off_account_currency) ) @@ -909,7 +916,7 @@ class SalesInvoice(SellingController): "against": self.customer, "credit_in_account_currency": self.base_rounding_adjustment, "credit": self.base_rounding_adjustment, - "cost_center": round_off_cost_center, + "cost_center": self.cost_center or round_off_cost_center, } )) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 468ed9f8c6..b0fbc98c4e 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1438,6 +1438,66 @@ class TestSalesInvoice(unittest.TestCase): si_doc = frappe.get_doc('Sales Invoice', si.name) self.assertEqual(si_doc.outstanding_amount, 0) + def test_sales_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center for BS Account - _TC" + create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") + + si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC") + self.assertEqual(si.cost_center, cost_center) + + expected_values = { + "Debtors - _TC": { + "cost_center": cost_center + }, + "Sales - _TC": { + "cost_center": cost_center + } + } + + gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s + order by account asc""", si.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + + def test_sales_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center - _TC" + si = create_sales_invoice(debit_to="Debtors - _TC") + + expected_values = { + "Debtors - _TC": { + "cost_center": None + }, + "Sales - _TC": { + "cost_center": cost_center + } + } + + gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s + order by account asc""", si.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") args = frappe._dict(args) @@ -1478,6 +1538,48 @@ def create_sales_invoice(**args): return si +def create_sales_invoice_against_cost_center(**args): + si = frappe.new_doc("Sales Invoice") + args = frappe._dict(args) + if args.posting_date: + si.set_posting_time = 1 + si.posting_date = args.posting_date or nowdate() + + si.company = args.company or "_Test Company" + si.cost_center = args.cost_center or "_Test Cost Center - _TC" + si.customer = args.customer or "_Test Customer" + si.debit_to = args.debit_to or "Debtors - _TC" + si.update_stock = args.update_stock + si.is_pos = args.is_pos + si.is_return = args.is_return + si.return_against = args.return_against + si.currency=args.currency or "INR" + si.conversion_rate = args.conversion_rate or 1 + + si.append("items", { + "item_code": args.item or args.item_code or "_Test Item", + "gst_hsn_code": "999800", + "warehouse": args.warehouse or "_Test Warehouse - _TC", + "qty": args.qty or 1, + "rate": args.rate or 100, + "income_account": "Sales - _TC", + "expense_account": "Cost of Goods Sold - _TC", + "cost_center": args.cost_center or "_Test Cost Center - _TC", + "serial_no": args.serial_no + }) + + if not args.do_not_save: + si.insert() + if not args.do_not_submit: + si.submit() + else: + si.payment_schedule = [] + else: + si.payment_schedule = [] + + return si + + test_dependencies = ["Journal Entry", "Contact", "Address"] test_records = frappe.get_test_records('Sales Invoice') diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 602e671ba6..df69b51741 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -17,6 +17,21 @@ frappe.query_reports["General Ledger"] = { "fieldtype": "Link", "options": "Finance Book" }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center", + "get_query": function() { + var company = frappe.query_report.get_filter_value('company'); + return { + "doctype": "Cost Center", + "filters": { + "company": company, + } + } + } + }, { "fieldname":"from_date", "label": __("From Date"), diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 2d174ffd53..4524367e3c 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -154,6 +154,11 @@ def get_conditions(filters): conditions.append("""account in (select name from tabAccount where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) + if filters.get("cost_center"): + lft, rgt = frappe.db.get_value("Cost Center", filters["cost_center"], ["lft", "rgt"]) + conditions.append("""cost_center in (select name from `tabCost Center` + where lft>=%s and rgt<=%s and docstatus<2)""" % (lft, rgt)) + if filters.get("voucher_no"): conditions.append("voucher_no=%(voucher_no)s") diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 8e95d8cc9c..c09fa71575 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -12,6 +12,21 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "default": frappe.defaults.get_user_default("Company"), "reqd": 1 }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center", + "get_query": function() { + var company = frappe.query_report.get_filter_value('company'); + return { + "doctype": "Cost Center", + "filters": { + "company": company, + } + } + } + }, { "fieldname": "fiscal_year", "label": __("Fiscal Year"), diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index ae128a7b3a..6fbe97de3e 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -84,7 +84,7 @@ def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None): throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year)) @frappe.whitelist() -def get_balance_on(account=None, date=None, party_type=None, party=None, company=None, in_account_currency=True): +def get_balance_on(account=None, date=None, party_type=None, party=None, company=None, in_account_currency=True, cost_center=None): if not account and frappe.form_dict.get("account"): account = frappe.form_dict.get("account") if not date and frappe.form_dict.get("date"): @@ -93,6 +93,9 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company party_type = frappe.form_dict.get("party_type") if not party and frappe.form_dict.get("party"): party = frappe.form_dict.get("party") + if not cost_center and frappe.form_dict.get("cost_center"): + cost_center = frappe.form_dict.get("cost_center") + cond = [] if date: @@ -113,17 +116,36 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company # hence, assuming balance as 0.0 return 0.0 + allow_cost_center_in_entry_of_bs_account = get_allow_cost_center_in_entry_of_bs_account() + + if cost_center and allow_cost_center_in_entry_of_bs_account: + cc = frappe.get_doc("Cost Center", cost_center) + if cc.is_group: + cond.append(""" exists ( + select 1 from `tabCost Center` cc where cc.name = gle.cost_center + and cc.lft >= %s and cc.rgt <= %s + )""" % (cc.lft, cc.rgt)) + + else: + cond.append("""gle.cost_center = "%s" """ % (frappe.db.escape(cost_center, percent=False), )) + + if account: + acc = frappe.get_doc("Account", account) if not frappe.flags.ignore_account_permission: acc.check_permission("read") - # for pl accounts, get balance within a fiscal year - if acc.report_type == 'Profit and Loss': + + if not allow_cost_center_in_entry_of_bs_account and acc.report_type == 'Profit and Loss': + # for pl accounts, get balance within a fiscal year + cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \ + % year_start_date) + elif allow_cost_center_in_entry_of_bs_account: + # for all accounts, get balance within a fiscal year if maintain cost center in balance account is checked cond.append("posting_date >= '%s' and voucher_type != 'Period Closing Voucher'" \ % year_start_date) - # different filter for group and ledger - improved performance if acc.is_group: cond.append("""exists ( @@ -830,3 +852,10 @@ def get_coa(doctype, parent, is_root, chart=None): accounts = [d for d in accounts if d['parent_account']==parent] return accounts + +def get_allow_cost_center_in_entry_of_bs_account(): + def generator(): + return cint(frappe.db.get_value('Accounts Settings', None, 'allow_cost_center_in_entry_of_bs_account')) + return frappe.local_cache("get_allow_cost_center_in_entry_of_bs_account", (), generator, regenerate_if_none=True) + + diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 07ddcb42db..b3ba053544 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -343,7 +343,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ weight_per_unit: item.weight_per_unit, weight_uom: item.weight_uom, uom : item.uom, - pos_profile: me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '' + pos_profile: me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '', + cost_center: item.cost_center } }, diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 8648687e47..f313ed1641 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -78,6 +78,21 @@ function get_filters(){ "fieldtype": "Link", "options": "Finance Book" }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center", + "get_query": function() { + var company = frappe.query_report.get_filter_value('company'); + return { + "doctype": "Cost Center", + "filters": { + "company": company, + } + }; + } + }, { "fieldname":"from_fiscal_year", "label": __("Start Year"), diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 682577bab0..434b58c4b6 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -201,7 +201,18 @@ $.extend(erpnext.utils, { } else { return options[0]; } - } + }, + copy_parent_value_in_all_row: function(doc, dt, dn, table_fieldname, fieldname, parent_fieldname) { + var d = locals[dt][dn]; + if(d[fieldname]){ + var cl = doc[table_fieldname] || []; + for(var i = 0; i < cl.length; i++) { + cl[i][fieldname] = doc[parent_fieldname]; + } + } + refresh_field(table_fieldname); + }, + }); erpnext.utils.select_alternate_items = function(opts) { diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 36836954f8..026d83cf7f 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -569,6 +569,74 @@ class TestDeliveryNote(unittest.TestCase): dt = make_delivery_trip(dn.name) self.assertEqual(dn.name, dt.delivery_stops[0].delivery_note) + def test_delivery_note_for_enable_allow_cost_center_in_entry_of_bs_account(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center for BS Account - _TC" + create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") + + company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') + set_perpetual_inventory(1, company) + + set_valuation_method("_Test Item", "FIFO") + + make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100) + + stock_in_hand_account = get_inventory_account('_Test Company') + dn = create_delivery_note(cost_center=cost_center) + + gl_entries = get_gl_entries("Delivery Note", dn.name) + self.assertTrue(gl_entries) + + expected_values = { + "Cost of Goods Sold - _TC": { + "cost_center": cost_center + }, + stock_in_hand_account: { + "cost_center": cost_center + } + } + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + set_perpetual_inventory(0, company) + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + + def test_delivery_note_for_disable_allow_cost_center_in_entry_of_bs_account(self): + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + cost_center = "_Test Cost Center - _TC" + + company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') + set_perpetual_inventory(1, company) + + set_valuation_method("_Test Item", "FIFO") + + make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100) + + stock_in_hand_account = get_inventory_account('_Test Company') + dn = create_delivery_note() + + gl_entries = get_gl_entries("Delivery Note", dn.name) + + self.assertTrue(gl_entries) + expected_values = { + "Cost of Goods Sold - _TC": { + "cost_center": cost_center + }, + stock_in_hand_account: { + "cost_center": None + } + } + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + set_perpetual_inventory(0, company) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") args = frappe._dict(args) @@ -589,7 +657,7 @@ def create_delivery_note(**args): "rate": args.rate or 100, "conversion_factor": 1.0, "expense_account": "Cost of Goods Sold - _TC", - "cost_center": "_Test Cost Center - _TC", + "cost_center": args.cost_center or "_Test Cost Center - _TC", "serial_no": args.serial_no, "target_warehouse": args.target_warehouse }) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 5c370d3bec..e482f58da3 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -470,4 +470,4 @@ def make_purchase_return(source_name, target_doc=None): @frappe.whitelist() def update_purchase_receipt_status(docname, status): pr = frappe.get_doc("Purchase Receipt", docname) - pr.update_status(status) \ No newline at end of file + pr.update_status(status) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 6e2863ee0c..a2da9246a2 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -333,11 +333,78 @@ class TestPurchaseReceipt(unittest.TestCase): pr.cancel() serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or [] self.assertEquals(len(serial_nos), 0) - frappe.db.sql("delete from `tabLocation") + #frappe.db.sql("delete from `tabLocation") frappe.db.sql("delete from `tabAsset`") + def test_purchase_receipt_for_enable_allow_cost_center_in_entry_of_bs_account(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 + accounts_settings.save() + cost_center = "_Test Cost Center for BS Account - _TC" + create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") + + if not frappe.db.exists('Location', 'Test Location'): + frappe.get_doc({ + 'doctype': 'Location', + 'location_name': 'Test Location' + }).insert() + + pr = make_purchase_receipt(cost_center=cost_center) + set_perpetual_inventory(1, pr.company) + stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse) + gl_entries = get_gl_entries("Purchase Receipt", pr.name) + + self.assertTrue(gl_entries) + + expected_values = { + "Stock Received But Not Billed - _TC": { + "cost_center": cost_center + }, + stock_in_hand_account: { + "cost_center": cost_center + } + } + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + set_perpetual_inventory(0, pr.company) + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + + def test_purchase_receipt_for_disable_allow_cost_center_in_entry_of_bs_account(self): + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 + accounts_settings.save() + + if not frappe.db.exists('Location', 'Test Location'): + frappe.get_doc({ + 'doctype': 'Location', + 'location_name': 'Test Location' + }).insert() + + pr = make_purchase_receipt() + set_perpetual_inventory(1, pr.company) + stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse) + gl_entries = get_gl_entries("Purchase Receipt", pr.name) + + self.assertTrue(gl_entries) + + expected_values = { + "Stock Received But Not Billed - _TC": { + "cost_center": None + }, + stock_in_hand_account: { + "cost_center": None + } + } + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + set_perpetual_inventory(0, pr.company) + def get_gl_entries(voucher_type, voucher_no): - return frappe.db.sql("""select account, debit, credit + return frappe.db.sql("""select account, debit, credit, cost_center from `tabGL Entry` where voucher_type=%s and voucher_no=%s order by account desc""", (voucher_type, voucher_no), as_dict=1)