Create a project from a Sales Order (#9111)

* init commit

* don't get tasks if project name is none

* cleanup

* Update sales_order.js
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 0345f05..66df901 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -47,7 +47,10 @@
 			self.append("tasks", task_map)
 
 	def get_tasks(self):
-		return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc")
+		if self.name is None:
+			return {}
+		else:
+			return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc")
 
 	def validate(self):
 		self.validate_dates()
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 18260fb..2ba2aba 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -10,7 +10,8 @@
 			'Delivery Note': 'Delivery',
 			'Sales Invoice': 'Invoice',
 			'Material Request': 'Material Request',
-			'Purchase Order': 'Purchase Order'
+			'Purchase Order': 'Purchase Order',
+			'Project': 'Project'
 		}
 		frm.add_fetch('customer', 'tax_id', 'tax_id');
 	},
@@ -120,6 +121,12 @@
 						function() { me.make_maintenance_schedule() }, __("Make"));
 				}
 
+				// project
+				if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
+						this.frm.add_custom_button(__('Project'),
+							function() { me.make_project() }, __("Make"));
+				}
+
 			} else {
 				if (this.frm.has_perm("submit")) {
 					// un-close
@@ -264,6 +271,13 @@
 		})
 	},
 
+	make_project: function() {
+		frappe.model.open_mapped_doc({
+			method: "erpnext.selling.doctype.sales_order.sales_order.make_project",
+			frm: this.frm
+		})
+	},
+
 	make_maintenance_visit: function() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_visit",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index bbeccc8..8295075 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -409,6 +409,34 @@
 	return doc
 
 @frappe.whitelist()
+def make_project(source_name, target_doc=None):
+	def postprocess(source, doc):
+		doc.project_type = "External"
+		doc.project_name = source.name
+
+	doc = get_mapped_doc("Sales Order", source_name, {
+		"Sales Order": {
+			"doctype": "Project",
+			"validation": {
+				"docstatus": ["=", 1]
+			},
+			"field_map":{
+				"name" : "sales_order",
+				"delivery_date" : "expected_end_date",
+				"base_grand_total" : "estimated_costing",
+			}
+		},
+		"Sales Order Item": {
+			"doctype": "Project Task",
+			"field_map": {
+				"description": "title",
+			},
+		}
+	}, target_doc, postprocess)
+
+	return doc
+
+@frappe.whitelist()
 def make_delivery_note(source_name, target_doc=None):
 	def set_missing_values(source, target):
 		if source.po_no: