feat(Sales/Purchase Order): optional to reference a Blanket Order (#19612)

diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index a0a1e8e..08f5d8b 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -17,6 +17,8 @@
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 from erpnext.controllers.accounts_controller import update_child_qty_rate
 from erpnext.controllers.status_updater import OverAllowanceError
+from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
+
 
 class TestPurchaseOrder(unittest.TestCase):
 	def test_make_purchase_receipt(self):
@@ -620,6 +622,27 @@
 		po.save()
 		self.assertEqual(po.schedule_date, add_days(nowdate(), 2))
 
+	
+	def test_po_optional_blanket_order(self):
+		"""
+			Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1.
+			Second Purchase Order should not add on to Blanket Orders Ordered Quantity.
+		"""
+
+		bo = make_blanket_order(blanket_order_type = "Purchasing", quantity = 10, rate = 10)
+
+		po = create_purchase_order(item_code= "_Test Item", qty = 5, against_blanket_order = 1)
+		po_doc = frappe.get_doc('Purchase Order', po.get('name'))
+		# To test if the PO has a Blanket Order
+		self.assertTrue(po_doc.items[0].blanket_order)
+
+		po = create_purchase_order(item_code= "_Test Item", qty = 5, against_blanket_order = 0)
+		po_doc = frappe.get_doc('Purchase Order', po.get('name'))
+		# To test if the PO does NOT have a Blanket Order
+		self.assertEqual(po_doc.items[0].blanket_order, None)
+
+
+
 
 def make_pr_against_po(po, received_qty=0):
 	pr = make_purchase_receipt(po)
@@ -693,7 +716,8 @@
 		"qty": args.qty or 10,
 		"rate": args.rate or 500,
 		"schedule_date": add_days(nowdate(), 1),
-		"include_exploded_items": args.get('include_exploded_items', 1)
+		"include_exploded_items": args.get('include_exploded_items', 1),
+		"against_blanket_order": args.against_blanket_order
 	})
 	if not args.do_not_save:
 		po.insert()
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index c409c1f..15bc97c 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -43,7 +43,6 @@
   "base_amount",
   "pricing_rules",
   "is_free_item",
-  "is_fixed_asset",
   "section_break_29",
   "net_rate",
   "net_amount",
@@ -67,6 +66,7 @@
   "supplier_quotation",
   "supplier_quotation_item",
   "col_break5",
+  "against_blanket_order",
   "blanket_order",
   "blanket_order_rate",
   "item_group",
@@ -511,6 +511,7 @@
    "read_only": 1
   },
   {
+   "depends_on": "eval:doc.against_blanket_order",
    "fieldname": "blanket_order",
    "fieldtype": "Link",
    "label": "Blanket Order",
@@ -518,6 +519,7 @@
    "options": "Blanket Order"
   },
   {
+   "depends_on": "eval:doc.against_blanket_order",
    "fieldname": "blanket_order_rate",
    "fieldtype": "Currency",
    "label": "Blanket Order Rate",
@@ -703,16 +705,14 @@
   },
   {
    "default": "0",
-   "fetch_from": "item_code.is_fixed_asset",
-   "fieldname": "is_fixed_asset",
+   "fieldname": "against_blanket_order",
    "fieldtype": "Check",
-   "label": "Is Fixed Asset",
-   "read_only": 1
+   "label": "Against Blanket Order"
   }
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2019-11-07 17:19:12.090355",
+ "modified": "2019-11-19 14:10:52.865006",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index faed707..38118bd 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -44,6 +44,8 @@
 			target.item_name = item.get("item_name")
 			target.description = item.get("description")
 			target.uom = item.get("stock_uom")
+			target.against_blanket_order = 1
+			target.blanket_order = source_name
 
 	target_doc = get_mapped_doc("Blanket Order", source_name, {
 		"Blanket Order": {
@@ -71,6 +73,8 @@
 			target.description = item.get("description")
 			target.uom = item.get("stock_uom")
 			target.warehouse = item.get("default_warehouse")
+			target.against_blanket_order = 1
+			target.blanket_order = source_name
 
 	target_doc = get_mapped_doc("Blanket Order", source_name, {
 		"Blanket Order": {
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 5da9493..46a58fb 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1716,6 +1716,14 @@
 		}
 	},
 
+	against_blanket_order: function(doc, cdt, cdn) {
+		var item = locals[cdt][cdn];
+		if(!item.against_blanket_order) {
+			frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order", null);
+			frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order_rate", 0.00);
+		}
+	},
+
 	blanket_order: function(doc, cdt, cdn) {
 		var me = this;
 		var item = locals[cdt][cdn];
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index bd07841..feb6b76 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -12,6 +12,7 @@
 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
 
 class TestSalesOrder(unittest.TestCase):
 	def tearDown(self):
@@ -819,6 +820,25 @@
 		mr_doc = frappe.get_doc('Material Request',mr.get('name'))
 		self.assertEqual(mr_doc.items[0].sales_order, so.name)
 
+	def test_so_optional_blanket_order(self):
+		"""
+			Expected result: Blanket order Ordered Quantity should only be affected on Sales Order with against_blanket_order = 1.
+			Second Sales Order should not add on to Blanket Orders Ordered Quantity.
+		"""
+
+		bo = make_blanket_order(blanket_order_type = "Selling", quantity = 10, rate = 10)
+
+		so = make_sales_order(item_code= "_Test Item", qty = 5, against_blanket_order = 1)
+		so_doc = frappe.get_doc('Sales Order', so.get('name'))
+		# To test if the SO has a Blanket Order
+		self.assertTrue(so_doc.items[0].blanket_order)
+
+		so = make_sales_order(item_code= "_Test Item", qty = 5, against_blanket_order = 0)
+		so_doc = frappe.get_doc('Sales Order', so.get('name'))
+		# To test if the SO does NOT have a Blanket Order
+		self.assertEqual(so_doc.items[0].blanket_order, None)
+
+
 def make_sales_order(**args):
 	so = frappe.new_doc("Sales Order")
 	args = frappe._dict(args)
@@ -845,7 +865,8 @@
 			"warehouse": args.warehouse,
 			"qty": args.qty or 10,
 			"uom": args.uom or None,
-			"rate": args.rate or 100
+			"rate": args.rate or 100,
+			"against_blanket_order": args.against_blanket_order
 		})
 
 	so.delivery_date = add_days(so.transaction_date, 10)
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 3fd1e64..86b09c2 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -68,6 +68,7 @@
   "target_warehouse",
   "prevdoc_docname",
   "col_break4",
+  "against_blanket_order",
   "blanket_order",
   "blanket_order_rate",
   "planning_section",
@@ -574,6 +575,7 @@
    "report_hide": 1
   },
   {
+   "depends_on": "eval:doc.against_blanket_order",
    "fieldname": "blanket_order",
    "fieldtype": "Link",
    "label": "Blanket Order",
@@ -581,6 +583,7 @@
    "options": "Blanket Order"
   },
   {
+   "depends_on": "eval:doc.against_blanket_order",
    "fieldname": "blanket_order_rate",
    "fieldtype": "Currency",
    "label": "Blanket Order Rate",
@@ -741,11 +744,17 @@
    "fieldname": "image_section",
    "fieldtype": "Section Break",
    "label": "Image"
+  },
+  {
+   "default": "0",
+   "fieldname": "against_blanket_order",
+   "fieldtype": "Check",
+   "label": "Against Blanket Order"
   }
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2019-10-10 08:46:26.244823",
+ "modified": "2019-11-19 14:19:29.491945",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 55f4be1..76644ed 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -213,7 +213,8 @@
 			project: "",
 			qty: "",
 			stock_qty: "",
-			conversion_factor: ""
+			conversion_factor: "",
+			against_blanket_order: 0/1
 		}
 	:param item: `item_code` of Item object
 	:return: frappe._dict
@@ -302,7 +303,8 @@
 		"weight_per_unit":item.weight_per_unit,
 		"weight_uom":item.weight_uom,
 		"last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0,
-		"transaction_date": args.get("transaction_date")
+		"transaction_date": args.get("transaction_date"),
+		"against_blanket_order": args.get("against_blanket_order")
 	})
 
 	if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
@@ -996,9 +998,10 @@
 
 
 def update_party_blanket_order(args, out):
-	blanket_order_details = get_blanket_order_details(args)
-	if blanket_order_details:
-		out.update(blanket_order_details)
+	if out["against_blanket_order"]:
+		blanket_order_details = get_blanket_order_details(args)
+		if blanket_order_details:
+			out.update(blanket_order_details)
 
 @frappe.whitelist()
 def get_blanket_order_details(args):