Product Bundle improvement

This change allows addition of multiple instances of the same product
inside a product bundle, if each has a different description in the
bundle list (thus no duplicates).

We use product bundles to include scrap material into our stock
calculations. Here we need to enter the same product with different
quantities into the same bundle. We explicitly specify scrap in the
description of each line in product bundle.
diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json
index b09765d..24cd09b 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.json
+++ b/erpnext/stock/doctype/packed_item/packed_item.json
@@ -523,6 +523,32 @@
    "search_index": 0, 
    "set_only_once": 0, 
    "unique": 0
+  },
+  {
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "fieldname": "packing_description", 
+   "fieldtype": "Text Editor", 
+   "hidden": 1, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
+   "label": "Packing Description", 
+   "length": 0, 
+   "no_copy": 0, 
+   "oldfieldname": "packing_description", 
+   "oldfieldtype": "Text", 
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "print_width": "300px", 
+   "read_only": 1, 
+   "report_hide": 1, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0, 
+   "width": "300px"
   }
  ], 
  "hide_heading": 0, 
diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py
index 0b69d31..af2d554 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -14,7 +14,7 @@
 	pass
 
 def get_product_bundle_items(item_code):
-	return frappe.db.sql("""select t1.item_code, t1.qty, t1.uom
+	return frappe.db.sql("""select t1.item_code, t1.qty, t1.uom, t1.description
 		from `tabProduct Bundle Item` t1, `tabProduct Bundle` t2
 		where t2.new_item_code=%s and t1.parent = t2.name""", item_code, as_dict=1)
 
@@ -27,14 +27,14 @@
 		where item_code = %s and warehouse = %s""", (item, warehouse), as_dict = 1)
 	return det and det[0] or frappe._dict()
 
-def update_packing_list_item(doc, packing_item_code, qty, main_item_row):
+def update_packing_list_item(doc, packing_item_code, qty, main_item_row, packing_description):
 	bin = get_bin_qty(packing_item_code, main_item_row.warehouse)
 	item = get_packing_item_details(packing_item_code)
 
 	# check if exists
 	exists = 0
 	for d in doc.get("packed_items"):
-		if d.parent_item == main_item_row.item_code and d.item_code == packing_item_code and d.parent_detail_docname == main_item_row.name:
+		if d.parent_item == main_item_row.item_code and d.item_code == packing_item_code and d.parent_detail_docname == main_item_row.name and d.packing_description == packing_description:
 			pi, exists = d, 1
 			break
 
@@ -50,6 +50,7 @@
 	pi.qty = flt(qty)
 	pi.actual_qty = flt(bin.get("actual_qty"))
 	pi.projected_qty = flt(bin.get("projected_qty"))
+	pi.packing_description = packing_description
 	if not pi.warehouse:
 		pi.warehouse = main_item_row.warehouse
 	if not pi.batch_no:
@@ -66,7 +67,7 @@
 	for d in doc.get("items"):
 		if frappe.db.get_value("Product Bundle", {"new_item_code": d.item_code}):
 			for i in get_product_bundle_items(d.item_code):
-				update_packing_list_item(doc, i.item_code, flt(i.qty)*flt(d.qty), d)
+				update_packing_list_item(doc, i.item_code, flt(i.qty)*flt(d.qty), d, i.description)
 
 			if [d.item_code, d.name] not in parent_items:
 				parent_items.append([d.item_code, d.name])