From 8e6249d361bb36a4fd1ef0cfa7333265bb8933d5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Aug 2024 11:35:15 +0530 Subject: [PATCH 01/11] feat: round off for opening entries (cherry picked from commit a5b228549c0c03517a53db8609f34fb6ab308445) --- erpnext/setup/doctype/company/company.json | 9 ++++++++- erpnext/setup/doctype/company/company.py | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 284bd2b7f22..4b07037ad3e 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -49,6 +49,7 @@ "default_cash_account", "default_receivable_account", "round_off_account", + "round_off_for_opening", "round_off_cost_center", "write_off_account", "exchange_gain_loss_account", @@ -801,6 +802,12 @@ "fieldtype": "Link", "label": "Default Operating Cost Account", "options": "Account" + }, + { + "fieldname": "round_off_for_opening", + "fieldtype": "Link", + "label": "Round Off for Opening", + "options": "Account" } ], "icon": "fa fa-building", @@ -808,7 +815,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2024-07-24 18:17:56.413971", + "modified": "2024-08-02 11:34:46.785377", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 8028b8e6af4..d781288c8bd 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -91,6 +91,7 @@ class Company(NestedSet): rgt: DF.Int round_off_account: DF.Link | None round_off_cost_center: DF.Link | None + round_off_for_opening: DF.Link | None sales_monthly_history: DF.SmallText | None series_for_depreciation_entry: DF.Data | None stock_adjustment_account: DF.Link | None From 9a3e9c4c9a627cac552a95ff880469ef3888c56f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Aug 2024 11:49:50 +0530 Subject: [PATCH 02/11] refactor: use separate round off for opening entries (cherry picked from commit 88e68168e36624d9ec31993c8f7a9b29add8c1ce) # Conflicts: # erpnext/accounts/general_ledger.py --- erpnext/accounts/doctype/account/account.json | 4 +- erpnext/accounts/doctype/account/account.py | 1 + .../purchase_invoice/purchase_invoice.py | 6 ++- .../doctype/sales_invoice/sales_invoice.py | 6 ++- erpnext/accounts/general_ledger.py | 52 ++++++++++++++++--- erpnext/controllers/accounts_controller.py | 6 ++- erpnext/setup/doctype/company/company.js | 1 + 7 files changed, 63 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index e87b59ea9cb..7b56444e635 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -121,7 +121,7 @@ "label": "Account Type", "oldfieldname": "account_type", "oldfieldtype": "Select", - "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary", + "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nRound Off for Opening\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary", "search_index": 1 }, { @@ -191,7 +191,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2024-06-27 16:23:04.444354", + "modified": "2024-08-19 15:19:11.095045", "modified_by": "Administrator", "module": "Accounts", "name": "Account", diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 2c876e09725..b510651e68f 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -60,6 +60,7 @@ class Account(NestedSet): "Payable", "Receivable", "Round Off", + "Round Off for Opening", "Stock", "Stock Adjustment", "Stock Received But Not Billed", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index b47e90eb77d..dc4051eecf4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1537,7 +1537,11 @@ class PurchaseInvoice(BuyingController): # eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2 # then base_rounding_adjustment becomes zero and error is thrown in GL Entry if not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment: - round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( + ( + round_off_account, + round_off_cost_center, + round_off_for_opening, + ) = get_round_off_account_and_cost_center( self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center ) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index eb43de47a54..d24717a614d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1633,7 +1633,11 @@ class SalesInvoice(SellingController): and self.base_rounding_adjustment and not self.is_internal_transfer() ): - round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( + ( + round_off_account, + round_off_cost_center, + round_off_for_opening, + ) = get_round_off_account_and_cost_center( self.company, "Sales Invoice", self.name, self.use_company_roundoff_cost_center ) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index f9b503675aa..b3d78284616 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -7,7 +7,12 @@ import copy import frappe from frappe import _ from frappe.model.meta import get_field_precision +<<<<<<< HEAD from frappe.utils import cint, flt, formatdate, getdate, now +======= +from frappe.utils import cint, flt, formatdate, get_link_to_form, getdate, now +from frappe.utils.dashboard import cache_source +>>>>>>> 88e68168e3 (refactor: use separate round off for opening entries) import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( @@ -496,16 +501,36 @@ def raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_ ) +def has_opening_entries(gl_map: list) -> bool: + for x in gl_map: + if x.is_opening == "Yes": + return True + return False + + def make_round_off_gle(gl_map, debit_credit_diff, precision): - round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( + round_off_account, round_off_cost_center, round_off_for_opening = get_round_off_account_and_cost_center( gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no ) round_off_gle = frappe._dict() round_off_account_exists = False + has_opening_entry = has_opening_entries(gl_map) + + if has_opening_entry: + if not round_off_for_opening: + frappe.throw( + _("Please set '{0}' in Company: {1}").format( + frappe.bold("Round Off for Opening"), get_link_to_form("Company", gl_map[0].company) + ) + ) + + account = round_off_for_opening + else: + account = round_off_account if gl_map[0].voucher_type != "Period Closing Voucher": for d in gl_map: - if d.account == round_off_account: + if d.account == account: round_off_gle = d if d.debit: debit_credit_diff -= flt(d.debit) - flt(d.credit) @@ -523,7 +548,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision): round_off_gle.update( { - "account": round_off_account, + "account": account, "debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0, "credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0, "debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0, @@ -537,6 +562,9 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision): } ) + if has_opening_entry: + round_off_gle.update({"is_opening": "Yes"}) + update_accounting_dimensions(round_off_gle) if not round_off_account_exists: gl_map.append(round_off_gle) @@ -561,8 +589,8 @@ def update_accounting_dimensions(round_off_gle): def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use_company_default=False): - round_off_account, round_off_cost_center = frappe.get_cached_value( - "Company", company, ["round_off_account", "round_off_cost_center"] + round_off_account, round_off_cost_center, round_off_for_opening = frappe.get_cached_value( + "Company", company, ["round_off_account", "round_off_cost_center", "round_off_for_opening"] ) or [None, None] # Use expense account as fallback @@ -578,12 +606,20 @@ def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use round_off_cost_center = parent_cost_center if not round_off_account: - frappe.throw(_("Please mention Round Off Account in Company")) + frappe.throw( + _("Please mention '{0}' in Company: {1}").format( + frappe.bold("Round Off Account"), get_link_to_form("Company", company) + ) + ) if not round_off_cost_center: - frappe.throw(_("Please mention Round Off Cost Center in Company")) + frappe.throw( + _("Please mention '{0}' in Company: {1}").format( + frappe.bold("Round Off Cost Center"), get_link_to_form("Company", company) + ) + ) - return round_off_account, round_off_cost_center + return round_off_account, round_off_cost_center, round_off_for_opening def make_reverse_gl_entries( diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b14cf428c53..b4b23dd5f4c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1298,7 +1298,11 @@ class AccountsController(TransactionBase): d.exchange_gain_loss = difference def make_precision_loss_gl_entry(self, gl_entries): - round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( + ( + round_off_account, + round_off_cost_center, + round_off_for_opening, + ) = get_round_off_account_and_cost_center( self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center ) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index f14057a272e..52ff21dc407 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -252,6 +252,7 @@ erpnext.company.setup_queries = function (frm) { ["default_expense_account", { root_type: "Expense" }], ["default_income_account", { root_type: "Income" }], ["round_off_account", { root_type: "Expense" }], + ["round_off_for_opening", { root_type: "Liability" }], ["write_off_account", { root_type: "Expense" }], ["default_deferred_expense_account", {}], ["default_deferred_revenue_account", {}], From b28ff25180ae95fc737708c50b4917f18f82cb1f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 29 Aug 2024 17:37:32 +0530 Subject: [PATCH 03/11] chore: default should return 3 elements (cherry picked from commit fc46ebcd7c9a3628ddd7b7a1f284d2c2d9d6b7f9) --- erpnext/accounts/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index b3d78284616..c2fbdcc95f1 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -591,7 +591,7 @@ def update_accounting_dimensions(round_off_gle): def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use_company_default=False): round_off_account, round_off_cost_center, round_off_for_opening = frappe.get_cached_value( "Company", company, ["round_off_account", "round_off_cost_center", "round_off_for_opening"] - ) or [None, None] + ) or [None, None, None] # Use expense account as fallback if not round_off_account: From 186b646dee3038cb78311ec99e6f5f292c50a21d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 30 Aug 2024 16:08:43 +0530 Subject: [PATCH 04/11] refactor: handle opening round off from sales invoice (cherry picked from commit 96e3c2ad1061af44b044fa64b0af16004d0d732e) --- .../doctype/sales_invoice/sales_invoice.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d24717a614d..8baa36475da 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1641,6 +1641,21 @@ class SalesInvoice(SellingController): self.company, "Sales Invoice", self.name, self.use_company_roundoff_cost_center ) + if self.is_opening == "Yes" and self.rounding_adjustment: + if not round_off_for_opening: + frappe.throw( + _( + "Opening Invoice has rounding adjustment of {0}.

'{1}' account is required to post these values. Please set it in Company: {2}.

Or, '{3}' can be enabled to not post any rounding adjustment." + ).format( + frappe.bold(self.rounding_adjustment), + frappe.bold("Round Off for Opening"), + get_link_to_form("Company", self.company), + frappe.bold("Disable Rounded Total"), + ) + ) + else: + round_off_account = round_off_for_opening + gl_entries.append( self.get_gl_dict( { From 820692f24647ca6f47370594160cb1e204c66aa7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 30 Aug 2024 17:55:02 +0530 Subject: [PATCH 05/11] test: rounding adjustment validation and posting (cherry picked from commit 5021c7ca2c3bb5f758d00c65eab1dcfec692d2a4) # Conflicts: # erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py --- .../sales_invoice/test_sales_invoice.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 56f90ae8cd4..d9d7d5fa3ba 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3924,6 +3924,7 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(res), 1) self.assertEqual(res[0][0], pos_return.return_against) +<<<<<<< HEAD @change_settings("Accounts Settings", {"enable_common_party_accounting": True}) def test_common_party_with_foreign_currency_jv(self): from erpnext.accounts.doctype.account.test_account import create_account @@ -4032,6 +4033,57 @@ class TestSalesInvoice(FrappeTestCase): ) self.assertTrue(all([x == "Credit Note" for x in gl_entries])) +======= + def test_validation_on_opening_invoice_with_rounding(self): + si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) + si.is_opening = "Yes" + si.items[0].income_account = "Temporary Opening - _TC" + si.save() + self.assertRaises(frappe.ValidationError, si.submit) + + def test_opening_invoice_with_rounding_adjustment(self): + si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) + si.is_opening = "Yes" + si.items[0].income_account = "Temporary Opening - _TC" + si.save() + + liability_root = frappe.db.get_all( + "Account", + filters={"company": si.company, "root_type": "Liability", "disabled": 0}, + order_by="lft", + limit=1, + )[0] + + # setup round off account + company = frappe.get_doc("Company", si.company) + if acc := frappe.db.exists( + "Account", + { + "account_name": "Round Off for Opening", + "account_type": "Round Off for Opening", + "company": si.company, + }, + ): + company.round_off_for_opening = acc + else: + acc = frappe.new_doc("Account") + acc.company = si.company + acc.parent_account = liability_root.name + acc.account_name = "Round Off for Opening" + acc.account_type = "Round Off for Opening" + acc.save() + company.round_off_for_opening = acc.name + company.save() + + si.reload() + si.submit() + res = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": si.name, "is_opening": "Yes"}, + fields=["account", "debit", "credit", "is_opening"], + ) + self.assertEqual(len(res), 3) +>>>>>>> 5021c7ca2c (test: rounding adjustment validation and posting) def set_advance_flag(company, flag, default_account): From da2f6a045aa637514754bbd342bac48ce5847521 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Sep 2024 16:33:55 +0530 Subject: [PATCH 06/11] test: opening round off with inclusive tax (cherry picked from commit 79267358d0fc95a10c9bcd03307ba1284e561526) --- .../sales_invoice/test_sales_invoice.py | 106 +++++++++++++----- 1 file changed, 79 insertions(+), 27 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d9d7d5fa3ba..15bc234fec1 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4041,39 +4041,40 @@ class TestSalesInvoice(FrappeTestCase): si.save() self.assertRaises(frappe.ValidationError, si.submit) + def _create_opening_roundoff_account(self, company_name): + liability_root = frappe.db.get_all( + "Account", + filters={"company": company_name, "root_type": "Liability", "disabled": 0}, + order_by="lft", + limit=1, + )[0] + + # setup round off account + if acc := frappe.db.exists( + "Account", + { + "account_name": "Round Off for Opening", + "account_type": "Round Off for Opening", + "company": company_name, + }, + ): + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc) + else: + acc = frappe.new_doc("Account") + acc.company = company_name + acc.parent_account = liability_root.name + acc.account_name = "Round Off for Opening" + acc.account_type = "Round Off for Opening" + acc.save() + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name) + def test_opening_invoice_with_rounding_adjustment(self): si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) si.is_opening = "Yes" si.items[0].income_account = "Temporary Opening - _TC" si.save() - liability_root = frappe.db.get_all( - "Account", - filters={"company": si.company, "root_type": "Liability", "disabled": 0}, - order_by="lft", - limit=1, - )[0] - - # setup round off account - company = frappe.get_doc("Company", si.company) - if acc := frappe.db.exists( - "Account", - { - "account_name": "Round Off for Opening", - "account_type": "Round Off for Opening", - "company": si.company, - }, - ): - company.round_off_for_opening = acc - else: - acc = frappe.new_doc("Account") - acc.company = si.company - acc.parent_account = liability_root.name - acc.account_name = "Round Off for Opening" - acc.account_type = "Round Off for Opening" - acc.save() - company.round_off_for_opening = acc.name - company.save() + self._create_opening_roundoff_account(si.company) si.reload() si.submit() @@ -4085,6 +4086,57 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(res), 3) >>>>>>> 5021c7ca2c (test: rounding adjustment validation and posting) + def _create_opening_invoice_with_inclusive_tax(self): + si = create_sales_invoice(qty=1, rate=90, do_not_submit=True) + si.is_opening = "Yes" + si.items[0].income_account = "Temporary Opening - _TC" + item_template = si.items[0].as_dict() + item_template.name = None + item_template.rate = 55 + si.append("items", item_template) + si.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Testing...", + "rate": 5, + "included_in_print_rate": True, + }, + ) + # there will be 0.01 precision loss between Dr and Cr + # caused by 'included_in_print_tax' option + si.save() + return si + + def test_rounding_validation_for_opening_with_inclusive_tax(self): + si = self._create_opening_invoice_with_inclusive_tax() + # 'Round Off for Opening' not set in Company master + # Ledger level validation must be thrown + self.assertRaises(frappe.ValidationError, si.submit) + + def test_ledger_entries_on_opening_invoice_with_rounding_loss_by_inclusive_tax(self): + si = self._create_opening_invoice_with_inclusive_tax() + # 'Round Off for Opening' is set in Company master + self._create_opening_roundoff_account(si.company) + + si.submit() + actual = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": si.name, "is_opening": "Yes"}, + fields=["account", "debit", "credit", "is_opening"], + order_by="account,debit", + ) + expected = [ + {"account": "_Test Account Service Tax - _TC", "debit": 0.0, "credit": 6.9, "is_opening": "Yes"}, + {"account": "Debtors - _TC", "debit": 145.0, "credit": 0.0, "is_opening": "Yes"}, + {"account": "Round Off for Opening - _TC", "debit": 0.0, "credit": 0.01, "is_opening": "Yes"}, + {"account": "Temporary Opening - _TC", "debit": 0.0, "credit": 138.09, "is_opening": "Yes"}, + ] + self.assertEqual(len(actual), 4) + self.assertEqual(expected, actual) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 7eb4b422808e5df5353e169279a2356a454cd69f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Sep 2024 17:52:28 +0530 Subject: [PATCH 07/11] refactor: filter on account_type (cherry picked from commit 193ea9ad8f29358820fff45698772ea5adeffcfa) --- erpnext/setup/doctype/company/company.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 52ff21dc407..72d28a705ad 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -252,7 +252,7 @@ erpnext.company.setup_queries = function (frm) { ["default_expense_account", { root_type: "Expense" }], ["default_income_account", { root_type: "Income" }], ["round_off_account", { root_type: "Expense" }], - ["round_off_for_opening", { root_type: "Liability" }], + ["round_off_for_opening", { root_type: "Liability", account_type: "Round Off for Opening" }], ["write_off_account", { root_type: "Expense" }], ["default_deferred_expense_account", {}], ["default_deferred_revenue_account", {}], From 8bec67cbcfb514dda6ba66f6bf0df07841bb8026 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Sep 2024 17:53:02 +0530 Subject: [PATCH 08/11] refactor: handle opening round off on purchase invoice (cherry picked from commit a5d6a25a9654ebe7f91285b9266e9b7cd9797545) --- .../doctype/purchase_invoice/purchase_invoice.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index dc4051eecf4..ebc4efc08a0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1545,6 +1545,21 @@ class PurchaseInvoice(BuyingController): self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center ) + if self.is_opening == "Yes" and self.rounding_adjustment: + if not round_off_for_opening: + frappe.throw( + _( + "Opening Invoice has rounding adjustment of {0}.

'{1}' account is required to post these values. Please set it in Company: {2}.

Or, '{3}' can be enabled to not post any rounding adjustment." + ).format( + frappe.bold(self.rounding_adjustment), + frappe.bold("Round Off for Opening"), + get_link_to_form("Company", self.company), + frappe.bold("Disable Rounded Total"), + ) + ) + else: + round_off_account = round_off_for_opening + gl_entries.append( self.get_gl_dict( { From 9bfd5cdb2b8999fd5e2140c79338542f135f77ce Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Sep 2024 11:04:26 +0530 Subject: [PATCH 09/11] test: opening purchase invoice with rounding adjustment (cherry picked from commit b7edc6dea908b887041998801a3d7724fc8777f1) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py --- .../purchase_invoice/test_purchase_invoice.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index f5835deb0d0..f234157d949 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2347,6 +2347,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) +<<<<<<< HEAD def test_last_purchase_rate(self): item = create_item("_Test Item For Last Purchase Rate from PI", is_stock_item=1) pi1 = make_purchase_invoice(item_code=item.item_code, qty=10, rate=100) @@ -2364,6 +2365,66 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): pi1.cancel() item.reload() self.assertEqual(item.last_purchase_rate, 0) +======= + def test_opening_invoice_rounding_adjustment_validation(self): + pi = make_purchase_invoice(do_not_save=1) + pi.items[0].rate = 99.98 + pi.items[0].qty = 1 + pi.items[0].expense_account = "Temporary Opening - _TC" + pi.is_opening = "Yes" + pi.save() + self.assertRaises(frappe.ValidationError, pi.submit) + + def _create_opening_roundoff_account(self, company_name): + liability_root = frappe.db.get_all( + "Account", + filters={"company": company_name, "root_type": "Liability", "disabled": 0}, + order_by="lft", + limit=1, + )[0] + + # setup round off account + if acc := frappe.db.exists( + "Account", + { + "account_name": "Round Off for Opening", + "account_type": "Round Off for Opening", + "company": company_name, + }, + ): + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc) + else: + acc = frappe.new_doc("Account") + acc.company = company_name + acc.parent_account = liability_root.name + acc.account_name = "Round Off for Opening" + acc.account_type = "Round Off for Opening" + acc.save() + frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name) + + def test_ledger_entries_of_opening_invoice_with_rounding_adjustment(self): + pi = make_purchase_invoice(do_not_save=1) + pi.items[0].rate = 99.98 + pi.items[0].qty = 1 + pi.items[0].expense_account = "Temporary Opening - _TC" + pi.is_opening = "Yes" + pi.save() + self._create_opening_roundoff_account(pi.company) + pi.submit() + actual = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pi.name, "is_opening": "Yes", "is_cancelled": False}, + fields=["account", "debit", "credit", "is_opening"], + order_by="account,debit", + ) + expected = [ + {"account": "Creditors - _TC", "debit": 0.0, "credit": 100.0, "is_opening": "Yes"}, + {"account": "Round Off for Opening - _TC", "debit": 0.02, "credit": 0.0, "is_opening": "Yes"}, + {"account": "Temporary Opening - _TC", "debit": 99.98, "credit": 0.0, "is_opening": "Yes"}, + ] + self.assertEqual(len(actual), 3) + self.assertEqual(expected, actual) +>>>>>>> b7edc6dea9 (test: opening purchase invoice with rounding adjustment) def set_advance_flag(company, flag, default_account): From ba79560c0c5f4b214c9b52061fac9762c3f6b363 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Sep 2024 11:05:34 +0530 Subject: [PATCH 10/11] refactor(test): filter for active ledger entries (cherry picked from commit cf11ac87fb7a9bb15518204900d776e06af60aaf) --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 15bc234fec1..adbab43001b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4124,7 +4124,7 @@ class TestSalesInvoice(FrappeTestCase): si.submit() actual = frappe.db.get_all( "GL Entry", - filters={"voucher_no": si.name, "is_opening": "Yes"}, + filters={"voucher_no": si.name, "is_opening": "Yes", "is_cancelled": False}, fields=["account", "debit", "credit", "is_opening"], order_by="account,debit", ) From 9598b1fc0fdf4c8be6145d0a1d4a9130635b595e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 14 Nov 2024 12:35:41 +0530 Subject: [PATCH 11/11] chore: resolve conflicts --- .../doctype/purchase_invoice/test_purchase_invoice.py | 4 +--- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 4 +--- erpnext/accounts/general_ledger.py | 5 ----- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index f234157d949..f0b51c32c05 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2347,7 +2347,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) -<<<<<<< HEAD def test_last_purchase_rate(self): item = create_item("_Test Item For Last Purchase Rate from PI", is_stock_item=1) pi1 = make_purchase_invoice(item_code=item.item_code, qty=10, rate=100) @@ -2365,7 +2364,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): pi1.cancel() item.reload() self.assertEqual(item.last_purchase_rate, 0) -======= + def test_opening_invoice_rounding_adjustment_validation(self): pi = make_purchase_invoice(do_not_save=1) pi.items[0].rate = 99.98 @@ -2424,7 +2423,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): ] self.assertEqual(len(actual), 3) self.assertEqual(expected, actual) ->>>>>>> b7edc6dea9 (test: opening purchase invoice with rounding adjustment) def set_advance_flag(company, flag, default_account): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index adbab43001b..90bec018257 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3924,7 +3924,6 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(res), 1) self.assertEqual(res[0][0], pos_return.return_against) -<<<<<<< HEAD @change_settings("Accounts Settings", {"enable_common_party_accounting": True}) def test_common_party_with_foreign_currency_jv(self): from erpnext.accounts.doctype.account.test_account import create_account @@ -4033,7 +4032,7 @@ class TestSalesInvoice(FrappeTestCase): ) self.assertTrue(all([x == "Credit Note" for x in gl_entries])) -======= + def test_validation_on_opening_invoice_with_rounding(self): si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True) si.is_opening = "Yes" @@ -4084,7 +4083,6 @@ class TestSalesInvoice(FrappeTestCase): fields=["account", "debit", "credit", "is_opening"], ) self.assertEqual(len(res), 3) ->>>>>>> 5021c7ca2c (test: rounding adjustment validation and posting) def _create_opening_invoice_with_inclusive_tax(self): si = create_sales_invoice(qty=1, rate=90, do_not_submit=True) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index c2fbdcc95f1..7d7c6f49e12 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -7,12 +7,7 @@ import copy import frappe from frappe import _ from frappe.model.meta import get_field_precision -<<<<<<< HEAD -from frappe.utils import cint, flt, formatdate, getdate, now -======= from frappe.utils import cint, flt, formatdate, get_link_to_form, getdate, now -from frappe.utils.dashboard import cache_source ->>>>>>> 88e68168e3 (refactor: use separate round off for opening entries) import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (