feat(Sales Order): Allow to skip delivery note creation (#19222)

* feat(sales-order): add option to skip delivery note creation for order type maintenance

* fix(sales-order): hide delivery date on setting skip delivery note creation

* fix: allow skipping delivery note for all order types

* fix: bypass delivery note creation on setting skip delivery note creation

* fix: show skip delivery note in list view

* fix: check for duplicate project name after submitting prompt

* fix:  toggle delivery note reqd for skip delivery note

* fix: update status based on skip delivery note
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 64d49b4..9d1389c 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -37,9 +37,9 @@
 	"Sales Order": [
 		["Draft", None],
 		["To Deliver and Bill", "eval:self.per_delivered < 100 and self.per_billed < 100 and self.docstatus == 1"],
-		["To Bill", "eval:self.per_delivered == 100 and self.per_billed < 100 and self.docstatus == 1"],
-		["To Deliver", "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1"],
-		["Completed", "eval:self.per_delivered == 100 and self.per_billed == 100 and self.docstatus == 1"],
+		["To Bill", "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1"],
+		["To Deliver", "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1 and not self.skip_delivery_note"],
+		["Completed", "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed == 100 and self.docstatus == 1"],
 		["Cancelled", "eval:self.docstatus==2"],
 		["Closed", "eval:self.status=='Closed'"],
 		["On Hold", "eval:self.status=='On Hold'"],
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 0112b39..b57548e 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -64,7 +64,8 @@
 				from `tabSales Order` so
 				inner join `tabSales Order Item` so_item on so_item.parent = so.name
 				left join `tabProduct Bundle Item` pk_item on so_item.item_code = pk_item.parent
-				where so.name=%s and so.docstatus = 1 and (
+				where so.name=%s and so.docstatus = 1
+					and so.skip_delivery_note  = 0 and (
 					so_item.item_code=%s or
 					pk_item.item_code=%s )
 			""", (self.sales_order, self.production_item, self.production_item), as_dict=1)
@@ -78,6 +79,7 @@
 					where so.name=%s
 						and so.name=so_item.parent
 						and so.name=packed_item.parent
+						and so.skip_delivery_note = 0
 						and so_item.item_code = packed_item.parent_item
 						and so.docstatus = 1 and packed_item.item_code=%s
 				""", (self.sales_order, self.production_item), as_dict=1)
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index d6fd8a1..a2b8544 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -136,7 +136,8 @@
 			if(doc.status !== 'Closed') {
 				if(doc.status !== 'On Hold') {
 
-					allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty))
+					allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty)) 
+						&& !this.frm.doc.skip_delivery_note
 
 					if (this.frm.has_perm("submit")) {
 						if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) {
@@ -341,7 +342,7 @@
 	},
 
 	order_type: function() {
-		this.frm.fields_dict.items.grid.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
+		this.toggle_delivery_date();
 	},
 
 	tc_name: function() {
@@ -355,6 +356,15 @@
 		})
 	},
 
+	skip_delivery_note: function() {
+		this.toggle_delivery_date();
+	},
+
+	toggle_delivery_date: function() {
+		this.frm.fields_dict.items.grid.toggle_reqd("delivery_date", 
+			(this.frm.doc.order_type == "Sales" && !this.frm.doc.skip_delivery_note));
+	},
+
 	make_raw_material_request: function() {
 		var me = this;
 		this.frm.call({
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index cd6965a..3885b13 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -14,6 +14,7 @@
   "customer",
   "customer_name",
   "order_type",
+  "skip_delivery_note",
   "column_break1",
   "amended_from",
   "company",
@@ -252,6 +253,7 @@
   },
   {
    "allow_on_submit": 1,
+   "depends_on": "eval:!doc.skip_delivery_note",
    "fieldname": "delivery_date",
    "fieldtype": "Date",
    "in_list_view": 1,
@@ -1023,7 +1025,7 @@
    "print_hide": 1
   },
   {
-   "depends_on": "eval:!doc.__islocal",
+   "depends_on": "eval:!doc.__islocal && !doc.skip_delivery_note_creation",
    "description": "% of materials delivered against this Sales Order",
    "fieldname": "per_delivered",
    "fieldtype": "Percent",
@@ -1171,12 +1173,19 @@
    "fieldtype": "Data",
    "label": "Phone",
    "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "skip_delivery_note",
+   "fieldtype": "Check",
+   "label": "Skip Delivery Note",
+   "print_hide": 1
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 105,
  "is_submittable": 1,
- "modified": "2019-09-27 14:23:52.233323",
+ "modified": "2019-10-10 08:46:07.540565",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index e60be5a..80784ea 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -57,13 +57,13 @@
 
 	def validate_po(self):
 		# validate p.o date v/s delivery date
-		if self.po_date:
+		if self.po_date and not self.skip_delivery_note:
 			for d in self.get("items"):
 				if d.delivery_date and getdate(self.po_date) > getdate(d.delivery_date):
 					frappe.throw(_("Row #{0}: Expected Delivery Date cannot be before Purchase Order Date")
 						.format(d.idx))
 
-		if self.po_no and self.customer:
+		if self.po_no and self.customer and not self.skip_delivery_note:
 			so = frappe.db.sql("select name from `tabSales Order` \
 				where ifnull(po_no, '') = %s and name != %s and docstatus < 2\
 				and customer = %s", (self.po_no, self.name, self.customer))
@@ -100,7 +100,7 @@
 		super(SalesOrder, self).validate_order_type()
 
 	def validate_delivery_date(self):
-		if self.order_type == 'Sales':
+		if self.order_type == 'Sales' and not self.skip_delivery_note:
 			delivery_date_list = [d.delivery_date for d in self.get("items") if d.delivery_date]
 			max_delivery_date = max(delivery_date_list) if delivery_date_list else None
 			if not self.delivery_date:
@@ -760,6 +760,7 @@
 		from
 			`tabSales Order`, `tabSales Order Item`
 		where `tabSales Order`.name = `tabSales Order Item`.parent
+			and `tabSales Order`.skip_delivery_note = 0
 			and (ifnull(`tabSales Order Item`.delivery_date, '0000-00-00')!= '0000-00-00') \
 			and (`tabSales Order Item`.delivery_date between %(start)s and %(end)s)
 			and `tabSales Order`.docstatus < 2
diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js
index 301ecde..26d96d5 100644
--- a/erpnext/selling/doctype/sales_order/sales_order_list.js
+++ b/erpnext/selling/doctype/sales_order/sales_order_list.js
@@ -1,58 +1,41 @@
 frappe.listview_settings['Sales Order'] = {
 	add_fields: ["base_grand_total", "customer_name", "currency", "delivery_date",
-		"per_delivered", "per_billed", "status", "order_type", "name"],
+		"per_delivered", "per_billed", "status", "order_type", "name", "skip_delivery_note"],
 	get_indicator: function (doc) {
 		if (doc.status === "Closed") {
+			// Closed
 			return [__("Closed"), "green", "status,=,Closed"];
-
 		} else if (doc.status === "On Hold") {
 			// on hold
 			return [__("On Hold"), "orange", "status,=,On Hold"];
-		} else if (doc.order_type !== "Maintenance"
-			&& flt(doc.per_delivered, 6) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
+		} else if (doc.status === "Completed") {
+			return [__("Completed"), "green", "status,=,Completed"];
+		} else if (!doc.skip_delivery_note && flt(doc.per_delivered, 6) < 100) {
+			if (frappe.datetime.get_diff(doc.delivery_date) < 0) {
 			// not delivered & overdue
-			return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"];
-
-		} else if (doc.order_type !== "Maintenance"
-			&& flt(doc.per_delivered, 6) < 100 && doc.status !== "Closed") {
-			// not delivered
-
-			if (flt(doc.grand_total) === 0) {
+				return [__("Overdue"), "red",
+					"per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"];
+			} else if (flt(doc.grand_total) === 0) {
 				// not delivered (zero-amount order)
-
 				return [__("To Deliver"), "orange",
 					"per_delivered,<,100|grand_total,=,0|status,!=,Closed"];
 			} else if (flt(doc.per_billed, 6) < 100) {
 				// not delivered & not billed
-
 				return [__("To Deliver and Bill"), "orange",
 					"per_delivered,<,100|per_billed,<,100|status,!=,Closed"];
 			} else {
 				// not billed
-
 				return [__("To Deliver"), "orange",
 					"per_delivered,<,100|per_billed,=,100|status,!=,Closed"];
 			}
-
-		} else if ((flt(doc.per_delivered, 6) === 100)
-			&& flt(doc.grand_total) !== 0 && flt(doc.per_billed, 6) < 100 && doc.status !== "Closed") {
+		} else if ((flt(doc.per_delivered, 6) === 100) && flt(doc.grand_total) !== 0
+			&& flt(doc.per_billed, 6) < 100) {
 			// to bill
-
-			return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"];
-
-		} else if ((flt(doc.per_delivered, 6) === 100)
-			&& (flt(doc.grand_total) === 0 || flt(doc.per_billed, 6) == 100) && doc.status !== "Closed") {
-			return [__("Completed"), "green", "per_delivered,=,100|per_billed,=,100|status,!=,Closed"];
-
-		}else if (doc.order_type === "Maintenance" && flt(doc.per_delivered, 6) < 100 && doc.status !== "Closed"){
-
-			if(flt(doc.per_billed, 6) < 100 ){
-				return [__("To Deliver and Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"];
-			}else if(flt(doc.per_billed, 6) === 100){
-				return [__("To Deliver"), "orange", "per_delivered,=,100|per_billed,=,100|status,!=,Closed"];
-			}
+			return [__("To Bill"), "orange",
+				"per_delivered,=,100|per_billed,<,100|status,!=,Closed"];
+		} else if (doc.skip_delivery_note && flt(doc.per_billed, 6) < 100){
+			return [__("To Bill"), "orange", "per_billed,<,100|status,!=,Closed"];
 		}
-
 	},
 	onload: function(listview) {
 		var method = "erpnext.selling.doctype.sales_order.sales_order.close_or_unclose_sales_orders";
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 b94dce1..3fd1e64 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -149,6 +149,7 @@
   },
   {
    "columns": 2,
+   "depends_on": "eval: !parent.skip_delivery_note",
    "fieldname": "delivery_date",
    "fieldtype": "Date",
    "in_list_view": 1,
@@ -693,6 +694,7 @@
    "description": "For Production",
    "fieldname": "produced_qty",
    "fieldtype": "Float",
+   "hidden": 1,
    "label": "Produced Quantity",
    "oldfieldname": "produced_qty",
    "oldfieldtype": "Currency",
@@ -743,7 +745,7 @@
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2019-09-13 12:18:54.903107",
+ "modified": "2019-10-10 08:46:26.244823",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",