diff --git a/erpnext/crm/report/lead_details/lead_details.py b/erpnext/crm/report/lead_details/lead_details.py index def3e28af74..7155c53e82a 100644 --- a/erpnext/crm/report/lead_details/lead_details.py +++ b/erpnext/crm/report/lead_details/lead_details.py @@ -4,7 +4,7 @@ import frappe from frappe import _ -from frappe.query_builder.functions import Concat_ws, Date +from frappe.query_builder.functions import Concat_ws, Date, NullIf def execute(filters=None): @@ -93,7 +93,9 @@ def get_data(filters): lead.phone, lead.owner, lead.company, - (Concat_ws(", ", address.address_line1, address.address_line2)).as_("address"), + (Concat_ws(", ", NullIf(address.address_line1, ""), NullIf(address.address_line2, ""))).as_( + "address" + ), address.pincode, address.city, address.state, diff --git a/erpnext/crm/report/lead_details/test_lead_details.py b/erpnext/crm/report/lead_details/test_lead_details.py new file mode 100644 index 00000000000..ec5d9a802a2 --- /dev/null +++ b/erpnext/crm/report/lead_details/test_lead_details.py @@ -0,0 +1,36 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe.utils import add_days, today + +from erpnext.crm.report.lead_details.lead_details import get_data +from erpnext.tests.utils import ERPNextTestSuite + + +class TestLeadDetailsReport(ERPNextTestSuite): + def test_address_column_omits_empty_line(self): + """An empty address_line2 is '' on MariaDB but NULL on Postgres; CONCAT_WS + keeps the empty string (trailing ', ') on MariaDB while Postgres drops it. + The report must render the same clean address on both engines.""" + lead = frappe.get_doc( + {"doctype": "Lead", "lead_name": "_Test PG Lead Address", "company": "_Test Company"} + ).insert() + frappe.get_doc( + { + "doctype": "Address", + "address_title": "_Test PG Lead Address", + "address_type": "Billing", + "address_line1": "221B Baker Street", + "address_line2": "", + "city": "London", + "country": "United Kingdom", + "links": [{"link_doctype": "Lead", "link_name": lead.name}], + } + ).insert() + + filters = frappe._dict( + company="_Test Company", from_date=add_days(today(), -1), to_date=add_days(today(), 1) + ) + row = next(r for r in get_data(filters) if r.get("name") == lead.name) + self.assertEqual(row.get("address"), "221B Baker Street") 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 951a696019e..1d9ed5a2af7 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 @@ -3198,6 +3198,7 @@ def get_picked_batches(kwargs) -> dict[str, dict]: & (table.voucher_type == "Pick List") & (table.voucher_no.isnotnull()) ) + .groupby(child_table.batch_no, child_table.warehouse) ) if kwargs.get("company"): diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py index 93f071a0b1c..2c6aac7d4ce 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py @@ -1355,6 +1355,15 @@ class TestSerialandBatchBundle(ERPNextTestSuite): # Stock queue should have the returned stock: [[5, 100]] self.assertEqual(json.loads(return_sle.stock_queue), [[5, 100]]) + def test_get_picked_batches_runs(self): + from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import get_picked_batches + + # Sum(qty) is selected with bare batch_no/warehouse; without a GROUP BY this + # raises a GroupingError on Postgres (and collapses to one arbitrary row on + # MariaDB). It must run and return a per-(batch, warehouse) mapping on both. + result = get_picked_batches(frappe._dict()) + self.assertIsInstance(result, dict) + def get_batch_from_bundle(bundle): from erpnext.stock.serial_batch_bundle import get_batch_nos