feat: Pick list from SO with Product Bundle
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index d3b4286..1d172ad 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -1232,10 +1232,27 @@
 
 @frappe.whitelist()
 def create_pick_list(source_name, target_doc=None):
-	def update_item_quantity(source, target, source_parent):
+	from erpnext.stock.doctype.packed_item.packed_item import is_product_bundle
+
+	def update_item_quantity(source, target, source_parent) -> None:
 		target.qty = flt(source.qty) - flt(source.delivered_qty)
 		target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor)
 
+	def update_packed_item_qty(source, target, source_parent) -> None:
+		qty = flt(source.qty)
+		for item in source_parent.items:
+			if source.parent_detail_docname == item.name:
+				pending_percent = (item.qty - item.delivered_qty) / item.qty
+				target.qty = target.stock_qty = qty * pending_percent
+				return
+
+	def should_pick_order_item(item) -> bool:
+		return (
+			abs(item.delivered_qty) < abs(item.qty)
+			and item.delivered_by_supplier != 1
+			and not is_product_bundle(item.item_code)
+		)
+
 	doc = get_mapped_doc(
 		"Sales Order",
 		source_name,
@@ -1245,8 +1262,16 @@
 				"doctype": "Pick List Item",
 				"field_map": {"parent": "sales_order", "name": "sales_order_item"},
 				"postprocess": update_item_quantity,
-				"condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty)
-				and doc.delivered_by_supplier != 1,
+				"condition": should_pick_order_item,
+			},
+			"Packed Item": {
+				"doctype": "Pick List Item",
+				"field_map": {
+					"parent": "sales_order",
+					"name": "sales_order_item",
+					"parent_detail_docname": "product_bundle_item",
+				},
+				"postprocess": update_packed_item_qty,
 			},
 		},
 		target_doc,
diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json
index e94c34d..4e67c84 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.json
+++ b/erpnext/stock/doctype/packed_item/packed_item.json
@@ -29,6 +29,7 @@
   "ordered_qty",
   "column_break_16",
   "incoming_rate",
+  "picked_qty",
   "page_break",
   "prevdoc_doctype",
   "parent_detail_docname"
@@ -234,13 +235,19 @@
    "label": "Ordered Qty",
    "no_copy": 1,
    "read_only": 1
+  },
+  {
+   "depends_on": "picked_qty",
+   "fieldname": "picked_qty",
+   "fieldtype": "Float",
+   "label": "Picked Qty"
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-03-10 15:42:00.265915",
+ "modified": "2022-04-21 08:05:29.785362",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Packed Item",
diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py
index 026dd4e..4d05d7a 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -32,7 +32,7 @@
 	reset = reset_packing_list(doc)
 
 	for item_row in doc.get("items"):
-		if frappe.db.exists("Product Bundle", {"new_item_code": item_row.item_code}):
+		if is_product_bundle(item_row.item_code):
 			for bundle_item in get_product_bundle_items(item_row.item_code):
 				pi_row = add_packed_item_row(
 					doc=doc,
@@ -54,6 +54,10 @@
 		set_product_bundle_rate_amount(doc, parent_items_price)  # set price in bundle item
 
 
+def is_product_bundle(item_code: str) -> bool:
+	return bool(frappe.db.exists("Product Bundle", {"new_item_code": item_code}))
+
+
 def get_indexed_packed_items_table(doc):
 	"""
 	Create dict from stale packed items table like:
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index 1ba46f7..d1a9472 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -10,6 +10,7 @@
 from erpnext.stock.doctype.item.test_item import create_item, make_item
 from erpnext.stock.doctype.pick_list.pick_list import create_delivery_note
 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
 	EmptyStockReconciliationItemsError,
 )
@@ -582,16 +583,19 @@
 
 	def test_picklist_with_bundles(self):
 		# from test_records.json
+		warehouse = "_Test Warehouse - _TC"
 		bundle = "_Test Product Bundle Item"
 		bundle_items = {"_Test Item": 5, "_Test Item Home Desktop 100": 2}
+		for item in bundle_items:
+			make_stock_entry(item=item, to_warehouse=warehouse, qty=10, rate=10)
 
-		so = make_sales_order(item_code=bundle, qty=1)
+		so = make_sales_order(item_code=bundle, qty=3)
 
 		pl = create_pick_list(so.name)
 		pl.save()
 		self.assertEqual(len(pl.locations), 2)
 		for item in pl.locations:
-			self.assertEqual(item.stock_qty, bundle_items[item.item_code])
+			self.assertEqual(item.stock_qty, bundle_items[item.item_code] * 3)
 
 	# def test_pick_list_skips_items_in_expired_batch(self):
 	# 	pass
diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json
index 805286d..a96ebfc 100644
--- a/erpnext/stock/doctype/pick_list_item/pick_list_item.json
+++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json
@@ -27,6 +27,7 @@
   "column_break_15",
   "sales_order",
   "sales_order_item",
+  "product_bundle_item",
   "material_request",
   "material_request_item"
  ],
@@ -146,6 +147,7 @@
   {
    "fieldname": "sales_order_item",
    "fieldtype": "Data",
+   "hidden": 1,
    "label": "Sales Order Item",
    "read_only": 1
   },
@@ -177,11 +179,19 @@
    "fieldtype": "Data",
    "label": "Item Group",
    "read_only": 1
+  },
+  {
+   "description": "product bundle item row's name in sales order. Also indicates that picked item is to be used for a product bundle",
+   "fieldname": "product_bundle_item",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Product Bundle Item",
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-09-28 12:02:16.923056",
+ "modified": "2022-04-22 05:27:38.497997",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Pick List Item",
@@ -190,5 +200,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file