From 4b07d4a1b9a711adf4cf484896857306c7b916c0 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 22 Dec 2025 17:05:29 +0530 Subject: [PATCH 1/2] fix: create delivery notes condering customer AND addresses --- erpnext/stock/doctype/pick_list/pick_list.js | 4 +- erpnext/stock/doctype/pick_list/pick_list.py | 47 +++++++++++++++----- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 192b4720669..6e95243eb24 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -129,13 +129,13 @@ frappe.ui.form.on("Pick List", { if (frm.doc.purpose === "Delivery") { frm.add_custom_button( - __("Create Delivery Note"), + __("Delivery Note"), () => frm.trigger("create_delivery_note"), __("Create") ); } else { frm.add_custom_button( - __("Create Stock Entry"), + __("Stock Entry"), () => frm.trigger("create_stock_entry"), __("Create") ); diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 83149143791..fd59a9850bc 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -1227,26 +1227,48 @@ def create_delivery_note(source_name, target_doc=None): validate_item_locations(pick_list) sales_dict = dict() sales_orders = [] - delivery_note = None + delivery_notes = [] for location in pick_list.locations: if location.sales_order: sales_orders.append( frappe.db.get_value( - "Sales Order", location.sales_order, ["customer", "name as sales_order"], as_dict=True + "Sales Order", + location.sales_order, + [ + "customer", + "name as sales_order", + "company_address", + "dispatch_address_name", + "shipping_address_name", + "customer_address", + ], + as_dict=True, ) ) - group_key = lambda so: so["customer"] # noqa - for customer, rows in groupby(sorted(sales_orders, key=group_key), key=group_key): - sales_dict[customer] = {row.sales_order for row in rows} + group_key = lambda so: ( # noqa + so["customer"], + so["company_address"], + so["dispatch_address_name"], + so["shipping_address_name"], + so["customer_address"], + ) + for key, rows in groupby(sorted(sales_orders, key=group_key), key=group_key): + sales_dict[key] = {row.sales_order for row in rows} if sales_dict: - delivery_note = create_dn_with_so(sales_dict, pick_list) + delivery_notes.extend(create_dn_with_so(sales_dict, pick_list)) if not all(item.sales_order for item in pick_list.locations): - delivery_note = create_dn_wo_so(pick_list) + delivery_notes.append(create_dn_wo_so(pick_list)) - return delivery_note + if len(delivery_notes) == 1: + return delivery_notes[0] + else: + from frappe.utils import comma_and + + doc_list = [get_link_to_form("Delivery Note", p.name) for p in delivery_notes] + frappe.msgprint(_("{0} created").format(comma_and(doc_list))) def create_dn_wo_so(pick_list, delivery_note=None): @@ -1309,17 +1331,18 @@ def create_dn_for_pick_lists(source_name, target_doc=None, kwargs=None): def create_dn_with_so(sales_dict, pick_list): """Create Delivery Note for each customer (based on SO) in a Pick List.""" - delivery_note = None + delivery_notes = [] - for customer in sales_dict: - delivery_note = create_dn_from_so(pick_list, sales_dict[customer], None) + for key in sales_dict: + delivery_note = create_dn_from_so(pick_list, sales_dict[key], None) if delivery_note: delivery_note.flags.ignore_mandatory = True # updates packed_items on save # save as multiple customers are possible delivery_note.save() + delivery_notes.append(delivery_note) - return delivery_note + return delivery_notes def create_dn_from_so(pick_list, sales_order_list, delivery_note=None, kwargs=None): From 6ac88f9bbf33c133182229b29ef4346448e79d6f Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 22 Dec 2025 21:50:00 +0530 Subject: [PATCH 2/2] test: add test case --- erpnext/stock/doctype/pick_list/pick_list.py | 8 +-- .../stock/doctype/pick_list/test_pick_list.py | 69 +++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index fd59a9850bc..aeeacaba655 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -1248,10 +1248,10 @@ def create_delivery_note(source_name, target_doc=None): group_key = lambda so: ( # noqa so["customer"], - so["company_address"], - so["dispatch_address_name"], - so["shipping_address_name"], - so["customer_address"], + so["company_address"] or "", + so["dispatch_address_name"] or "", + so["shipping_address_name"] or "", + so["customer_address"] or "", ) for key, rows in groupby(sorted(sales_orders, key=group_key), key=group_key): sales_dict[key] = {row.sales_order for row in rows} diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index bb15014b2cd..87c2275d3d1 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -1525,3 +1525,72 @@ class TestPickList(IntegrationTestCase): pick_list.cancel() sales_order.cancel() stock_entry.cancel() + + def test_creating_dn_from_so_with_different_addresses(self): + warehouse = "_Test Warehouse - _TC" + item1 = make_item().name + item2 = make_item().name + item3 = make_item().name + + customer_shipping_address_1 = frappe.get_doc( + { + "doctype": "Address", + "address_title": "Customer Shipping Address", + "address_type": "Shipping", + "address_line1": "123, ABC Street", + "city": "Mumbai", + "country": "India", + "links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}], + "is_shipping_address": 1, + } + ).insert() + + sales_order1 = make_sales_order(item_code=item1, qty=1) + make_stock_entry(item=item1, to_warehouse=warehouse, qty=1) + sales_order2 = make_sales_order(item_code=item2, qty=1) + make_stock_entry(item=item2, to_warehouse=warehouse, qty=1) + sales_order3 = make_sales_order(item_code=item3, qty=1, do_not_submit=True) + + customer_shipping_address_2 = frappe.get_doc( + { + "doctype": "Address", + "address_title": "Customer Shipping Address", + "address_type": "Shipping", + "address_line1": "123, ABC Street", + "city": "Mumbai", + "country": "India", + "links": [{"link_doctype": "Customer", "link_name": "_Test Customer"}], + } + ).insert() + sales_order3.shipping_address_name = customer_shipping_address_2.name + sales_order3.submit() + make_stock_entry(item=item3, to_warehouse=warehouse, qty=1) + + from json import dumps + + from frappe.model.mapper import map_docs + + pick_list = frappe.new_doc("Pick List") + map_docs( + "erpnext.selling.doctype.sales_order.sales_order.create_pick_list", + dumps([sales_order1.name, sales_order2.name, sales_order3.name]), + pick_list, + ) + pick_list.submit() + + create_delivery_note(pick_list.name) + delivery_notes = frappe.get_all( + "Delivery Note Item", filters={"against_pick_list": pick_list.name}, pluck="parent", distinct=True + ) + self.assertEqual(len(delivery_notes), 2) + + for delivery_note in delivery_notes: + doc = frappe.get_doc("Delivery Note", delivery_note) + if len(doc.items) == 1: + self.assertEqual(doc.items[0].item_code, item3) + self.assertEqual(doc.shipping_address_name, customer_shipping_address_2.name) + else: + self.assertEqual(doc.shipping_address_name, customer_shipping_address_1.name) + item_codes = [item.item_code for item in doc.items] + self.assertTrue(item1 in item_codes) + self.assertTrue(item2 in item_codes)