From 028b3e2fbf52cdb7561e8d63b54d5918b5bc8af4 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 11:48:27 +0530 Subject: [PATCH 01/18] fix: `TypeError` in PR for non-stock item --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 2a4b6f34b57e..cbc1693eaadc 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -600,11 +600,10 @@ def make_divisional_loss_gl_entry(item, outgoing_amount): make_rate_difference_entry(d) make_sub_contracting_gl_entries(d) make_divisional_loss_gl_entry(d, outgoing_amount) - elif ( - d.warehouse not in warehouse_with_no_account - or d.rejected_warehouse not in warehouse_with_no_account + elif (d.warehouse and d.warehouse not in warehouse_with_no_account) or ( + d.rejected_warehouse and d.rejected_warehouse not in warehouse_with_no_account ): - warehouse_with_no_account.append(d.warehouse) + warehouse_with_no_account.append(d.warehouse or d.rejected_warehouse) if d.is_fixed_asset: self.update_assets(d, d.valuation_rate) From 8fa677b8e8c6e4e65193ad0be7c0a6d5b4f1769e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 1 Nov 2023 20:30:16 +0530 Subject: [PATCH 02/18] refactor: checkbox to toggle remarks in General Ledger --- erpnext/accounts/report/general_ledger/general_ledger.js | 6 ++++++ erpnext/accounts/report/general_ledger/general_ledger.py | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 37d0659acfc8..c0b4f5957943 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -193,7 +193,13 @@ frappe.query_reports["General Ledger"] = { "fieldname": "add_values_in_transaction_currency", "label": __("Add Columns in Transaction Currency"), "fieldtype": "Check" + }, + { + "fieldname": "show_remarks", + "label": __("Show Remarks"), + "fieldtype": "Check" } + ] } diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 79bfd7833ab9..5e484cf5584e 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -163,6 +163,9 @@ def get_gl_entries(filters, accounting_dimensions): select_fields = """, debit, credit, debit_in_account_currency, credit_in_account_currency """ + if filters.get("show_remarks"): + select_fields += """,remarks""" + order_by_statement = "order by posting_date, account, creation" if filters.get("include_dimensions"): @@ -195,7 +198,7 @@ def get_gl_entries(filters, accounting_dimensions): voucher_type, voucher_no, {dimension_fields} cost_center, project, {transaction_currency_fields} against_voucher_type, against_voucher, account_currency, - remarks, against, is_opening, creation {select_fields} + against, is_opening, creation {select_fields} from `tabGL Entry` where company=%(company)s {conditions} {order_by_statement} @@ -631,8 +634,10 @@ def get_columns(filters): "width": 100, }, {"label": _("Supplier Invoice No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 100}, - {"label": _("Remarks"), "fieldname": "remarks", "width": 400}, ] ) + if filters.get("show_remarks"): + columns.extend([{"label": _("Remarks"), "fieldname": "remarks", "width": 400}]) + return columns From 38e5e4a8930714c08471308ba47b9f063802a4b3 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 1 Nov 2023 18:05:50 +0100 Subject: [PATCH 03/18] feat(Stock Balance): add filters from route --- erpnext/stock/page/stock_balance/stock_balance.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js index f00dd3e7912d..90b8d453420d 100644 --- a/erpnext/stock/page/stock_balance/stock_balance.js +++ b/erpnext/stock/page/stock_balance/stock_balance.js @@ -11,6 +11,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { label: __('Warehouse'), fieldtype:'Link', options:'Warehouse', + default: frappe.route_options && frappe.route_options.warehouse, change: function() { page.item_dashboard.start = 0; page.item_dashboard.refresh(); @@ -22,6 +23,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { label: __('Item'), fieldtype:'Link', options:'Item', + default: frappe.route_options && frappe.route_options.item_code, change: function() { page.item_dashboard.start = 0; page.item_dashboard.refresh(); @@ -33,6 +35,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { label: __('Item Group'), fieldtype:'Link', options:'Item Group', + default: frappe.route_options && frappe.route_options.item_group, change: function() { page.item_dashboard.start = 0; page.item_dashboard.refresh(); From 54e8ce1ac5a0d14905050fbb630a1602a560088a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 09:16:26 +0530 Subject: [PATCH 04/18] refactor: pass limits to JE and PE queries in reconciliation tool --- .../payment_reconciliation/payment_reconciliation.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 1626f25f3ee3..43167be15a87 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -109,6 +109,8 @@ def get_jv_entries(self): "t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1" ) + limit = f"limit {self.payment_limit}" if self.payment_limit else " " + # nosemgrep journal_entries = frappe.db.sql( """ @@ -132,11 +134,13 @@ def get_jv_entries(self): ELSE {bank_account_condition} END) order by t1.posting_date + {limit} """.format( **{ "dr_or_cr": dr_or_cr, "bank_account_condition": bank_account_condition, "condition": condition, + "limit": limit, } ), { @@ -162,7 +166,7 @@ def get_return_invoices(self): if self.payment_name: conditions.append(doc.name.like(f"%{self.payment_name}%")) - self.return_invoices = ( + self.return_invoices_query = ( qb.from_(doc) .select( ConstantColumn(voucher_type).as_("voucher_type"), @@ -170,8 +174,11 @@ def get_return_invoices(self): doc.return_against, ) .where(Criterion.all(conditions)) - .run(as_dict=True) ) + if self.payment_limit: + self.return_invoices_query = self.return_invoices_query.limit(self.payment_limit) + + self.return_invoices = self.return_invoices_query.run(as_dict=True) def get_dr_or_cr_notes(self): From a9fceeb00ff266b181791dc0ca4ca334810fff0a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 11:32:24 +0530 Subject: [PATCH 05/18] chore: std permissions for Process Payment Reconciilation log --- .../process_payment_reconciliation_log.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json index 1131a0fca69b..b4ac9812cbf2 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json +++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json @@ -110,7 +110,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-04-21 17:36:26.642617", + "modified": "2023-11-02 11:32:12.254018", "modified_by": "Administrator", "module": "Accounts", "name": "Process Payment Reconciliation Log", @@ -125,7 +125,19 @@ "print": 1, "read": 1, "report": 1, - "role": "System Manager", + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", "share": 1, "write": 1 } From 0104897d693ca98796536eac7a9edadc58c64fff Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 2 Nov 2023 13:14:56 +0530 Subject: [PATCH 06/18] fix: remove voucher type and no for Item and Warehouse based reposting --- erpnext/controllers/stock_controller.py | 2 -- .../stock_reposting_settings/stock_reposting_settings.json | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index a7330ec63c0f..fc45c7ad529f 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1210,8 +1210,6 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa repost_entry = frappe.new_doc("Repost Item Valuation") repost_entry.based_on = "Item and Warehouse" - repost_entry.voucher_type = voucher_type - repost_entry.voucher_no = voucher_no repost_entry.item_code = sle.item_code repost_entry.warehouse = sle.warehouse diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json index 7c712ce22586..68afd996b498 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json @@ -50,7 +50,7 @@ "label": "Limit timeslot for Stock Reposting" }, { - "default": "0", + "default": "1", "fieldname": "item_based_reposting", "fieldtype": "Check", "label": "Use Item based reposting" @@ -70,7 +70,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-05-04 16:14:29.080697", + "modified": "2023-11-01 16:14:29.080697", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reposting Settings", From 539f0251d95d80055112d72e54af9571bea987dc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 13:17:21 +0530 Subject: [PATCH 07/18] refactor: better output on gl and pl comparison report --- .../general_and_payment_ledger_comparison.py | 82 ++++++++++++++++--- 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py index 099884a48ecc..696a03b0a700 100644 --- a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py +++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py @@ -79,7 +79,9 @@ def get_gle(self): .select( gle.company, gle.account, + gle.voucher_type, gle.voucher_no, + gle.party_type, gle.party, outstanding, ) @@ -89,7 +91,9 @@ def get_gle(self): & (gle.account.isin(val.accounts)) ) .where(Criterion.all(filter_criterion)) - .groupby(gle.company, gle.account, gle.voucher_no, gle.party) + .groupby( + gle.company, gle.account, gle.voucher_type, gle.voucher_no, gle.party_type, gle.party + ) .run() ) @@ -112,7 +116,13 @@ def get_ple(self): self.account_types[acc_type].ple = ( qb.from_(ple) .select( - ple.company, ple.account, ple.voucher_no, ple.party, Sum(ple.amount).as_("outstanding") + ple.company, + ple.account, + ple.voucher_type, + ple.voucher_no, + ple.party_type, + ple.party, + Sum(ple.amount).as_("outstanding"), ) .where( (ple.company == self.filters.company) @@ -120,7 +130,9 @@ def get_ple(self): & (ple.account.isin(val.accounts)) ) .where(Criterion.all(filter_criterion)) - .groupby(ple.company, ple.account, ple.voucher_no, ple.party) + .groupby( + ple.company, ple.account, ple.voucher_type, ple.voucher_no, ple.party_type, ple.party + ) .run() ) @@ -138,12 +150,12 @@ def compare(self): self.diff = frappe._dict({}) for x in self.variation_in_payment_ledger: - self.diff[(x[0], x[1], x[2], x[3])] = frappe._dict({"gl_balance": x[4]}) + self.diff[(x[0], x[1], x[2], x[3], x[4], x[5])] = frappe._dict({"gl_balance": x[6]}) for x in self.variation_in_general_ledger: - self.diff.setdefault((x[0], x[1], x[2], x[3]), frappe._dict({"gl_balance": 0.0})).update( - frappe._dict({"pl_balance": x[4]}) - ) + self.diff.setdefault( + (x[0], x[1], x[2], x[3], x[4], x[5]), frappe._dict({"gl_balance": 0.0}) + ).update(frappe._dict({"pl_balance": x[6]})) def generate_data(self): self.data = [] @@ -151,8 +163,12 @@ def generate_data(self): self.data.append( frappe._dict( { - "voucher_no": key[2], - "party": key[3], + "company": key[0], + "account": key[1], + "voucher_type": key[2], + "voucher_no": key[3], + "party_type": key[4], + "party": key[5], "gl_balance": val.gl_balance, "pl_balance": val.pl_balance, } @@ -162,12 +178,52 @@ def generate_data(self): def get_columns(self): self.columns = [] options = None + self.columns.append( + dict( + label=_("Company"), + fieldname="company", + fieldtype="Link", + options="Company", + width="100", + ) + ) + + self.columns.append( + dict( + label=_("Account"), + fieldname="account", + fieldtype="Link", + options="Account", + width="100", + ) + ) + + self.columns.append( + dict( + label=_("Voucher Type"), + fieldname="voucher_type", + fieldtype="Link", + options="DocType", + width="100", + ) + ) + self.columns.append( dict( label=_("Voucher No"), fieldname="voucher_no", - fieldtype="Data", - options=options, + fieldtype="Dynamic Link", + options="voucher_type", + width="100", + ) + ) + + self.columns.append( + dict( + label=_("Party Type"), + fieldname="party_type", + fieldtype="Link", + options="DocType", width="100", ) ) @@ -176,8 +232,8 @@ def get_columns(self): dict( label=_("Party"), fieldname="party", - fieldtype="Data", - options=options, + fieldtype="Dynamic Link", + options="party_type", width="100", ) ) From 639f427d6d31c7d018033c2110c478f8e6ea8ce4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 13:40:40 +0530 Subject: [PATCH 08/18] refactor(test): for ledger comparision report --- .../test_general_and_payment_ledger_comparison.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py b/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py index 4b0e99d71254..59e906ba3321 100644 --- a/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py +++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py @@ -50,7 +50,11 @@ def test_01_basic_report_functionality(self): self.assertEqual(len(data), 1) expected = { + "company": sinv.company, + "account": sinv.debit_to, + "voucher_type": sinv.doctype, "voucher_no": sinv.name, + "party_type": "Customer", "party": sinv.customer, "gl_balance": sinv.grand_total, "pl_balance": sinv.grand_total - 1, From 1b808e1d7c2c2d1ad51bd18acf6a45e9b5cfc605 Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:01:26 +0530 Subject: [PATCH 09/18] fix: standard submit perm in repost ledger for editable invoices (#37826) * fix: ignore perm while reposting ledger * fix: use flag in save * fix: remove unnecessary save --- erpnext/controllers/accounts_controller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 6efe631a29f0..38c1e820d270 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2269,6 +2269,7 @@ def repost_accounting_entries(self): repost_ledger = frappe.new_doc("Repost Accounting Ledger") repost_ledger.company = self.company repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name}) + repost_ledger.flags.ignore_permissions = True repost_ledger.insert() repost_ledger.submit() self.db_set("repost_required", 0) From ed1c198897c31795c720f0f738c2ec40f4a70bc8 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 2 Nov 2023 14:21:16 +0530 Subject: [PATCH 10/18] chore: fix test cases --- .../repost_item_valuation/test_repost_item_valuation.py | 4 +++- erpnext/stock/doctype/stock_entry/test_stock_entry.py | 1 + .../doctype/stock_reconciliation/test_stock_reconciliation.py | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py index 623e8fafe975..5b76e442f471 100644 --- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock, call import frappe -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, add_to_date, now, nowdate, today from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice @@ -196,6 +196,7 @@ def test_stock_freeze_validation(self): riv.set_status("Skipped") + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_prevention_of_cancelled_transaction_riv(self): frappe.flags.dont_execute_stock_reposts = True @@ -373,6 +374,7 @@ def test_account_freeze_validation(self): accounts_settings.acc_frozen_upto = "" accounts_settings.save() + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_create_repost_entry_for_cancelled_document(self): pr = make_purchase_receipt( company="_Test Company with perpetual inventory", diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 3e0610ef6ed0..b640983a09d6 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1467,6 +1467,7 @@ def test_stock_entry_item_details(self): self.assertEqual(se.items[0].item_name, item.item_name) self.assertEqual(se.items[0].stock_uom, item.stock_uom) + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_reposting_for_depedent_warehouse(self): from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_sl_entries from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 4817c8d8dc29..34a7cbaa72a7 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -674,6 +674,7 @@ def test_serial_no_batch_no_item(self): self.assertEqual(flt(sl_entry.actual_qty), 1.0) self.assertEqual(flt(sl_entry.qty_after_transaction), 1.0) + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_backdated_stock_reco_entry(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry From c37e374fdd322c0f0423469ce95faecac15b18e4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 2 Nov 2023 21:00:45 +0530 Subject: [PATCH 11/18] fix: Add index to supplier invoice field (#37861) * fix: Add index to supplier invoice field * chore: remove unintetional changes --- .../doctype/purchase_invoice/purchase_invoice.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 2d1f4451b67c..00176c01fce6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -384,7 +384,8 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "search_index": 1 }, { "fieldname": "column_break_15", @@ -1602,7 +1603,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-10-16 16:24:51.886231", + "modified": "2023-11-02 18:46:16.810187", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", @@ -1665,4 +1666,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} \ No newline at end of file +} From e019d43d0b3fa6faa241a7b9885f65f6fccc9459 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 3 Nov 2023 00:53:30 +0530 Subject: [PATCH 12/18] fix: permission error while creating Supplier Quotation from Portal --- erpnext/controllers/buying_controller.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index a76abe2154b3..3a802bd26fa7 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -4,7 +4,7 @@ import frappe from frappe import ValidationError, _, msgprint -from frappe.contacts.doctype.address.address import get_address_display +from frappe.contacts.doctype.address.address import render_address from frappe.utils import cint, flt, getdate from frappe.utils.data import nowtime @@ -246,7 +246,9 @@ def set_supplier_address(self): for address_field, address_display_field in address_dict.items(): if self.get(address_field): - self.set(address_display_field, get_address_display(self.get(address_field))) + self.set( + address_display_field, render_address(self.get(address_field), check_permissions=False) + ) def set_total_in_words(self): from frappe.utils import money_in_words From 23beb46d15a2b96601e1f20a68dee06d7e5f0c49 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 3 Nov 2023 11:03:54 +0530 Subject: [PATCH 13/18] refactor: group only by voucher flag in AR/AP report --- .../accounts_payable/accounts_payable.js | 8 ++++++- .../accounts_receivable.js | 8 ++++++- .../accounts_receivable.py | 22 +++++++++++++++---- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 9c73cbb344f6..eff705dafac3 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -143,7 +143,13 @@ frappe.query_reports["Accounts Payable"] = { "fieldname": "show_future_payments", "label": __("Show Future Payments"), "fieldtype": "Check", + }, + { + "fieldname": "ignore_accounts", + "label": __("Group by Voucher"), + "fieldtype": "Check", } + ], "formatter": function(value, row, column, data, default_formatter) { @@ -175,4 +181,4 @@ function get_party_type_options() { }); }); return options; -} \ No newline at end of file +} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 1073be0bdc40..786aad601ba7 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -172,7 +172,13 @@ frappe.query_reports["Accounts Receivable"] = { "fieldname": "show_remarks", "label": __("Show Remarks"), "fieldtype": "Check", + }, + { + "fieldname": "ignore_accounts", + "label": __("Group by Voucher"), + "fieldtype": "Check", } + ], "formatter": function(value, row, column, data, default_formatter) { @@ -205,4 +211,4 @@ function get_party_type_options() { }); }); return options; -} \ No newline at end of file +} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index b9c7a0bfb877..69f703d90702 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -116,7 +116,12 @@ def init_voucher_balance(self): # build all keys, since we want to exclude vouchers beyond the report date for ple in self.ple_entries: # get the balance object for voucher_type - key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) + + if self.filters.get("ingore_accounts"): + key = (ple.voucher_type, ple.voucher_no, ple.party) + else: + key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) + if not key in self.voucher_balance: self.voucher_balance[key] = frappe._dict( voucher_type=ple.voucher_type, @@ -183,7 +188,10 @@ def get_voucher_balance(self, ple): ): return - key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party) + if self.filters.get("ingore_accounts"): + key = (ple.against_voucher_type, ple.against_voucher_no, ple.party) + else: + key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party) # If payment is made against credit note # and credit note is made against a Sales Invoice @@ -192,13 +200,19 @@ def get_voucher_balance(self, ple): if ple.against_voucher_no in self.return_entries: return_against = self.return_entries.get(ple.against_voucher_no) if return_against: - key = (ple.account, ple.against_voucher_type, return_against, ple.party) + if self.filters.get("ingore_accounts"): + key = (ple.against_voucher_type, return_against, ple.party) + else: + key = (ple.account, ple.against_voucher_type, return_against, ple.party) row = self.voucher_balance.get(key) if not row: # no invoice, this is an invoice / stand-alone payment / credit note - row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party)) + if self.filters.get("ingore_accounts"): + row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party)) + else: + row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party)) row.party_type = ple.party_type return row From 469ae2c7f1edb6dbd2d770b65c48bad5a0293ffe Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 3 Nov 2023 15:57:52 +0530 Subject: [PATCH 14/18] perf: index return against for purchase invoice (#37881) --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 00176c01fce6..09bffff6da52 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -408,7 +408,8 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "section_addresses", @@ -1603,7 +1604,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-11-02 18:46:16.810187", + "modified": "2023-11-03 15:47:30.319200", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", From d4c0dbfacc7e99da6cba2c5d389f0a662490b0eb Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 3 Nov 2023 17:19:06 +0530 Subject: [PATCH 15/18] fix: incorrect available qty for backdated stock reco with batch (#37858) * fix: incorrect available qty for backdated stock reco with batch * test: added test case --- .../serial_and_batch_bundle.py | 13 +- .../stock_reconciliation.py | 137 +++++++++++++----- .../test_stock_reconciliation.py | 69 ++++++++- .../stock_reconciliation_item.json | 3 +- .../stock/report/stock_ledger/stock_ledger.py | 8 + .../stock_ledger_invariant_check.py | 13 +- erpnext/stock/stock_ledger.py | 66 +++++---- 7 files changed, 229 insertions(+), 80 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index f96c184c887a..f2bbf2b21179 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -121,7 +121,7 @@ def validate_serial_nos_duplicate(self): def throw_error_message(self, message, exception=frappe.ValidationError): frappe.throw(_(message), exception, title=_("Error")) - def set_incoming_rate(self, row=None, save=False): + def set_incoming_rate(self, row=None, save=False, allow_negative_stock=False): if self.type_of_transaction not in ["Inward", "Outward"] or self.voucher_type in [ "Installation Note", "Job Card", @@ -131,7 +131,9 @@ def set_incoming_rate(self, row=None, save=False): return if self.type_of_transaction == "Outward": - self.set_incoming_rate_for_outward_transaction(row, save) + self.set_incoming_rate_for_outward_transaction( + row, save, allow_negative_stock=allow_negative_stock + ) else: self.set_incoming_rate_for_inward_transaction(row, save) @@ -152,7 +154,9 @@ def calculate_total_qty(self, save=True): def get_serial_nos(self): return [d.serial_no for d in self.entries if d.serial_no] - def set_incoming_rate_for_outward_transaction(self, row=None, save=False): + def set_incoming_rate_for_outward_transaction( + self, row=None, save=False, allow_negative_stock=False + ): sle = self.get_sle_for_outward_transaction() if self.has_serial_no: @@ -181,7 +185,8 @@ def set_incoming_rate_for_outward_transaction(self, row=None, save=False): if self.docstatus == 1: available_qty += flt(d.qty) - self.validate_negative_batch(d.batch_no, available_qty) + if not allow_negative_stock: + self.validate_negative_batch(d.batch_no, available_qty) d.stock_value_difference = flt(d.qty) * flt(d.incoming_rate) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 98b4ffdfcfd0..323ad4f57d7f 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -6,7 +6,7 @@ import frappe from frappe import _, bold, msgprint from frappe.query_builder.functions import CombineDatetime, Sum -from frappe.utils import cint, cstr, flt +from frappe.utils import add_to_date, cint, cstr, flt import erpnext from erpnext.accounts.utils import get_company_default @@ -88,9 +88,12 @@ def on_cancel(self): self.repost_future_sle_and_gle() self.delete_auto_created_batches() - def set_current_serial_and_batch_bundle(self): + def set_current_serial_and_batch_bundle(self, voucher_detail_no=None, save=False) -> None: """Set Serial and Batch Bundle for each item""" for item in self.items: + if voucher_detail_no and voucher_detail_no != item.name: + continue + item_details = frappe.get_cached_value( "Item", item.item_code, ["has_serial_no", "has_batch_no"], as_dict=1 ) @@ -148,6 +151,7 @@ def set_current_serial_and_batch_bundle(self): "warehouse": item.warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, + "ignore_voucher_nos": [self.name], } ) ) @@ -163,11 +167,36 @@ def set_current_serial_and_batch_bundle(self): ) if not serial_and_batch_bundle.entries: + if voucher_detail_no: + return + continue - item.current_serial_and_batch_bundle = serial_and_batch_bundle.save().name + serial_and_batch_bundle.save() + item.current_serial_and_batch_bundle = serial_and_batch_bundle.name item.current_qty = abs(serial_and_batch_bundle.total_qty) item.current_valuation_rate = abs(serial_and_batch_bundle.avg_rate) + if save: + sle_creation = frappe.db.get_value( + "Serial and Batch Bundle", item.serial_and_batch_bundle, "creation" + ) + creation = add_to_date(sle_creation, seconds=-1) + item.db_set( + { + "current_serial_and_batch_bundle": item.current_serial_and_batch_bundle, + "current_qty": item.current_qty, + "current_valuation_rate": item.current_valuation_rate, + "creation": creation, + } + ) + + serial_and_batch_bundle.db_set( + { + "creation": creation, + "voucher_no": self.name, + "voucher_detail_no": voucher_detail_no, + } + ) def set_new_serial_and_batch_bundle(self): for item in self.items: @@ -689,56 +718,84 @@ def cancel(self): else: self._cancel() - def recalculate_current_qty(self, item_code, batch_no): + def recalculate_current_qty(self, voucher_detail_no, sle_creation, add_new_sle=False): from erpnext.stock.stock_ledger import get_valuation_rate sl_entries = [] + for row in self.items: - if ( - not (row.item_code == item_code and row.batch_no == batch_no) - and not row.serial_and_batch_bundle - ): + if voucher_detail_no != row.name: continue + current_qty = 0.0 if row.current_serial_and_batch_bundle: - self.recalculate_qty_for_serial_and_batch_bundle(row) - continue - - current_qty = get_batch_qty_for_stock_reco( - item_code, row.warehouse, batch_no, self.posting_date, self.posting_time, self.name - ) + current_qty = self.get_qty_for_serial_and_batch_bundle(row) + elif row.batch_no: + current_qty = get_batch_qty_for_stock_reco( + row.item_code, row.warehouse, row.batch_no, self.posting_date, self.posting_time, self.name + ) precesion = row.precision("current_qty") - if flt(current_qty, precesion) == flt(row.current_qty, precesion): - continue - - val_rate = get_valuation_rate( - item_code, row.warehouse, self.doctype, self.name, company=self.company, batch_no=batch_no - ) + if flt(current_qty, precesion) != flt(row.current_qty, precesion): + val_rate = get_valuation_rate( + row.item_code, + row.warehouse, + self.doctype, + self.name, + company=self.company, + batch_no=row.batch_no, + serial_and_batch_bundle=row.current_serial_and_batch_bundle, + ) - row.current_valuation_rate = val_rate - if not row.current_qty and current_qty: - sle = self.get_sle_for_items(row) - sle.actual_qty = current_qty * -1 - sle.valuation_rate = val_rate - sl_entries.append(sle) + row.current_valuation_rate = val_rate + row.current_qty = current_qty + row.db_set( + { + "current_qty": row.current_qty, + "current_valuation_rate": row.current_valuation_rate, + "current_amount": flt(row.current_qty * row.current_valuation_rate), + } + ) - row.current_qty = current_qty - row.db_set( - { - "current_qty": row.current_qty, - "current_valuation_rate": row.current_valuation_rate, - "current_amount": flt(row.current_qty * row.current_valuation_rate), - } - ) + if ( + add_new_sle + and not frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_detail_no": row.name, "actual_qty": ("<", 0), "is_cancelled": 0}, + "name", + ) + and (not row.current_serial_and_batch_bundle and not row.batch_no) + ): + self.set_current_serial_and_batch_bundle(voucher_detail_no, save=True) + row.reload() + + if row.current_qty > 0 and row.current_serial_and_batch_bundle: + new_sle = self.get_sle_for_items(row) + new_sle.actual_qty = row.current_qty * -1 + new_sle.valuation_rate = row.current_valuation_rate + new_sle.creation_time = add_to_date(sle_creation, seconds=-1) + new_sle.serial_and_batch_bundle = row.current_serial_and_batch_bundle + new_sle.qty_after_transaction = 0.0 + sl_entries.append(new_sle) if sl_entries: - self.make_sl_entries(sl_entries, allow_negative_stock=True) + self.make_sl_entries(sl_entries, allow_negative_stock=self.has_negative_stock_allowed()) + if not frappe.db.exists("Repost Item Valuation", {"voucher_no": self.name, "status": "Queued"}): + self.repost_future_sle_and_gle(force=True) + + def has_negative_stock_allowed(self): + allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) - def recalculate_qty_for_serial_and_batch_bundle(self, row): + if all(d.serial_and_batch_bundle and flt(d.qty) == flt(d.current_qty) for d in self.items): + allow_negative_stock = True + + return allow_negative_stock + + def get_qty_for_serial_and_batch_bundle(self, row): doc = frappe.get_doc("Serial and Batch Bundle", row.current_serial_and_batch_bundle) precision = doc.entries[0].precision("qty") + current_qty = 0 for d in doc.entries: qty = ( get_batch_qty( @@ -751,10 +808,12 @@ def recalculate_qty_for_serial_and_batch_bundle(self, row): or 0 ) * -1 - if flt(d.qty, precision) == flt(qty, precision): - continue + if flt(d.qty, precision) != flt(qty, precision): + d.db_set("qty", qty) + + current_qty += qty - d.db_set("qty", qty) + return abs(current_qty) def get_batch_qty_for_stock_reco( diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 34a7cbaa72a7..1ec99bf9a5b6 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -742,13 +742,6 @@ def test_backdated_stock_reco_entry(self): se2.cancel() - self.assertTrue(frappe.db.exists("Repost Item Valuation", {"voucher_no": stock_reco.name})) - - self.assertEqual( - frappe.db.get_value("Repost Item Valuation", {"voucher_no": stock_reco.name}, "status"), - "Completed", - ) - sle = frappe.get_all( "Stock Ledger Entry", filters={"item_code": item_code, "warehouse": warehouse, "is_cancelled": 0}, @@ -766,6 +759,68 @@ def test_backdated_stock_reco_entry(self): self.assertEqual(flt(sle[0].actual_qty), flt(-100.0)) + def test_backdated_stock_reco_entry_with_batch(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + item_code = self.make_item( + "Test New Batch Item ABCVSD", + { + "is_stock_item": 1, + "has_batch_no": 1, + "batch_number_series": "BNS9.####", + "create_new_batch": 1, + }, + ).name + + warehouse = "_Test Warehouse - _TC" + + # Stock Reco for 100, Balace Qty 100 + stock_reco = create_stock_reconciliation( + item_code=item_code, + posting_date=nowdate(), + posting_time="11:00:00", + warehouse=warehouse, + qty=100, + rate=100, + ) + + sles = frappe.get_all( + "Stock Ledger Entry", + fields=["actual_qty"], + filters={"voucher_no": stock_reco.name, "is_cancelled": 0}, + ) + + self.assertEqual(len(sles), 1) + + stock_reco.reload() + batch_no = get_batch_from_bundle(stock_reco.items[0].serial_and_batch_bundle) + + # Stock Reco for 100, Balace Qty 100 + stock_reco1 = create_stock_reconciliation( + item_code=item_code, + posting_date=add_days(nowdate(), -1), + posting_time="11:00:00", + batch_no=batch_no, + warehouse=warehouse, + qty=60, + rate=100, + ) + + sles = frappe.get_all( + "Stock Ledger Entry", + fields=["actual_qty"], + filters={"voucher_no": stock_reco.name, "is_cancelled": 0}, + ) + + stock_reco1.reload() + new_batch_no = get_batch_from_bundle(stock_reco1.items[0].serial_and_batch_bundle) + + self.assertEqual(len(sles), 2) + + for row in sles: + if row.actual_qty < 0: + self.assertEqual(row.actual_qty, -60) + def test_update_stock_reconciliation_while_reposting(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json index ca19bbb96ad7..d9cbf9571046 100644 --- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json +++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json @@ -205,6 +205,7 @@ "fieldname": "current_serial_and_batch_bundle", "fieldtype": "Link", "label": "Current Serial / Batch Bundle", + "no_copy": 1, "options": "Serial and Batch Bundle", "read_only": 1 }, @@ -216,7 +217,7 @@ ], "istable": 1, "links": [], - "modified": "2023-07-26 12:54:34.011915", + "modified": "2023-11-02 15:47:07.929550", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation Item", diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index eeef39641b01..e59f2fe64493 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -249,6 +249,13 @@ def get_columns(filters): "options": "Serial No", "width": 100, }, + { + "label": _("Serial and Batch Bundle"), + "fieldname": "serial_and_batch_bundle", + "fieldtype": "Link", + "options": "Serial and Batch Bundle", + "width": 100, + }, {"label": _("Balance Serial No"), "fieldname": "balance_serial_no", "width": 100}, { "label": _("Project"), @@ -287,6 +294,7 @@ def get_stock_ledger_entries(filters, items): sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference, + sle.serial_and_batch_bundle, sle.voucher_no, sle.stock_value, sle.batch_no, diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py index ca15afe444d5..fb392f7e36a5 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py +++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py @@ -24,6 +24,7 @@ "stock_value_difference", "valuation_rate", "voucher_detail_no", + "serial_and_batch_bundle", ) @@ -64,7 +65,11 @@ def add_invariant_check_fields(sles): balance_qty += sle.actual_qty balance_stock_value += sle.stock_value_difference - if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no: + if ( + sle.voucher_type == "Stock Reconciliation" + and not sle.batch_no + and not sle.serial_and_batch_bundle + ): balance_qty = frappe.db.get_value("Stock Reconciliation Item", sle.voucher_detail_no, "qty") if balance_qty is None: balance_qty = sle.qty_after_transaction @@ -143,6 +148,12 @@ def get_columns(): "label": _("Batch"), "options": "Batch", }, + { + "fieldname": "serial_and_batch_bundle", + "fieldtype": "Link", + "label": _("Serial and Batch Bundle"), + "options": "Serial and Batch Bundle", + }, { "fieldname": "use_batchwise_valuation", "fieldtype": "Check", diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index b950f1881078..551701b47a6b 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -203,6 +203,11 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): sle.allow_negative_stock = allow_negative_stock sle.via_landed_cost_voucher = via_landed_cost_voucher sle.submit() + + # Added to handle the case when the stock ledger entry is created from the repostig + if args.get("creation_time") and args.get("voucher_type") == "Stock Reconciliation": + sle.db_set("creation", args.get("creation_time")) + return sle @@ -689,9 +694,11 @@ def process_sle(self, sle): if ( sle.voucher_type == "Stock Reconciliation" - and (sle.batch_no or (sle.has_batch_no and sle.serial_and_batch_bundle)) + and ( + sle.batch_no or (sle.has_batch_no and sle.serial_and_batch_bundle and not sle.has_serial_no) + ) and sle.voucher_detail_no - and sle.actual_qty < 0 + and not self.args.get("sle_id") ): self.reset_actual_qty_for_stock_reco(sle) @@ -754,27 +761,22 @@ def process_sle(self, sle): self.update_outgoing_rate_on_transaction(sle) def reset_actual_qty_for_stock_reco(self, sle): - if sle.serial_and_batch_bundle: - current_qty = frappe.get_cached_value( - "Serial and Batch Bundle", sle.serial_and_batch_bundle, "total_qty" - ) + doc = frappe.get_cached_doc("Stock Reconciliation", sle.voucher_no) + doc.recalculate_current_qty(sle.voucher_detail_no, sle.creation, sle.actual_qty > 0) - if current_qty is not None: - current_qty = abs(current_qty) - else: - current_qty = frappe.get_cached_value( - "Stock Reconciliation Item", sle.voucher_detail_no, "current_qty" + if sle.actual_qty < 0: + sle.actual_qty = ( + flt(frappe.db.get_value("Stock Reconciliation Item", sle.voucher_detail_no, "current_qty")) + * -1 ) - if current_qty: - sle.actual_qty = current_qty * -1 - elif current_qty == 0: - sle.is_cancelled = 1 + if abs(sle.actual_qty) == 0.0: + sle.is_cancelled = 1 def calculate_valuation_for_serial_batch_bundle(self, sle): doc = frappe.get_cached_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle) - doc.set_incoming_rate(save=True) + doc.set_incoming_rate(save=True, allow_negative_stock=self.allow_negative_stock) doc.calculate_qty_and_amount(save=True) self.wh_data.stock_value = round_off_if_near_zero(self.wh_data.stock_value + doc.total_amount) @@ -1461,6 +1463,7 @@ def get_valuation_rate( currency=None, company=None, raise_error_if_no_rate=True, + batch_no=None, serial_and_batch_bundle=None, ): @@ -1469,6 +1472,25 @@ def get_valuation_rate( if not company: company = frappe.get_cached_value("Warehouse", warehouse, "company") + if warehouse and batch_no and frappe.db.get_value("Batch", batch_no, "use_batchwise_valuation"): + table = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(table) + .select(Sum(table.stock_value_difference) / Sum(table.actual_qty)) + .where( + (table.item_code == item_code) + & (table.warehouse == warehouse) + & (table.batch_no == batch_no) + & (table.is_cancelled == 0) + & (table.voucher_no != voucher_no) + & (table.voucher_type != voucher_type) + ) + ) + + last_valuation_rate = query.run() + if last_valuation_rate: + return flt(last_valuation_rate[0][0]) + # Get moving average rate of a specific batch number if warehouse and serial_and_batch_bundle: batch_obj = BatchNoValuation( @@ -1563,8 +1585,6 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): next_stock_reco_detail = get_next_stock_reco(args) if next_stock_reco_detail: detail = next_stock_reco_detail[0] - if detail.batch_no or (detail.serial_and_batch_bundle and detail.has_batch_no): - regenerate_sle_for_batch_stock_reco(detail) # add condition to update SLEs before this date & time datetime_limit_condition = get_datetime_limit_condition(detail) @@ -1593,16 +1613,6 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): validate_negative_qty_in_future_sle(args, allow_negative_stock) -def regenerate_sle_for_batch_stock_reco(detail): - doc = frappe.get_cached_doc("Stock Reconciliation", detail.voucher_no) - doc.recalculate_current_qty(detail.item_code, detail.batch_no) - - if not frappe.db.exists( - "Repost Item Valuation", {"voucher_no": doc.name, "status": "Queued", "docstatus": "1"} - ): - doc.repost_future_sle_and_gle(force=True) - - def get_stock_reco_qty_shift(args): stock_reco_qty_shift = 0 if args.get("is_cancelled"): From f14d1eb8716e1a816af4ed69f9dd1f6a4d30f035 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 3 Nov 2023 17:56:40 +0530 Subject: [PATCH 16/18] chore: performance optimization on payment ledger entry doctype --- .../payment_ledger_entry.json | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json index 4ae813571af6..28c9529995dd 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -65,7 +65,8 @@ "fieldtype": "Link", "in_standard_filter": 1, "label": "Voucher Type", - "options": "DocType" + "options": "DocType", + "search_index": 1 }, { "fieldname": "voucher_no", @@ -73,14 +74,16 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Voucher No", - "options": "voucher_type" + "options": "voucher_type", + "search_index": 1 }, { "fieldname": "against_voucher_type", "fieldtype": "Link", "in_standard_filter": 1, "label": "Against Voucher Type", - "options": "DocType" + "options": "DocType", + "search_index": 1 }, { "fieldname": "against_voucher_no", @@ -88,7 +91,8 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Against Voucher No", - "options": "against_voucher_type" + "options": "against_voucher_type", + "search_index": 1 }, { "fieldname": "amount", @@ -148,13 +152,14 @@ { "fieldname": "voucher_detail_no", "fieldtype": "Data", - "label": "Voucher Detail No" + "label": "Voucher Detail No", + "search_index": 1 } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-10-30 16:15:00.470283", + "modified": "2023-11-03 16:39:58.904113", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", From 568d5bfbe82088bb1aea52b4541d16f23ccec931 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Fri, 3 Nov 2023 22:35:08 +0530 Subject: [PATCH 17/18] chore: rename daily_depreciation in asset to depreciation_amount_based_on_num_days_in_month [dev] (#37893) * chore: rename daily_depreciation to depreciation_based_on_num_days_in_month * chore: add patch * chore: remove unnecessary files * chore: add amount in field name * chore: add amount in label --- erpnext/assets/doctype/asset/asset.py | 2 +- erpnext/assets/doctype/asset/test_asset.py | 9 +++++--- .../asset_depreciation_schedule.json | 8 +++---- .../asset_depreciation_schedule.py | 8 ++++--- .../asset_finance_book.json | 18 ++++++++-------- erpnext/patches.txt | 1 + ...ation_amount_based_on_num_days_in_month.py | 21 +++++++++++++++++++ 7 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 9d3563493309..c003afe118f5 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -818,7 +818,7 @@ def get_item_details(item_code, asset_category, gross_purchase_amount): "depreciation_method": d.depreciation_method, "total_number_of_depreciations": d.total_number_of_depreciations, "frequency_of_depreciation": d.frequency_of_depreciation, - "daily_depreciation": d.daily_depreciation, + "depreciation_amount_based_on_num_days_in_month": d.depreciation_amount_based_on_num_days_in_month, "salvage_value_percentage": d.salvage_value_percentage, "expected_value_after_useful_life": flt(gross_purchase_amount) * flt(d.salvage_value_percentage / 100), diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index d69f5ef0b79f..cdaccbbfbe26 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -755,7 +755,9 @@ def test_schedule_for_straight_line_method(self): self.assertEqual(schedules, expected_schedules) - def test_schedule_for_straight_line_method_with_daily_depreciation(self): + def test_schedule_for_straight_line_method_with_depreciation_amount_based_on_num_days_in_month( + self, + ): asset = create_asset( calculate_depreciation=1, available_for_use_date="2023-01-01", @@ -764,7 +766,7 @@ def test_schedule_for_straight_line_method_with_daily_depreciation(self): depreciation_start_date="2023-01-31", total_number_of_depreciations=12, frequency_of_depreciation=1, - daily_depreciation=1, + depreciation_amount_based_on_num_days_in_month=1, ) expected_schedules = [ @@ -1760,7 +1762,8 @@ def create_asset(**args): "total_number_of_depreciations": args.total_number_of_depreciations or 5, "expected_value_after_useful_life": args.expected_value_after_useful_life or 0, "depreciation_start_date": args.depreciation_start_date, - "daily_depreciation": args.daily_depreciation or 0, + "depreciation_amount_based_on_num_days_in_month": args.depreciation_amount_based_on_num_days_in_month + or 0, }, ) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json index 3772ef4d683c..6f07d84bcb22 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json @@ -19,7 +19,7 @@ "depreciation_method", "total_number_of_depreciations", "rate_of_depreciation", - "daily_depreciation", + "depreciation_amount_based_on_num_days_in_month", "column_break_8", "frequency_of_depreciation", "expected_value_after_useful_life", @@ -179,9 +179,9 @@ { "default": "0", "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "daily_depreciation", + "fieldname": "depreciation_amount_based_on_num_days_in_month", "fieldtype": "Check", - "label": "Daily Depreciation", + "label": "Depreciation amount based on number of days in the month", "print_hide": 1, "read_only": 1 } @@ -189,7 +189,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-08-10 22:22:09.722968", + "modified": "2023-11-03 21:32:15.021796", "modified_by": "Administrator", "module": "Assets", "name": "Asset Depreciation Schedule", diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 7a88ffc5b7f8..109f96f9f4d6 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -153,7 +153,9 @@ def set_draft_asset_depr_schedule_details(self, asset_doc, row): self.frequency_of_depreciation = row.frequency_of_depreciation self.rate_of_depreciation = row.rate_of_depreciation self.expected_value_after_useful_life = row.expected_value_after_useful_life - self.daily_depreciation = row.daily_depreciation + self.depreciation_amount_based_on_num_days_in_month = ( + row.depreciation_amount_based_on_num_days_in_month + ) self.status = "Draft" def make_depr_schedule( @@ -573,7 +575,7 @@ def get_straight_line_or_manual_depr_amount( ) # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: - if row.daily_depreciation: + if row.depreciation_amount_based_on_num_days_in_month: daily_depr_amount = ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) ) / date_diff( @@ -618,7 +620,7 @@ def get_straight_line_or_manual_depr_amount( ) / number_of_pending_depreciations # if the Depreciation Schedule is being prepared for the first time else: - if row.daily_depreciation: + if row.depreciation_amount_based_on_num_days_in_month: daily_depr_amount = ( flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index 2c27dc9aca4d..df560692c505 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -8,7 +8,7 @@ "finance_book", "depreciation_method", "total_number_of_depreciations", - "daily_depreciation", + "depreciation_amount_based_on_num_days_in_month", "column_break_5", "frequency_of_depreciation", "depreciation_start_date", @@ -86,23 +86,23 @@ "fieldtype": "Percent", "label": "Rate of Depreciation" }, - { - "default": "0", - "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "daily_depreciation", - "fieldtype": "Check", - "label": "Daily Depreciation" - }, { "fieldname": "salvage_value_percentage", "fieldtype": "Percent", "label": "Salvage Value Percentage" + }, + { + "default": "0", + "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", + "fieldname": "depreciation_amount_based_on_num_days_in_month", + "fieldtype": "Check", + "label": "Depreciation amount based on number of days in the month" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-09-29 15:39:52.740594", + "modified": "2023-11-03 21:30:24.266601", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 78d2c2c34083..ae2caa71eeb7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -345,5 +345,6 @@ erpnext.patches.v14_0.rename_over_order_allowance_field erpnext.patches.v14_0.migrate_delivery_stop_lock_field execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) +erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py b/erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py new file mode 100644 index 000000000000..63dc0e09bc95 --- /dev/null +++ b/erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py @@ -0,0 +1,21 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.utils.rename_field import rename_field + + +def execute(): + try: + rename_field( + "Asset Finance Book", "daily_depreciation", "depreciation_amount_based_on_num_days_in_month" + ) + rename_field( + "Asset Depreciation Schedule", + "daily_depreciation", + "depreciation_amount_based_on_num_days_in_month", + ) + + except Exception as e: + if e.args[0] != 1054: + raise From 2ce6bbf291d656542492106555ef10ac920731c1 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Sat, 4 Nov 2023 01:41:46 +0530 Subject: [PATCH 18/18] chore: rename depreciation_amount_based_on_num_days_in_month to daily_prorata_based [dev] (#37897) chore: rename depreciation_amount_based_on_num_days_in_month to daily_prorata_based --- erpnext/assets/doctype/asset/asset.py | 2 +- erpnext/assets/doctype/asset/test_asset.py | 7 +++---- .../asset_depreciation_schedule.json | 6 +++--- .../asset_depreciation_schedule.py | 8 +++---- .../asset_finance_book.json | 6 +++--- erpnext/patches.txt | 1 + ...um_days_in_month_to_daily_prorata_based.py | 21 +++++++++++++++++++ 7 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index c003afe118f5..3c570d1af0fe 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -818,7 +818,7 @@ def get_item_details(item_code, asset_category, gross_purchase_amount): "depreciation_method": d.depreciation_method, "total_number_of_depreciations": d.total_number_of_depreciations, "frequency_of_depreciation": d.frequency_of_depreciation, - "depreciation_amount_based_on_num_days_in_month": d.depreciation_amount_based_on_num_days_in_month, + "daily_prorata_based": d.daily_prorata_based, "salvage_value_percentage": d.salvage_value_percentage, "expected_value_after_useful_life": flt(gross_purchase_amount) * flt(d.salvage_value_percentage / 100), diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index cdaccbbfbe26..9e3ec6faa81d 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -755,7 +755,7 @@ def test_schedule_for_straight_line_method(self): self.assertEqual(schedules, expected_schedules) - def test_schedule_for_straight_line_method_with_depreciation_amount_based_on_num_days_in_month( + def test_schedule_for_straight_line_method_with_daily_prorata_based( self, ): asset = create_asset( @@ -766,7 +766,7 @@ def test_schedule_for_straight_line_method_with_depreciation_amount_based_on_num depreciation_start_date="2023-01-31", total_number_of_depreciations=12, frequency_of_depreciation=1, - depreciation_amount_based_on_num_days_in_month=1, + daily_prorata_based=1, ) expected_schedules = [ @@ -1762,8 +1762,7 @@ def create_asset(**args): "total_number_of_depreciations": args.total_number_of_depreciations or 5, "expected_value_after_useful_life": args.expected_value_after_useful_life or 0, "depreciation_start_date": args.depreciation_start_date, - "depreciation_amount_based_on_num_days_in_month": args.depreciation_amount_based_on_num_days_in_month - or 0, + "daily_prorata_based": args.daily_prorata_based or 0, }, ) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json index 6f07d84bcb22..8d8b46321fc3 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json @@ -19,7 +19,7 @@ "depreciation_method", "total_number_of_depreciations", "rate_of_depreciation", - "depreciation_amount_based_on_num_days_in_month", + "daily_prorata_based", "column_break_8", "frequency_of_depreciation", "expected_value_after_useful_life", @@ -179,9 +179,9 @@ { "default": "0", "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "depreciation_amount_based_on_num_days_in_month", + "fieldname": "daily_prorata_based", "fieldtype": "Check", - "label": "Depreciation amount based on number of days in the month", + "label": "Depreciate based on daily pro-rata", "print_hide": 1, "read_only": 1 } diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 109f96f9f4d6..7305691f97c4 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -153,9 +153,7 @@ def set_draft_asset_depr_schedule_details(self, asset_doc, row): self.frequency_of_depreciation = row.frequency_of_depreciation self.rate_of_depreciation = row.rate_of_depreciation self.expected_value_after_useful_life = row.expected_value_after_useful_life - self.depreciation_amount_based_on_num_days_in_month = ( - row.depreciation_amount_based_on_num_days_in_month - ) + self.daily_prorata_based = row.daily_prorata_based self.status = "Draft" def make_depr_schedule( @@ -575,7 +573,7 @@ def get_straight_line_or_manual_depr_amount( ) # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: - if row.depreciation_amount_based_on_num_days_in_month: + if row.daily_prorata_based: daily_depr_amount = ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) ) / date_diff( @@ -620,7 +618,7 @@ def get_straight_line_or_manual_depr_amount( ) / number_of_pending_depreciations # if the Depreciation Schedule is being prepared for the first time else: - if row.depreciation_amount_based_on_num_days_in_month: + if row.daily_prorata_based: daily_depr_amount = ( flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index df560692c505..e597d5fe31e6 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -8,7 +8,7 @@ "finance_book", "depreciation_method", "total_number_of_depreciations", - "depreciation_amount_based_on_num_days_in_month", + "daily_prorata_based", "column_break_5", "frequency_of_depreciation", "depreciation_start_date", @@ -94,9 +94,9 @@ { "default": "0", "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "depreciation_amount_based_on_num_days_in_month", + "fieldname": "daily_prorata_based", "fieldtype": "Check", - "label": "Depreciation amount based on number of days in the month" + "label": "Depreciate based on daily pro-rata" } ], "index_web_pages_for_search": 1, diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ae2caa71eeb7..1e5b08bf36f1 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -346,5 +346,6 @@ erpnext.patches.v14_0.migrate_delivery_stop_lock_field execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month +erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py b/erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py new file mode 100644 index 000000000000..2c03c23d8754 --- /dev/null +++ b/erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py @@ -0,0 +1,21 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.utils.rename_field import rename_field + + +def execute(): + try: + rename_field( + "Asset Finance Book", "depreciation_amount_based_on_num_days_in_month", "daily_prorata_based" + ) + rename_field( + "Asset Depreciation Schedule", + "depreciation_amount_based_on_num_days_in_month", + "daily_prorata_based", + ) + + except Exception as e: + if e.args[0] != 1054: + raise