From 69463b47986fdc190ca651d0d0d3cab6b74303fb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 21:56:36 +0530 Subject: [PATCH 1/4] fix: filter of item for manufacture type material request (backport #47712) (#47716) * fix: filter of item for manufacture type material request (#47712) (cherry picked from commit 874750f9ceca3c6351ea7bdc09939326588d5489) # Conflicts: # erpnext/stock/doctype/material_request/material_request.js * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../material_request/material_request.js | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index c32bd53af5e..aaffe50359e 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -497,26 +497,23 @@ erpnext.buying.MaterialRequestController = class MaterialRequestController exten } onload(doc, cdt, cdn) { - this.frm.set_query("item_code", "items", function() { + this.frm.set_query("item_code", "items", function () { + let filters = { is_stock_item: 1 }; + if (doc.material_request_type == "Customer Provided") { - return{ - query: "erpnext.controllers.queries.item_query", - filters:{ - 'customer': me.frm.doc.customer, - 'is_stock_item':1 - } - } - } else if (doc.material_request_type == "Purchase") { - return{ - query: "erpnext.controllers.queries.item_query", - filters: {'is_purchase_item': 1} - } - } else { - return{ - query: "erpnext.controllers.queries.item_query", - filters: {'is_stock_item': 1} - } + filters.customer = doc.customer; + } else if ( + doc.material_request_type == "Purchase" + ) { + filters = { is_purchase_item: 1 }; + } else if (doc.material_request_type == "Manufacture") { + filters.include_item_in_manufacturing = 1; } + + return { + query: "erpnext.controllers.queries.item_query", + filters: filters, + }; }); } From b664781faed74c21d0997ed1aef1537a3b65d2f9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 19:21:20 +0200 Subject: [PATCH 2/4] feat: add column "Item Name" to "BOM Stock Report" (backport #47116) (#47484) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrick Eißler <77415730+PatrickDEissler@users.noreply.github.com> Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- .../manufacturing/report/bom_stock_report/bom_stock_report.py | 2 ++ .../report/bom_stock_report/test_bom_stock_report.py | 1 + 2 files changed, 3 insertions(+) diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py index 48ffbac5820..d233643c244 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py @@ -23,6 +23,7 @@ def get_columns(): """return columns""" columns = [ _("Item") + ":Link/Item:150", + _("Item Name") + "::240", _("Description") + "::300", _("BOM Qty") + ":Float:160", _("BOM UoM") + "::160", @@ -73,6 +74,7 @@ def get_bom_stock(filters): .on((BOM_ITEM.item_code == BIN.item_code) & (CONDITIONS)) .select( BOM_ITEM.item_code, + BOM_ITEM.item_name, BOM_ITEM.description, BOM_ITEM.stock_qty, BOM_ITEM.stock_uom, diff --git a/erpnext/manufacturing/report/bom_stock_report/test_bom_stock_report.py b/erpnext/manufacturing/report/bom_stock_report/test_bom_stock_report.py index 1c56ebe24d4..8a6915d7f5f 100644 --- a/erpnext/manufacturing/report/bom_stock_report/test_bom_stock_report.py +++ b/erpnext/manufacturing/report/bom_stock_report/test_bom_stock_report.py @@ -94,6 +94,7 @@ def get_expected_data(bom, warehouse, qty_to_produce, show_exploded_view=False): expected_data.append( [ item.item_code, + item.item_name, item.description, item.stock_qty, item.stock_uom, From ca4858318e8209a2c4cb0e68fc6f8e3c15c833be Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 29 May 2025 11:18:08 +0530 Subject: [PATCH 3/4] fix: use `query.walk() `for escaping special chars in receiable/payable report (cherry picked from commit a0a51b507496574d16caf5ce65c84806a08cf939) --- .../report/accounts_receivable/accounts_receivable.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 5e31542fa44..a87f359f564 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -118,7 +118,8 @@ class ReceivablePayableReport: self.build_data() def fetch_ple_in_buffered_cursor(self): - self.ple_entries = frappe.db.sql(self.ple_query.get_sql(), as_dict=True) + query, param = self.ple_query.walk() + self.ple_entries = frappe.db.sql(query, param, as_dict=True) for ple in self.ple_entries: self.init_voucher_balance(ple) # invoiced, paid, credit_note, outstanding @@ -131,8 +132,9 @@ class ReceivablePayableReport: def fetch_ple_in_unbuffered_cursor(self): self.ple_entries = [] + query, param = self.ple_query.walk() with frappe.db.unbuffered_cursor(): - for ple in frappe.db.sql(self.ple_query.get_sql(), as_dict=True, as_iterator=True): + for ple in frappe.db.sql(query, param, as_dict=True, as_iterator=True): self.init_voucher_balance(ple) # invoiced, paid, credit_note, outstanding self.ple_entries.append(ple) From df779bb7dd7b42927dadf4967fac61a946c8e4b8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 30 May 2025 14:01:49 +0530 Subject: [PATCH 4/4] fix: incorrect actual qty in product bundle balance report (backport #47791) (#47813) fix: incorrect actual qty in product bundle balance report (#47791) (cherry picked from commit c544c3e01810225c66da926963ff2758c6fd9b39) Co-authored-by: rohitwaghchaure --- .../product_bundle_balance.py | 65 ++++++++++++++----- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py index 10f8650b525..5a8960396b3 100644 --- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py +++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py @@ -4,7 +4,7 @@ import frappe from frappe import _ -from frappe.query_builder.functions import IfNull +from frappe.query_builder.functions import IfNull, Max from frappe.utils import flt from pypika.terms import ExistsCriterion @@ -208,30 +208,21 @@ def get_stock_ledger_entries(filters, items): if not items: return [] - sle = frappe.qb.DocType("Stock Ledger Entry") - sle2 = frappe.qb.DocType("Stock Ledger Entry") + max_posting_datetime_query = get_item_wise_max_posting_datetime(filters, items) + sle = frappe.qb.DocType("Stock Ledger Entry") query = ( frappe.qb.from_(sle) - .left_join(sle2) + .join(max_posting_datetime_query) .on( - (sle.item_code == sle2.item_code) - & (sle.warehouse == sle2.warehouse) - & (sle.posting_datetime < sle2.posting_datetime) - & (sle.name < sle2.name) + (sle.item_code == max_posting_datetime_query.item_code) + & (sle.warehouse == max_posting_datetime_query.warehouse) + & (sle.posting_datetime == max_posting_datetime_query.posting_datetime) ) .select(sle.item_code, sle.warehouse, sle.qty_after_transaction, sle.company) - .where((sle2.name.isnull()) & (sle.docstatus < 2) & (sle.item_code.isin(items))) + .where(sle.is_cancelled == 0) ) - if filters.get("company"): - query = query.where(sle.company == filters.get("company")) - - if date := filters.get("date"): - query = query.where(sle.posting_date <= date) - else: - frappe.throw(_("'Date' is required")) - if filters.get("warehouse"): warehouse_details = frappe.db.get_value( "Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1 @@ -247,4 +238,44 @@ def get_stock_ledger_entries(filters, items): ) ) + if filters.get("company"): + query = query.where(sle.company == filters.get("company")) + + if filters.get("data"): + query = query.where(sle.posting_date <= filters.get("data")) + return query.run(as_dict=True) + + +def get_item_wise_max_posting_datetime(filters, items): + """Get the maximum Stock Ledger Entry name for the given filters and items.""" + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(sle) + .select(sle.item_code, sle.warehouse, sle.name, Max(sle.posting_datetime).as_("posting_datetime")) + .where(sle.item_code.isin(items) & (sle.is_cancelled == 0)) + .groupby(sle.item_code, sle.warehouse) + ) + + if filters.get("warehouse"): + warehouse_details = frappe.db.get_value( + "Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1 + ) + + if warehouse_details: + wh = frappe.qb.DocType("Warehouse") + query = query.where( + sle.warehouse.isin( + frappe.qb.from_(wh) + .select(wh.name) + .where((wh.lft >= warehouse_details.lft) & (wh.rgt <= warehouse_details.rgt)) + ) + ) + + if filters.get("company"): + query = query.where(sle.company == filters.get("company")) + + if filters.get("data"): + query = query.where(sle.posting_date <= filters.get("data")) + + return query