feat: [production_plan -> fetching item description] Fetch item description from Material Request or Sales Order (#19541)

* Change packed item

* Remove description field on update_packed_items

* add possibility to modify description on packed items

* Fetch description from Material Request or Sales Order in production plan, add the possibility to modify the description un production plan

* sync with fork

* Fetch description from Material Request or Sales Order in production plan, Add the possibility to modify description in production plan

* code cleaning syncing fork

* code cleaning syncing fork

* code cleaning syncing fork

* code cleaning syncing fork

* rewied and add item_details.description in case of blank field
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 0f49f73..5d26969 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -99,7 +99,7 @@
 			self.get_mr_items()
 
 	def get_so_items(self):
-		so_list = [d.sales_order for d in self.get("sales_orders", []) if d.sales_order]
+		so_list = [d.sales_order for d in self.sales_orders if d.sales_order]
 		if not so_list:
 			msgprint(_("Please enter Sales Orders in the above table"))
 			return []
@@ -109,7 +109,7 @@
 			item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))
 
 		items = frappe.db.sql("""select distinct parent, item_code, warehouse,
-			(qty - work_order_qty) * conversion_factor as pending_qty, name
+			(qty - work_order_qty) * conversion_factor as pending_qty, description, name
 			from `tabSales Order Item` so_item
 			where parent in (%s) and docstatus = 1 and qty > work_order_qty
 			and exists (select name from `tabBOM` bom where bom.item=so_item.item_code
@@ -121,7 +121,7 @@
 
 		packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as warehouse,
 			(((so_item.qty - so_item.work_order_qty) * pi.qty) / so_item.qty)
-				as pending_qty, pi.parent_item, so_item.name
+				as pending_qty, pi.parent_item, pi.description, so_item.name
 			from `tabSales Order Item` so_item, `tabPacked Item` pi
 			where so_item.parent = pi.parent and so_item.docstatus = 1
 			and pi.parent_item = so_item.item_code
@@ -134,7 +134,7 @@
 		self.calculate_total_planned_qty()
 
 	def get_mr_items(self):
-		mr_list = [d.material_request for d in self.get("material_requests", []) if d.material_request]
+		mr_list = [d.material_request for d in self.material_requests if d.material_request]
 		if not mr_list:
 			msgprint(_("Please enter Material Requests in the above table"))
 			return []
@@ -143,7 +143,7 @@
 		if self.item_code:
 			item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code))
 
-		items = frappe.db.sql("""select distinct parent, name, item_code, warehouse,
+		items = frappe.db.sql("""select distinct parent, name, item_code, warehouse, description,
 			(qty - ordered_qty) as pending_qty
 			from `tabMaterial Request Item` mr_item
 			where parent in (%s) and docstatus = 1 and qty > ordered_qty
@@ -162,7 +162,7 @@
 				'include_exploded_items': 1,
 				'warehouse': data.warehouse,
 				'item_code': data.item_code,
-				'description': item_details and item_details.description or '',
+				'description': data.description or item_details.description,
 				'stock_uom': item_details and item_details.stock_uom or '',
 				'bom_no': item_details and item_details.bom_no or '',
 				'planned_qty': data.pending_qty,
@@ -174,10 +174,12 @@
 			if self.get_items_from == "Sales Order":
 				pi.sales_order = data.parent
 				pi.sales_order_item = data.name
+				pi.description = data.description
 
 			elif self.get_items_from == "Material Request":
 				pi.material_request = data.parent
 				pi.material_request_item = data.name
+				pi.description = data.description
 
 	def calculate_total_planned_qty(self):
 		self.total_planned_qty = 0
@@ -195,7 +197,6 @@
 		for data in self.po_items:
 			if data.name == production_plan_item:
 				data.produced_qty = produced_qty
-				data.pending_qty = data.planned_qty - data.produced_qty
 				data.db_update()
 
 		self.calculate_total_produced_qty()
@@ -302,6 +303,7 @@
 				wo_list.extend(work_orders)
 
 		frappe.flags.mute_messages = False
+
 		if wo_list:
 			wo_list = ["""<a href="#Form/Work Order/%s" target="_blank">%s</a>""" % \
 				(p, p) for p in wo_list]
@@ -309,16 +311,15 @@
 		else :
 			msgprint(_("No Work Orders created"))
 
-
 	def make_work_order_for_sub_assembly_items(self, item):
 		work_orders = []
 		bom_data = {}
 
-		get_sub_assembly_items(item.get("bom_no"), bom_data, item.get("qty"))
+		get_sub_assembly_items(item.get("bom_no"), bom_data)
 
 		for key, data in bom_data.items():
 			data.update({
-				'qty': data.get("stock_qty"),
+				'qty': data.get("stock_qty") * item.get("qty"),
 				'production_plan': self.name,
 				'company': self.company,
 				'fg_warehouse': item.get("fg_warehouse"),
@@ -561,7 +562,7 @@
 		item_filter += " and so_item.item_code = %(item)s"
 
 	open_so = frappe.db.sql("""
-		select distinct so.name, so.transaction_date, so.customer, so.base_grand_total as grand_total
+		select distinct so.name, so.transaction_date, so.customer, so.base_grand_total
 		from `tabSales Order` so, `tabSales Order Item` so_item
 		where so_item.parent = so.name
 			and so.docstatus = 1 and so.status not in ("Stopped", "Closed")
@@ -625,7 +626,7 @@
 	for data in po_items:
 		planned_qty = data.get('required_qty') or data.get('planned_qty')
 		ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty
-		warehouse = warehouse or data.get("warehouse")
+		warehouse = data.get("warehouse") or warehouse
 
 		item_details = {}
 		if data.get("bom") or data.get("bom_no"):
@@ -708,11 +709,11 @@
 
 	return {
 		"bom_no": item_details.get("bom_no"),
-		"stock_uom": item_details.get("stock_uom"),
-		"description": item_details.get("description")
+		"stock_uom": item_details.get("stock_uom")
+#		"description": item_details.get("description")
 	}
 
-def get_sub_assembly_items(bom_no, bom_data, qty):
+def get_sub_assembly_items(bom_no, bom_data):
 	data = get_children('BOM', parent = bom_no)
 	for d in data:
 		if d.expandable:
@@ -729,6 +730,6 @@
 				})
 
 			bom_item = bom_data.get(key)
-			bom_item["stock_qty"] += ((d.stock_qty * qty) / d.parent_bom_qty)
+			bom_item["stock_qty"] += d.stock_qty
 
-			get_sub_assembly_items(bom_item.get("bom_no"), bom_data, bom_item["stock_qty"])
+			get_sub_assembly_items(bom_item.get("bom_no"), bom_data)
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 4479641..f70c9cc 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -11,11 +11,9 @@
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
 from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
 from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
 
 class TestProductionPlan(unittest.TestCase):
 	def setUp(self):
-		set_perpetual_inventory(0)
 		for item in ['Test Production Item 1', 'Subassembly Item 1',
 			'Raw Material Item 1', 'Raw Material Item 2']:
 			create_item(item, valuation_rate=100)