Merge pull request #27040 from marination/cart-pl-exchange-rate

fix: Shopping cart Exchange rate validation
diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
index 795fb1c..a70d5c9 100644
--- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
+++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
@@ -106,7 +106,6 @@
    "depends_on": "eval:doc.rate_or_discount==\"Rate\"",
    "fieldname": "rate",
    "fieldtype": "Currency",
-   "in_list_view": 1,
    "label": "Rate"
   },
   {
@@ -170,7 +169,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-03-07 11:56:23.424137",
+ "modified": "2021-08-19 15:49:29.598727",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Promotional Scheme Price Discount",
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index 3f50b41..e72c8eb 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -235,7 +235,7 @@
 						reqd: 1,
 					},
 					{
-						fieldname: "varint_item_code",
+						fieldname: "variant_item_code",
 						options: "Item",
 						label: __("Variant Item"),
 						fieldtype: "Link",
@@ -287,7 +287,7 @@
 			let variant_items = data.items || [];
 
 			variant_items.forEach(d => {
-				if (!d.varint_item_code) {
+				if (!d.variant_item_code) {
 					frappe.throw(__("Select variant item code for the template item {0}", [d.item_code]));
 				}
 			})
@@ -299,7 +299,7 @@
 		has_template_rm.forEach(d => {
 			dialog.fields_dict.items.df.data.push({
 				"item_code": d.item_code,
-				"varint_item_code": "",
+				"variant_item_code": "",
 				"qty": d.qty,
 				"source_warehouse": d.source_warehouse,
 				"operation": d.operation
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 7e7dc04..b4c6635 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -346,6 +346,7 @@
 				"production_plan"       : self.name,
 				"production_plan_item"  : d.name,
 				"product_bundle_item"	: d.product_bundle_item,
+				"planned_start_date"    : d.planned_start_date,
 				"make_work_order_for_sub_assembly_items": d.get("make_work_order_for_sub_assembly_items", 0)
 			}
 
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index af8de8e..a5b9ff8 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -5,7 +5,7 @@
 
 import frappe
 import unittest
-from frappe.utils import nowdate, now_datetime, flt
+from frappe.utils import nowdate, now_datetime, flt, add_to_date
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
@@ -61,6 +61,21 @@
 		pln = frappe.get_doc('Production Plan', pln.name)
 		pln.cancel()
 
+	def test_production_plan_start_date(self):
+		planned_date = add_to_date(date=None, days=3)
+		plan = create_production_plan(item_code='Test Production Item 1', planned_start_date=planned_date)
+		plan.make_work_order()
+
+		work_orders = frappe.get_all('Work Order', fields = ['name', 'planned_start_date'],
+			filters = {'production_plan': plan.name})
+
+		self.assertEqual(work_orders[0].planned_start_date, planned_date)
+
+		for wo in work_orders:
+			frappe.delete_doc('Work Order', wo.name)
+
+		frappe.get_doc('Production Plan', plan.name).cancel()
+
 	def test_production_plan_for_existing_ordered_qty(self):
 		sr1 = create_stock_reconciliation(item_code="Raw Material Item 1",
 			target="_Test Warehouse - _TC", qty=1, rate=110)
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 282b5d0..69a4b95 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -838,7 +838,7 @@
 
 	for item in variant_items:
 		args = frappe._dict({
-			"item_code": item.get("varint_item_code"),
+			"item_code": item.get("variant_item_code"),
 			"required_qty": item.get("qty"),
 			"qty": item.get("qty"), # for bom
 			"source_warehouse": item.get("source_warehouse"),
@@ -859,7 +859,7 @@
 		}, bom_doc)
 
 		if not args.source_warehouse:
-			args["source_warehouse"] = get_item_defaults(item.get("varint_item_code"),
+			args["source_warehouse"] = get_item_defaults(item.get("variant_item_code"),
 				wo_doc.company).default_warehouse
 
 		args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate"))
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 7b31d2f..90a33d3 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -317,9 +317,6 @@
 				d.s_warehouse = self.from_warehouse
 				d.t_warehouse = self.to_warehouse
 
-			if not (d.s_warehouse or d.t_warehouse):
-				frappe.throw(_("Atleast one warehouse is mandatory"))
-
 			if self.purpose in source_mandatory and not d.s_warehouse:
 				if self.from_warehouse:
 					d.s_warehouse = self.from_warehouse
@@ -332,6 +329,7 @@
 				else:
 					frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
 
+
 			if self.purpose == "Manufacture":
 				if validate_for_manufacture:
 					if d.is_finished_item or d.is_scrap_item:
@@ -346,6 +344,9 @@
 			if cstr(d.s_warehouse) == cstr(d.t_warehouse) and not self.purpose == "Material Transfer for Manufacture":
 				frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
 
+			if not (d.s_warehouse or d.t_warehouse):
+				frappe.throw(_("Atleast one warehouse is mandatory"))
+
 	def validate_work_order(self):
 		if self.purpose in ("Manufacture", "Material Transfer for Manufacture", "Material Consumption for Manufacture"):
 			# check if work order is entered