From a5b228549c0c03517a53db8609f34fb6ab308445 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Aug 2024 11:35:15 +0530 Subject: [PATCH 01/10] feat: round off for opening entries --- 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 7d74eb1192e..805bea18b53 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", @@ -785,6 +786,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", @@ -792,7 +799,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 3bdf93f63a9..54765420ad4 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 88e68168e36624d9ec31993c8f7a9b29add8c1ce Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Aug 2024 11:49:50 +0530 Subject: [PATCH 02/10] refactor: use separate round off for opening entries --- 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 | 49 +++++++++++++++---- erpnext/controllers/accounts_controller.py | 6 ++- erpnext/setup/doctype/company/company.js | 1 + 7 files changed, 59 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index 5a50d7a1ab5..34e9a691fd2 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -124,7 +124,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 }, { @@ -194,7 +194,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 180edc61ced..f6b0a6f8056 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 44d8a407017..b9194beccb5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1520,7 +1520,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 4f62ad2f4c4..0ee98114794 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1626,7 +1626,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 4fba1095546..98e1ac2de4a 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -7,7 +7,7 @@ import copy import frappe from frappe import _ from frappe.model.meta import get_field_precision -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 import erpnext @@ -490,16 +490,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) @@ -517,7 +537,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, @@ -531,6 +551,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) @@ -555,8 +578,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 @@ -572,12 +595,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 65f73061008..f81b06546b1 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1266,7 +1266,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 812d29810e6..29ae1127653 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -251,6 +251,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 fc46ebcd7c9a3628ddd7b7a1f284d2c2d9d6b7f9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 29 Aug 2024 17:37:32 +0530 Subject: [PATCH 03/10] chore: default should return 3 elements --- 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 98e1ac2de4a..84217da1a6b 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -580,7 +580,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 96e3c2ad1061af44b044fa64b0af16004d0d732e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 30 Aug 2024 16:08:43 +0530 Subject: [PATCH 04/10] refactor: handle opening round off from sales invoice --- .../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 0ee98114794..da95440759b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1634,6 +1634,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 5021c7ca2c3bb5f758d00c65eab1dcfec692d2a4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 30 Aug 2024 17:55:02 +0530 Subject: [PATCH 05/10] test: rounding adjustment validation and posting --- .../sales_invoice/test_sales_invoice.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index e94b27b0545..f0882ac592b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3884,6 +3884,56 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(len(res), 1) self.assertEqual(res[0][0], pos_return.return_against) + 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) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 79267358d0fc95a10c9bcd03307ba1284e561526 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Sep 2024 16:33:55 +0530 Subject: [PATCH 06/10] test: opening round off with inclusive tax --- .../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 f0882ac592b..e44d8a2bd35 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3891,39 +3891,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() @@ -3934,6 +3935,57 @@ class TestSalesInvoice(FrappeTestCase): ) self.assertEqual(len(res), 3) + 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 193ea9ad8f29358820fff45698772ea5adeffcfa Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Sep 2024 17:52:28 +0530 Subject: [PATCH 07/10] refactor: filter on account_type --- 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 29ae1127653..b356c197751 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -251,7 +251,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 a5d6a25a9654ebe7f91285b9266e9b7cd9797545 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 2 Sep 2024 17:53:02 +0530 Subject: [PATCH 08/10] refactor: handle opening round off on purchase invoice --- .../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 b9194beccb5..7ff600b0b3b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1528,6 +1528,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 b7edc6dea908b887041998801a3d7724fc8777f1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Sep 2024 11:04:26 +0530 Subject: [PATCH 09/10] test: opening purchase invoice with rounding adjustment --- .../purchase_invoice/test_purchase_invoice.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index d85abf93ea7..ccc14353c6b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2302,6 +2302,65 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) + 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) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From cf11ac87fb7a9bb15518204900d776e06af60aaf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 3 Sep 2024 11:05:34 +0530 Subject: [PATCH 10/10] refactor(test): filter for active ledger entries --- 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 e44d8a2bd35..c2d1a9b1388 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3973,7 +3973,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", )