fix: Update Packed Items via Update Items in SO (#22392)

diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index f54b593..ead503e 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -20,6 +20,7 @@
 from six import text_type
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
 from erpnext.stock.get_item_details import get_item_warehouse
+from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
 
 force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
 
@@ -1301,6 +1302,7 @@
 	parent.set_qty_as_per_stock_uom()
 	parent.calculate_taxes_and_totals()
 	if parent_doctype == "Sales Order":
+		make_packing_list(parent)
 		parent.set_gross_profit()
 	frappe.get_doc('Authorization Control').validate_approving_authority(parent.doctype,
 		parent.company, parent.base_grand_total)
diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
index 85a2b20..7d1d372 100644
--- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py
+++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
@@ -7,7 +7,7 @@
 import frappe
 test_records = frappe.get_test_records('Product Bundle')
 
-def make_product_bundle(parent, items):
+def make_product_bundle(parent, items, qty=None):
 	if frappe.db.exists("Product Bundle", parent):
 		return frappe.get_doc("Product Bundle", parent)
 
@@ -17,7 +17,7 @@
 	})
 
 	for item in items:
-		product_bundle.append("items", {"item_code": item, "qty": 1})
+		product_bundle.append("items", {"item_code": item, "qty": qty or 1})
 
 	product_bundle.insert()
 
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 74e742f..accaa9c 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -2,6 +2,7 @@
 # License: GNU General Public License v3. See license.txt
 from __future__ import unicode_literals
 import frappe
+import json
 from frappe.utils import flt, add_days, nowdate
 import frappe.permissions
 import unittest
@@ -10,9 +11,10 @@
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.selling.doctype.sales_order.sales_order import make_work_orders
 from erpnext.controllers.accounts_controller import update_child_qty_rate
-import json
 from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request
 from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
+from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+from erpnext.stock.doctype.item.test_item import make_item
 
 class TestSalesOrder(unittest.TestCase):
 	def tearDown(self):
@@ -417,6 +419,26 @@
 		self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
 		frappe.set_user("Administrator")
 
+	def test_update_child_qty_rate_product_bundle(self):
+		# test Update Items with product bundle
+		if not frappe.db.exists("Item", "_Product Bundle Item"):
+			bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0})
+			bundle_item.append("item_defaults", {
+					"company": "_Test Company",
+					"default_warehouse": "_Test Warehouse - _TC"})
+			bundle_item.save(ignore_permissions=True)
+
+		make_item("_Packed Item", {"is_stock_item": 1})
+		make_product_bundle("_Product Bundle Item", ["_Packed Item"], 2)
+
+		so = make_sales_order(item_code = "_Test Item", warehouse=None)
+
+		added_item = json.dumps([{"item_code" : "_Product Bundle Item", "rate" : 200, 'qty' : 2}])
+		update_child_qty_rate('Sales Order', added_item, so.name)
+
+		so.reload()
+		self.assertEqual(so.packed_items[0].qty, 4)
+
 	def test_warehouse_user(self):
 		frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
 		frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
@@ -457,8 +479,6 @@
 		self.assertRaises(frappe.CancelledLinkError, dn.submit)
 
 	def test_service_type_product_bundle(self):
-		from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
-		from erpnext.stock.doctype.item.test_item import make_item
 		make_item("_Test Service Product Bundle", {"is_stock_item": 0})
 		make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0})
 		make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0})
@@ -472,8 +492,6 @@
 		self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items])
 
 	def test_mix_type_product_bundle(self):
-		from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
-		from erpnext.stock.doctype.item.test_item import make_item
 		make_item("_Test Mix Product Bundle", {"is_stock_item": 0})
 		make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1})
 		make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0})
@@ -484,7 +502,6 @@
 		self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="")
 
 	def test_auto_insert_price(self):
-		from erpnext.stock.doctype.item.test_item import make_item
 		make_item("_Test Item for Auto Price List", {"is_stock_item": 0})
 		frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
 
@@ -519,7 +536,6 @@
 		from erpnext.buying.doctype.purchase_order.purchase_order import update_status
 
 		make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
-		from erpnext.stock.doctype.item.test_item import make_item
 		po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "delivered_by_supplier": 1})
 
 		dn_item = make_item("_Test Regular Item", {"is_stock_item": 1})
@@ -714,7 +730,6 @@
 
 	def test_serial_no_based_delivery(self):
 		frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1)
-		from erpnext.stock.doctype.item.test_item import make_item
 		item = make_item("_Reserved_Serialized_Item", {"is_stock_item": 1,
 					"maintain_stock": 1,
 					"has_serial_no": 1,
@@ -835,7 +850,6 @@
 		self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
 
 	def test_request_for_raw_materials(self):
-		from erpnext.stock.doctype.item.test_item import make_item
 		item = make_item("_Test Finished Item", {"is_stock_item": 1,
 			"maintain_stock": 1,
 			"valuation_rate": 500,