Merge branch 'master' into develop
diff --git a/erpnext/tests/ui/accounts/test_account.js b/erpnext/accounts/doctype/account/test_account.js
similarity index 77%
rename from erpnext/tests/ui/accounts/test_account.js
rename to erpnext/accounts/doctype/account/test_account.js
index 6d7709b..7b23ef0 100644
--- a/erpnext/tests/ui/accounts/test_account.js
+++ b/erpnext/accounts/doctype/account/test_account.js
@@ -5,16 +5,16 @@
 	let done = assert.async();
 	frappe.run_serially([
 		() => frappe.set_route('Tree', 'Account'),
-		() => frappe.tests.click_button('Expand All'),
-		() => frappe.tests.click_link('Debtors'),
-		() => frappe.tests.click_button('Edit'),
+		() => frappe.click_button('Expand All'),
+		() => frappe.click_link('Debtors'),
+		() => frappe.click_button('Edit'),
 		() => frappe.timeout(1),
 		() => {
 			assert.ok(cur_frm.doc.root_type=='Asset');
 			assert.ok(cur_frm.doc.report_type=='Balance Sheet');
 			assert.ok(cur_frm.doc.account_type=='Receivable');
 		},
-		() => frappe.tests.click_button('Ledger'),
+		() => frappe.click_button('Ledger'),
 		() => frappe.timeout(1),
 		() => {
 			// check if general ledger report shown
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index ba4cc83..577c77f 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -129,10 +129,11 @@
 
 				// account filter
 				frappe.model.validate_missing(jvd, "account");
-
 				var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
 				out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
-			} else {
+			}
+
+			if(in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) {
 				// party_type and party mandatory
 				frappe.model.validate_missing(jvd, "party_type");
 				frappe.model.validate_missing(jvd, "party");
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index bf509de..005abe6 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -284,12 +284,14 @@
 	if not credit_days_based_on:
 		if party_type == "Customer":
 			credit_days_based_on, credit_days = \
-				frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"]) \
-				or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
+				frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"])
 		else:
 			credit_days_based_on, credit_days = \
-				frappe.db.get_value("Supplier Type", supplier_type, ["credit_days_based_on", "credit_days"])\
-				or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"] )
+				frappe.db.get_value("Supplier Type", supplier_type, ["credit_days_based_on", "credit_days"])
+
+	if not credit_days_based_on:
+		credit_days_based_on, credit_days = \
+			frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
 
 	return credit_days_based_on, credit_days
 
diff --git a/erpnext/controllers/tests/test_recurring_document.py b/erpnext/controllers/tests/test_recurring_document.py
index d47c5c7..e218fa6 100644
--- a/erpnext/controllers/tests/test_recurring_document.py
+++ b/erpnext/controllers/tests/test_recurring_document.py
@@ -27,7 +27,8 @@
 	base_doc.set(date_field, today)
 
 	if base_doc.doctype == "Sales Order":
-		base_doc.set("delivery_date", add_days(today, 15))
+		for d in base_doc.get("items"):
+			d.set("delivery_date", add_days(today, 15))
 
 	# monthly
 	doc1 = frappe.copy_doc(base_doc)
diff --git a/erpnext/crm/doctype/lead/test_lead.js b/erpnext/crm/doctype/lead/test_lead.js
new file mode 100644
index 0000000..66d3337
--- /dev/null
+++ b/erpnext/crm/doctype/lead/test_lead.js
@@ -0,0 +1,43 @@
+QUnit.module("sales");
+
+QUnit.test("test: lead", function (assert) {
+	assert.expect(4);
+	let done = assert.async();
+	let lead_name = frappe.utils.get_random(10);
+	frappe.run_serially([
+		// test lead creation
+		() => frappe.set_route("List", "Lead"),
+		() => frappe.new_doc("Lead"),
+		() => frappe.timeout(1),
+		() => cur_frm.set_value("lead_name", lead_name),
+		() => cur_frm.save(),
+		() => frappe.timeout(1),
+		() => {
+			assert.ok(cur_frm.doc.lead_name.includes(lead_name),
+				'name correctly set');
+			frappe.lead_name = cur_frm.doc.name;
+		},
+		// create address and contact
+		() => frappe.click_link('Address & Contact'),
+		() => frappe.click_button('New Address'),
+		() => frappe.timeout(1),
+		() => frappe.set_control('address_line1', 'Gateway'),
+		() => frappe.set_control('city', 'Mumbai'),
+		() => cur_frm.save(),
+		() => frappe.timeout(3),
+		() => assert.equal(frappe.get_route()[1], 'Lead',
+			'back to lead form'),
+		() => frappe.click_link('Address & Contact'),
+		() => assert.ok($('.address-box').text().includes('Mumbai'),
+			'city is seen in address box'),
+
+		// make opportunity
+		() => frappe.click_button('Make'),
+		() => frappe.click_link('Opportunity'),
+		() => frappe.timeout(2),
+		() => assert.equal(cur_frm.doc.lead, frappe.lead_name,
+			'lead name correctly mapped'),
+
+		() => done()
+	]);
+});
diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.js b/erpnext/crm/doctype/opportunity/test_opportunity.js
new file mode 100644
index 0000000..f2b04f8
--- /dev/null
+++ b/erpnext/crm/doctype/opportunity/test_opportunity.js
@@ -0,0 +1,56 @@
+QUnit.test("test: opportunity", function (assert) {
+	assert.expect(8);
+	let done = assert.async();
+	frappe.run_serially([
+		() => frappe.set_route('List', 'Opportunity'),
+		() => frappe.timeout(1),
+		() => frappe.click_button('New'),
+		() => frappe.timeout(1),
+		() => cur_frm.set_value('enquiry_from', 'Customer'),
+		() => cur_frm.set_value('customer', 'Test Customer 1'),
+
+		// check items
+		() => cur_frm.set_value('with_items', 1),
+		() => frappe.tests.set_grid_values(cur_frm, 'items', [
+			[
+				{item_code:'Test Product 1'},
+				{qty: 4}
+			]
+		]),
+		() => cur_frm.save(),
+		() => frappe.timeout(1),
+		() => {
+			assert.notOk(cur_frm.is_new(), 'saved');
+			frappe.opportunity_name = cur_frm.doc.name;
+		},
+
+		// close and re-open
+		() => frappe.click_button('Close'),
+		() => frappe.timeout(1),
+		() => assert.equal(cur_frm.doc.status, 'Closed',
+			'closed'),
+
+		() => frappe.click_button('Reopen'),
+		() => assert.equal(cur_frm.doc.status, 'Open',
+			'reopened'),
+		() => frappe.timeout(1),
+
+		// make quotation
+		() => frappe.click_button('Make'),
+		() => frappe.click_link('Quotation', 1),
+		() => frappe.timeout(2),
+		() => {
+			assert.equal(frappe.get_route()[1], 'Quotation',
+				'made quotation');
+			assert.equal(cur_frm.doc.customer, 'Test Customer 1',
+				'customer set in quotation');
+			assert.equal(cur_frm.doc.items[0].item_code, 'Test Product 1',
+				'item set in quotation');
+			assert.equal(cur_frm.doc.items[0].qty, 4,
+				'qty set in quotation');
+			assert.equal(cur_frm.doc.items[0].prevdoc_docname, frappe.opportunity_name,
+				'opportunity set in quotation');
+		},
+		() => done()
+	]);
+});
diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py
index ddd36ef..666e201 100644
--- a/erpnext/demo/user/sales.py
+++ b/erpnext/demo/user/sales.py
@@ -118,7 +118,8 @@
 		from erpnext.selling.doctype.quotation.quotation import make_sales_order
 		so = frappe.get_doc(make_sales_order(q))
 		so.transaction_date = frappe.flags.current_date
-		so.delivery_date = frappe.utils.add_days(frappe.flags.current_date, 10)
+		for d in so.get("items"):
+			d.delivery_date = frappe.utils.add_days(frappe.flags.current_date, 10)
 		so.insert()
 		frappe.db.commit()
 		so.submit()
diff --git a/erpnext/docs/assets/img/selling/make-so.gif b/erpnext/docs/assets/img/selling/make-so.gif
index 5edd19b..0e568a8 100644
--- a/erpnext/docs/assets/img/selling/make-so.gif
+++ b/erpnext/docs/assets/img/selling/make-so.gif
Binary files differ
diff --git a/erpnext/docs/user/manual/en/selling/sales-order.md b/erpnext/docs/user/manual/en/selling/sales-order.md
index 99b9a85..b103038 100644
--- a/erpnext/docs/user/manual/en/selling/sales-order.md
+++ b/erpnext/docs/user/manual/en/selling/sales-order.md
@@ -31,7 +31,7 @@
 There are a few amongst other things that a Sales Order will ask you to
 update.
 
-  * Expected date of delivery.
+  * Enter delivery date agaist each item. If there are multiple items and if you enter delivery date in the first row, the date will be copied to other rows as well where it is blank.
   * Customer Purchase Order number: If your customer has sent you a Purchase Order, you can update its number for future reference (in billing).
 
 ### Packing List
@@ -89,9 +89,9 @@
 Once you “Submit” your Sales Order, you can now trigger different aspects of
 your organization:
 
-  * To begin purchase click on “Make Purchase Request”
-  * To make a shipment entry click on “Make Delivery Note”
-  * To bill, make “Make Sales Invoice”
+  * To begin purchase click on Make -> Purchase Request
+  * To make a shipment entry click on Make -> Delivery Note. You can also make Delivery Note for selected items based on delivery date.
+  * To bill, make Make -> Sales Invoice
   * To stop further process on this Sales Order, click on “Stop”
 
 ### Submission
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.js b/erpnext/hr/doctype/process_payroll/process_payroll.js
index 0ec7f70..a9ad429 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.js
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.js
@@ -1,6 +1,8 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
+var in_progress = false;
+
 frappe.ui.form.on("Process Payroll", {
 	onload: function (frm) {
 		frm.doc.posting_date = frappe.datetime.nowdate();
@@ -47,11 +49,12 @@
 	},
 
 	start_date: function (frm) {
-		frm.trigger("set_start_end_dates");
-	},
-
-	end_date: function (frm) {
-		frm.trigger("set_start_end_dates");
+		if(!in_progress && frm.doc.start_date){
+			frm.trigger("set_end_date");
+		}else{
+			// reset flag
+			in_progress = false
+		}
 	},
 
 	salary_slip_based_on_timesheet: function (frm) {
@@ -68,10 +71,11 @@
 				method: 'erpnext.hr.doctype.process_payroll.process_payroll.get_start_end_dates',
 				args: {
 					payroll_frequency: frm.doc.payroll_frequency,
-					start_date: frm.doc.start_date || frm.doc.posting_date
+					start_date: frm.doc.posting_date
 				},
 				callback: function (r) {
 					if (r.message) {
+						in_progress = true;
 						frm.set_value('start_date', r.message.start_date);
 						frm.set_value('end_date', r.message.end_date);
 					}
@@ -79,6 +83,21 @@
 			})
 		}
 	},
+
+	set_end_date: function(frm){
+		frappe.call({
+			method: 'erpnext.hr.doctype.process_payroll.process_payroll.get_end_date',
+			args: {
+				frequency: frm.doc.payroll_frequency,
+				start_date: frm.doc.start_date
+			},
+			callback: function (r) {
+				if (r.message) {
+					frm.set_value('end_date', r.message.end_date);
+				}
+			}
+		})
+	}
 })
 
 cur_frm.cscript.display_activity_log = function (msg) {
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.py b/erpnext/hr/doctype/process_payroll/process_payroll.py
index 7ca94b8..b58c787 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.py
@@ -3,7 +3,8 @@
 
 from __future__ import unicode_literals
 import frappe
-from frappe.utils import cint, flt, nowdate, add_days, getdate, fmt_money
+from dateutil.relativedelta import relativedelta
+from frappe.utils import cint, nowdate, add_days, getdate, fmt_money, add_to_date, DATE_FORMAT
 from frappe import _
 from erpnext.accounts.utils import get_fiscal_year
 
@@ -47,7 +48,6 @@
 			%s """% cond, {"sal_struct": sal_struct})
 			return emp_list
 
-
 	def get_filter_condition(self):
 		self.check_mandatory()
 
@@ -58,7 +58,6 @@
 
 		return cond
 
-
 	def get_joining_releiving_condition(self):
 		cond = """
 			and ifnull(t1.date_of_joining, '0000-00-00') <= '%(end_date)s'
@@ -66,7 +65,6 @@
 		""" % {"start_date": self.start_date, "end_date": self.end_date}
 		return cond
 
-
 	def check_mandatory(self):
 		for fieldname in ['company', 'start_date', 'end_date']:
 			if not self.get(fieldname):
@@ -110,7 +108,6 @@
 					ss_list.append(ss_dict)
 		return self.create_log(ss_list)
 
-
 	def create_log(self, ss_list):
 		if not ss_list or len(ss_list) < 1: 
 			log = "<p>" + _("No employee for the above selected criteria OR salary slip already created") + "</p>"
@@ -134,7 +131,6 @@
 		""" % ('%s', '%s', '%s','%s', cond), (ss_status, self.start_date, self.end_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
 		return ss_list
 
-
 	def submit_salary_slips(self):
 		"""
 			Submit all salary slips based on selected criteria
@@ -194,7 +190,6 @@
 	def format_as_links(self, salary_slip):
 		return ['<a href="#Form/Salary Slip/{0}">{0}</a>'.format(salary_slip)]
 
-
 	def get_total_salary_and_loan_amounts(self):
 		"""
 			Get total loan principal, loan interest and salary amount from submitted salary slip based on selected criteria
@@ -257,7 +252,6 @@
 
 		return payroll_payable_account	
 
-
 	def make_accural_jv_entry(self):
 		self.check_permission('write')
 		earnings = self.get_salary_component_total(component_type = "earnings") or {}
@@ -358,7 +352,6 @@
 		self.update(get_start_end_dates(self.payroll_frequency, 
 			self.start_date or self.posting_date, self.company))
 
-
 @frappe.whitelist()
 def get_start_end_dates(payroll_frequency, start_date=None, company=None):
 	'''Returns dict of start and end dates for given payroll frequency based on start_date'''
@@ -391,6 +384,29 @@
 		'start_date': start_date, 'end_date': end_date
 	})
 
+def get_frequency_kwargs(frequency_name):
+	frequency_dict = {
+		'monthly': {'months': 1},
+		'fortnightly': {'days': 14},
+		'weekly': {'days': 7},
+		'daily': {'days': 1}
+	}
+	return frequency_dict.get(frequency_name)
+
+@frappe.whitelist()
+def get_end_date(start_date, frequency):
+	start_date = getdate(start_date)
+	frequency = frequency.lower() if frequency else 'monthly'
+	kwargs = get_frequency_kwargs(frequency) if frequency != 'bimonthly' else get_frequency_kwargs('monthly')
+
+	# weekly, fortnightly and daily intervals have fixed days so no problems
+	end_date = add_to_date(start_date, **kwargs) - relativedelta(days=1)
+	if frequency != 'bimonthly':
+		return dict(end_date=end_date.strftime(DATE_FORMAT))
+
+	else:
+		return dict(end_date='')
+
 def get_month_details(year, month):
 	ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
 	if ysd:
diff --git a/erpnext/hr/doctype/process_payroll/test_process_payroll.py b/erpnext/hr/doctype/process_payroll/test_process_payroll.py
index 6347a2a..1f9ba26 100644
--- a/erpnext/hr/doctype/process_payroll/test_process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/test_process_payroll.py
@@ -3,10 +3,12 @@
 from __future__ import unicode_literals
 
 import unittest
-import frappe
+
 import erpnext
-from frappe.utils import flt, add_months, cint, nowdate, getdate, add_days, random_string
-from frappe.utils.make_random import get_random
+import frappe
+from frappe.utils import nowdate
+from erpnext.hr.doctype.process_payroll.process_payroll import get_end_date
+
 
 class TestProcessPayroll(unittest.TestCase):
 	def test_process_payroll(self):
@@ -30,6 +32,16 @@
 			process_payroll.submit_salary_slips()
 			if process_payroll.get_sal_slip_list(ss_status = 1):
 				r = process_payroll.make_payment_entry()
+
+	def test_get_end_date(self):
+		self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'})
+		self.assertEqual(get_end_date('2017-02-01', 'monthly'), {'end_date': '2017-02-28'})
+		self.assertEqual(get_end_date('2017-02-01', 'fortnightly'), {'end_date': '2017-02-14'})
+		self.assertEqual(get_end_date('2017-02-01', 'bimonthly'), {'end_date': ''})
+		self.assertEqual(get_end_date('2017-01-01', 'bimonthly'), {'end_date': ''})
+		self.assertEqual(get_end_date('2020-02-15', 'bimonthly'), {'end_date': ''})
+		self.assertEqual(get_end_date('2017-02-15', 'monthly'), {'end_date': '2017-03-14'})
+		self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'})
 	
 
 def get_salary_component_account(sal_comp):
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/hr/doctype/salary_slip/salary_slip.js
index a96347a..de3276c 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.js
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.js
@@ -29,6 +29,27 @@
 		})
 	},
 
+	start_date: function(frm){
+		if(frm.doc.start_date){
+			frm.trigger("set_end_date");
+		}
+	},
+
+	set_end_date: function(frm){
+		frappe.call({
+			method: 'erpnext.hr.doctype.process_payroll.process_payroll.get_end_date',
+			args: {
+				frequency: frm.doc.payroll_frequency,
+				start_date: frm.doc.start_date
+			},
+			callback: function (r) {
+				if (r.message) {
+					frm.set_value('end_date', r.message.end_date);
+				}
+			}
+		})
+	},
+
 	company: function(frm) {
 		var company = locals[':Company'][frm.doc.company];
 		if(!frm.doc.letter_head && company.default_letter_head) {
@@ -45,11 +66,17 @@
 	},	
 
 	salary_slip_based_on_timesheet: function(frm) {
-		frm.trigger("toggle_fields")
+		frm.trigger("toggle_fields");
+		frm.set_value('start_date', '');
 	},
 	
 	payroll_frequency: function(frm) {
-		frm.trigger("toggle_fields")
+		frm.trigger("toggle_fields");
+		frm.set_value('start_date', '');
+	},
+
+	employee: function(frm){
+		frm.set_value('start_date', '');
 	},
 
 	toggle_fields: function(frm) {
@@ -74,18 +101,19 @@
 // Get leave details
 //---------------------------------------------------------------------
 cur_frm.cscript.start_date = function(doc, dt, dn){
-	return frappe.call({
-		method: 'get_emp_and_leave_details',
-		doc: locals[dt][dn],
-		callback: function(r, rt) {
-			cur_frm.refresh();
-			calculate_all(doc, dt, dn);
-		}
-	});
+	if(!doc.start_date){
+		return frappe.call({
+			method: 'get_emp_and_leave_details',
+			doc: locals[dt][dn],
+			callback: function(r, rt) {
+				cur_frm.refresh();
+				calculate_all(doc, dt, dn);
+			}
+		});
+	}
 }
 
 cur_frm.cscript.payroll_frequency = cur_frm.cscript.salary_slip_based_on_timesheet = cur_frm.cscript.start_date;
-cur_frm.cscript.end_date = cur_frm.cscript.start_date;
 
 cur_frm.cscript.employee = function(doc,dt,dn){
 	doc.salary_structure = ''
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 681b62f..ce895898 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -51,6 +51,7 @@
 		from erpnext.utilities.transaction_base import validate_uom_is_integer
 		validate_uom_is_integer(self, "stock_uom", "stock_qty", "BOM Item")
 
+		self.update_stock_qty()
 		self.validate_materials()
 		self.set_bom_material_details()
 		self.validate_operations()
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.js b/erpnext/manufacturing/doctype/production_order/production_order.js
index 657819a..c61eec6 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.js
+++ b/erpnext/manufacturing/doctype/production_order/production_order.js
@@ -77,7 +77,6 @@
 		if (!frm.doc.status)
 			frm.doc.status = 'Draft';
 
-		frm.add_fetch("sales_order", "delivery_date", "expected_delivery_date");
 		frm.add_fetch("sales_order", "project", "project");
 
 		if(frm.doc.__islocal) {
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py
index 040a559..511f6de 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/production_order.py
@@ -50,8 +50,12 @@
 
 	def validate_sales_order(self):
 		if self.sales_order:
-			so = frappe.db.sql("""select name, delivery_date, project from `tabSales Order`
-				where name=%s and docstatus = 1""", self.sales_order, as_dict=1)
+			so = frappe.db.sql("""
+				select so.name, so_item.delivery_date, so.project 
+				from `tabSales Order` so, `tabSales Order Item` so_item
+				where so.name=%s and so.name=so_item.parent 
+					and so.docstatus = 1 and so_item.item_code=%s
+			""", (self.sales_order, self.production_item), as_dict=1)
 
 			if len(so):
 				if not self.expected_delivery_date:
diff --git a/erpnext/manufacturing/doctype/workstation/_test_workstation.js b/erpnext/manufacturing/doctype/workstation/_test_workstation.js
new file mode 100644
index 0000000..0f09bd1
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation/_test_workstation.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Workstation", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(1);
+
+	frappe.run_serially('Workstation', [
+		// insert a new Workstation
+		() => frappe.tests.make([
+			// values to be set
+			{key: 'value'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.key, 'value');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/manufacturing/doctype/workstation/workstation.json b/erpnext/manufacturing/doctype/workstation/workstation.json
index 38188be..dca9891 100644
--- a/erpnext/manufacturing/doctype/workstation/workstation.json
+++ b/erpnext/manufacturing/doctype/workstation/workstation.json
@@ -1,5 +1,6 @@
 {
  "allow_copy": 0, 
+ "allow_guest_to_view": 0, 
  "allow_import": 1, 
  "allow_rename": 1, 
  "autoname": "field:workstation_name", 
@@ -12,11 +13,12 @@
  "editable_grid": 0, 
  "fields": [
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
-   "collapsible": 0, 
+   "collapsible": 1, 
    "columns": 0, 
-   "fieldname": "description_and_warehouse", 
+   "fieldname": "description_section", 
    "fieldtype": "Section Break", 
    "hidden": 0, 
    "ignore_user_permissions": 0, 
@@ -25,7 +27,7 @@
    "in_global_search": 0, 
    "in_list_view": 0, 
    "in_standard_filter": 0, 
-   "label": "", 
+   "label": "Description", 
    "length": 0, 
    "no_copy": 0, 
    "permlevel": 0, 
@@ -41,6 +43,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -71,6 +74,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -102,65 +106,7 @@
    "width": "300px"
   }, 
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_4", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "", 
-   "fieldname": "holiday_list", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Holiday List", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Holiday List", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
-  {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -190,6 +136,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -221,6 +168,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -252,6 +200,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -280,6 +229,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -311,6 +261,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -342,6 +293,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -373,6 +325,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -402,6 +355,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -430,20 +384,52 @@
    "search_index": 0, 
    "set_only_once": 0, 
    "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "default": "", 
+   "fieldname": "holiday_list", 
+   "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 0, 
+   "in_standard_filter": 0, 
+   "label": "Holiday List", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Holiday List", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
   }
  ], 
+ "has_web_view": 0, 
  "hide_heading": 0, 
  "hide_toolbar": 0, 
  "icon": "icon-wrench", 
  "idx": 1, 
  "image_view": 0, 
  "in_create": 0, 
- "in_dialog": 0, 
  "is_submittable": 0, 
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2017-02-17 17:26:57.238167", 
+ "modified": "2017-07-18 22:28:50.163219", 
  "modified_by": "Administrator", 
  "module": "Manufacturing", 
  "name": "Workstation", 
@@ -470,11 +456,11 @@
    "write": 1
   }
  ], 
- "quick_entry": 0, 
+ "quick_entry": 1, 
  "read_only": 0, 
  "read_only_onload": 0, 
  "show_name_in_global_search": 1, 
  "sort_order": "ASC", 
- "track_changes": 0, 
+ "track_changes": 1, 
  "track_seen": 0
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/workstation/workstation_list.js b/erpnext/manufacturing/doctype/workstation/workstation_list.js
new file mode 100644
index 0000000..6a89d21
--- /dev/null
+++ b/erpnext/manufacturing/doctype/workstation/workstation_list.js
@@ -0,0 +1,5 @@
+/* eslint-disable */
+frappe.listview_settings['Workstation'] = {
+	// add_fields: ["status"],
+	// filters:[["status","=", "Open"]]
+};
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 48e3a35..fb8f02e 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -422,4 +422,5 @@
 erpnext.patches.v8_3.set_restrict_to_domain_for_module_def
 erpnext.patches.v8_1.update_expense_claim_status
 erpnext.patches.v8_3.update_company_total_sales
+erpnext.patches.v8_1.set_delivery_date_in_so_item
 erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs
\ No newline at end of file
diff --git a/erpnext/patches/v8_1/set_delivery_date_in_so_item.py b/erpnext/patches/v8_1/set_delivery_date_in_so_item.py
new file mode 100644
index 0000000..6840424
--- /dev/null
+++ b/erpnext/patches/v8_1/set_delivery_date_in_so_item.py
@@ -0,0 +1,13 @@
+import frappe
+
+def execute():
+	frappe.reload_doctype("Sales Order")
+	frappe.reload_doctype("Sales Order Item")
+
+	frappe.db.sql("""update `tabSales Order` set final_delivery_date = delivery_date where docstatus=1""")
+
+	frappe.db.sql("""
+		update `tabSales Order` so, `tabSales Order Item` so_item
+		set so_item.delivery_date = so.delivery_date
+		where so.name = so_item.parent
+	""")
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 78cdb13..f16cf07 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -3,6 +3,22 @@
 
 frappe.ui.form.on("Project", {
 	onload: function(frm) {
+		frm.set_indicator_formatter('title',
+			function(doc) {
+				let indicator = 'orange';
+				if (doc.status == 'Overdue') {
+					indicator = 'red';
+				}
+				else if (doc.status == 'Cancelled') {
+					indicator = 'dark grey';
+				}
+				else if (doc.status == 'Closed') {
+					indicator = 'green';
+				}
+				return indicator;
+			}
+		);
+
 		var so = frappe.meta.get_docfield("Project", "sales_order");
 		so.get_route_options_for_new_doc = function(field) {
 			if(frm.is_new()) return;
@@ -35,6 +51,7 @@
 			}
 		});
 	},
+
 	refresh: function(frm) {
 		if(frm.doc.__islocal) {
 			frm.web_link && frm.web_link.remove();
diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json
index 32a3ffd..28eb730 100644
--- a/erpnext/projects/doctype/project/project.json
+++ b/erpnext/projects/doctype/project/project.json
@@ -14,6 +14,7 @@
  "engine": "InnoDB", 
  "fields": [
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -44,6 +45,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -76,6 +78,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -107,6 +110,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -138,6 +142,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -169,6 +174,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -197,6 +203,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -228,6 +235,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -258,6 +266,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 1, 
    "collapsible": 0, 
@@ -269,7 +278,7 @@
    "ignore_xss_filter": 0, 
    "in_filter": 0, 
    "in_global_search": 0, 
-   "in_list_view": 0, 
+   "in_list_view": 1, 
    "in_standard_filter": 0, 
    "label": "Expected End Date", 
    "length": 0, 
@@ -288,6 +297,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 1, 
    "collapsible": 0, 
@@ -316,6 +326,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 1, 
@@ -346,6 +357,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -377,6 +389,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -405,6 +418,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -435,6 +449,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 1, 
@@ -464,6 +479,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -495,6 +511,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -525,6 +542,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -555,6 +573,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -584,6 +603,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 1, 
@@ -614,6 +634,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -644,6 +665,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 1, 
@@ -673,6 +695,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -702,6 +725,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -731,6 +755,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -759,6 +784,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -789,6 +815,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 1, 
@@ -819,6 +846,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -850,6 +878,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -880,6 +909,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -910,6 +940,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -939,6 +970,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -968,6 +1000,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -996,6 +1029,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -1026,6 +1060,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -1055,6 +1090,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -1084,6 +1120,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 1, 
@@ -1114,6 +1151,7 @@
    "width": "50%"
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -1145,6 +1183,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -1173,6 +1212,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -1215,8 +1255,8 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 4, 
- "modified": "2017-04-19 13:16:32.462005", 
- "modified_by": "faris@erpnext.com", 
+ "modified": "2017-07-19 14:36:20.857673", 
+ "modified_by": "Administrator", 
  "module": "Projects", 
  "name": "Project", 
  "owner": "Administrator", 
diff --git a/erpnext/projects/doctype/task/test_task.js b/erpnext/projects/doctype/task/test_task.js
new file mode 100644
index 0000000..8a1a5bf
--- /dev/null
+++ b/erpnext/projects/doctype/task/test_task.js
@@ -0,0 +1,24 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Task", function (assert) {
+	let done = assert.async();
+
+	// number of asserts
+	assert.expect(2);
+
+	frappe.run_serially([
+		// insert a new Task
+		() => frappe.tests.make('Task', [
+			// values to be set
+			{subject: 'new task'}
+		]),
+		() => {
+			assert.equal(cur_frm.doc.status, 'Open');
+			assert.equal(cur_frm.doc.priority, 'Low');
+		},
+		() => done()
+	]);
+
+});
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 2edfa9e..441ab1a 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -133,10 +133,15 @@
 	var child = locals[cdt][cdn];
 
 	var d = moment(child.from_time);
-	d.add(child.hours, "hours");
-	frm._setting_hours = true;
-	frappe.model.set_value(cdt, cdn, "to_time", d.format(moment.defaultDatetimeFormat));
-	frm._setting_hours = false;
+	if(child.hours) {
+		d.add(child.hours, "hours");
+		frm._setting_hours = true;
+		frappe.model.set_value(cdt, cdn, "to_time",
+			d.format(moment.defaultDatetimeFormat)).then(() => {
+				frm._setting_hours = false;
+			});
+	}
+
 
 	if((frm.doc.__islocal || frm.doc.__onload.maintain_bill_work_hours_same) && child.hours){
 		frappe.model.set_value(cdt, cdn, "billing_hours", child.hours);
diff --git a/erpnext/selling/doctype/quotation/test_quotation.js b/erpnext/selling/doctype/quotation/test_quotation.js
new file mode 100644
index 0000000..44173cc
--- /dev/null
+++ b/erpnext/selling/doctype/quotation/test_quotation.js
@@ -0,0 +1,53 @@
+QUnit.test("test: quotation", function (assert) {
+	assert.expect(10);
+	let done = assert.async();
+	frappe.run_serially([
+		() => {
+			return frappe.tests.make("Quotation", [
+				{customer: "Test Customer 1"},
+				{items: [
+					[
+						{"item_code": "Test Product 1"},
+						{"qty": 5}
+					]]
+				}
+			]);
+		},
+		() => {
+			// get_item_details
+			assert.ok(cur_frm.doc.items[0].item_name == "Test Product 1", "Added Test Product 1");
+
+			// calculate_taxes_and_totals
+			assert.ok(cur_frm.doc.grand_total === 500, "Total Amount is correct");
+		},
+		() => cur_frm.set_value("customer_address", "Test1-Billing"),
+		() => cur_frm.set_value("shipping_address_name", "Test1-Warehouse"),
+		() => cur_frm.set_value("contact_person", "Contact 1-Test Customer 1"),
+		() => cur_frm.set_value("currency", "USD"),
+		() => frappe.timeout(0.3),
+		() => cur_frm.set_value("selling_price_list", "Test-Selling-USD"),
+		() => frappe.timeout(0.5),
+		() => cur_frm.doc.items[0].rate = 200,
+		() => frappe.timeout(0.3),
+		() => cur_frm.set_value("tc_name", "Test Term 1"),
+		() => frappe.timeout(0.3),
+		() => cur_frm.save(),
+		() => {
+			// Check Address and Contact Info
+			assert.ok(cur_frm.doc.address_display.includes("Billing Street 1"), "Address Changed");
+			assert.ok(cur_frm.doc.shipping_address.includes("Warehouse Street 1"), "Address Changed");
+			assert.ok(cur_frm.doc.contact_display == "Contact 1", "Contact info changed");
+
+			// Check Currency
+			assert.ok(cur_frm.doc_currency == "USD", "Currency Changed");
+			assert.ok(cur_frm.doc.selling_price_list == "Test-Selling-USD", "Price List Changed");
+			assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually");
+			assert.equal(cur_frm.doc.total, 1000, "New Total Calculated");
+
+			// Check Terms and Condtions
+			assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked");
+
+		},
+		() => done()
+	]);
+});
\ No newline at end of file
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 640c026..7fb4074 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -27,7 +27,8 @@
 		self.assertEquals(sales_order.get("items")[0].prevdoc_docname, quotation.name)
 		self.assertEquals(sales_order.customer, "_Test Customer")
 
-		sales_order.delivery_date = "2014-01-01"
+		for d in sales_order.get("items"):
+			d.delivery_date = "2014-01-01"
 		sales_order.naming_series = "_T-Quotation-"
 		sales_order.transaction_date = "2013-05-12"
 		sales_order.insert()
@@ -51,9 +52,11 @@
 		quotation.submit()
 
 		sales_order = make_sales_order(quotation.name)
-		sales_order.delivery_date = "2016-01-02"
 		sales_order.naming_series = "_T-Quotation-"
 		sales_order.transaction_date = "2016-01-01"
+		for d in sales_order.get("items"):
+			d.delivery_date = "2016-01-02"
+
 		sales_order.insert()
 
 		self.assertEquals(quotation.get("items")[0].rate, rate_with_margin)
@@ -86,4 +89,4 @@
 				'rate': 100
 			}
 		]
-	}
\ No newline at end of file
+	}
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 457b6aa..2e01a08 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -33,7 +33,13 @@
 			function(doc) { return (doc.stock_qty<=doc.delivered_qty) ? "green" : "orange" })
 
 		erpnext.queries.setup_warehouse_query(frm);
-	}
+	},
+});
+
+frappe.ui.form.on("Sales Order Item", {
+	delivery_date: function(frm, cdt, cdn) {
+		erpnext.utils.copy_value_in_all_row(frm.doc, cdt, cdn, "items", "delivery_date");
+	}	
 });
 
 erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
@@ -77,7 +83,7 @@
 				// delivery note
 				if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
 					this.frm.add_custom_button(__('Delivery'),
-						function() { me.make_delivery_note() }, __("Make"));
+						function() { me.make_delivery_note_based_on_delivery_note(); }, __("Make"));
 					this.frm.add_custom_button(__('Production Order'),
 						function() { me.make_production_order() }, __("Make"));
 
@@ -235,7 +241,7 @@
 	},
 
 	order_type: function() {
-		this.frm.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
+		this.frm.fields_dict.items.grid.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
 	},
 
 	tc_name: function() {
@@ -249,10 +255,72 @@
 		})
 	},
 
+	make_delivery_note_based_on_delivery_note: function() {
+		var me = this;
+		
+		var delivery_dates = [];
+		$.each(this.frm.doc.items || [], function(i, d) {
+			if(!delivery_dates.includes(d.delivery_date)) {
+				delivery_dates.push(d.delivery_date);
+			}
+		});
+		
+		var item_grid = this.frm.fields_dict["items"].grid;
+		if(!item_grid.get_selected().length && delivery_dates.length > 1) {
+			var dialog = new frappe.ui.Dialog({
+				title: __("Select Items based on Delivery Date"),
+				fields: [{fieldtype: "HTML", fieldname: "dates_html"}]
+			});
+			
+			var html = $(`
+				<div style="border: 1px solid #d1d8dd">
+					<div class="list-item list-item--head">
+						<div class="list-item__content list-item__content--flex-2">
+							${__('Delivery Date')}
+						</div>
+					</div>
+					${delivery_dates.map(date => `
+						<div class="list-item">
+							<div class="list-item__content list-item__content--flex-2">
+								<label>
+								<input type="checkbox" data-date="${date}" checked="checked"/>
+								${frappe.datetime.str_to_user(date)}
+								</label>
+							</div>
+						</div>
+					`).join("")}
+				</div>
+			`);
+
+			var wrapper = dialog.fields_dict.dates_html.$wrapper;
+			wrapper.html(html);
+
+			dialog.set_primary_action(__("Select"), function() {
+				var dates = wrapper.find('input[type=checkbox]:checked')
+					.map((i, el) => $(el).attr('data-date')).toArray();
+
+				if(!dates) return;
+				
+				$.each(dates, function(i, d) {
+					$.each(item_grid.grid_rows || [], function(j, row) {
+						if(row.doc.delivery_date == d) {
+							row.doc.__checked = 1;
+						}
+					});
+				})
+				me.make_delivery_note();
+				dialog.hide();
+			});
+			dialog.show();
+		} else {
+			this.make_delivery_note();
+		}
+	},
+
 	make_delivery_note: function() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
-			frm: this.frm
+			frm: me.frm
 		})
 	},
 
@@ -344,6 +412,11 @@
 		if(cint(frappe.boot.notification_settings.sales_order)) {
 			this.frm.email_doc(frappe.boot.notification_settings.sales_order_message);
 		}
+	},
+
+	items_add: function(doc, cdt, cdn) {
+		var row = frappe.get_doc(cdt, cdn);
+		this.frm.script_manager.copy_from_first_row("items", row, ["delivery_date"]);
 	}
 });
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index c7c5ea2..8d19083 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -367,32 +367,29 @@
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
-   "depends_on": "eval:doc.order_type == 'Sales'", 
-   "fieldname": "delivery_date", 
+   "fieldname": "final_delivery_date", 
    "fieldtype": "Date", 
-   "hidden": 0, 
+   "hidden": 1, 
    "ignore_user_permissions": 0, 
    "ignore_xss_filter": 0, 
    "in_filter": 0, 
    "in_global_search": 0, 
-   "in_list_view": 0, 
+   "in_list_view": 1, 
    "in_standard_filter": 0, 
-   "label": "Delivery Date", 
+   "label": "Final Delivery Date", 
    "length": 0, 
    "no_copy": 1, 
-   "oldfieldname": "delivery_date", 
-   "oldfieldtype": "Date", 
    "permlevel": 0, 
-   "print_hide": 0, 
+   "precision": "", 
+   "print_hide": 1, 
    "print_hide_if_no_value": 0, 
-   "read_only": 0, 
+   "read_only": 1, 
    "remember_last_selected_value": 0, 
    "report_hide": 0, 
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
-   "unique": 0, 
-   "width": "160px"
+   "unique": 0
   }, 
   {
    "allow_bulk_edit": 0, 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index da9660b..72b1154 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -30,7 +30,6 @@
 
 		self.validate_order_type()
 		self.validate_delivery_date()
-		self.validate_mandatory()
 		self.validate_proj_cust()
 		self.validate_po()
 		self.validate_uom_is_integer("stock_uom", "stock_qty")
@@ -48,25 +47,20 @@
 		if not self.billing_status: self.billing_status = 'Not Billed'
 		if not self.delivery_status: self.delivery_status = 'Not Delivered'
 
-	def validate_mandatory(self):
-		# validate transaction date v/s delivery date
-		if self.delivery_date:
-			if getdate(self.transaction_date) > getdate(self.delivery_date):
-				frappe.msgprint(_("Expected Delivery Date is be before Sales Order Date"),
-					indicator='orange',
-					title=_('Warning'))
-
 	def validate_po(self):
 		# validate p.o date v/s delivery date
-		if self.po_date and self.delivery_date and getdate(self.po_date) > getdate(self.delivery_date):
-			frappe.throw(_("Expected Delivery Date cannot be before Purchase Order Date"))
+		if self.po_date:
+			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:
 			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))
-			if so and so[0][0] and not \
-				cint(frappe.db.get_single_value("Selling Settings", "allow_against_multiple_purchase_orders")):
+			if so and so[0][0] and not cint(frappe.db.get_single_value("Selling Settings",
+				"allow_against_multiple_purchase_orders")):
 				frappe.msgprint(_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(so[0][0], self.po_no))
 
 	def validate_for_items(self):
@@ -78,7 +72,7 @@
 			d.transaction_date = self.transaction_date
 
 			tot_avail_qty = frappe.db.sql("select projected_qty from `tabBin` \
-				where item_code = %s and warehouse = %s", (d.item_code,d.warehouse))
+				where item_code = %s and warehouse = %s", (d.item_code, d.warehouse))
 			d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0
 
 		# check for same entry multiple times
@@ -97,16 +91,30 @@
 	def validate_sales_mntc_quotation(self):
 		for d in self.get('items'):
 			if d.prevdoc_docname:
-				res = frappe.db.sql("select name from `tabQuotation` where name=%s and order_type = %s", (d.prevdoc_docname, self.order_type))
+				res = frappe.db.sql("select name from `tabQuotation` where name=%s and order_type = %s",
+					(d.prevdoc_docname, self.order_type))
 				if not res:
-					frappe.msgprint(_("Quotation {0} not of type {1}").format(d.prevdoc_docname, self.order_type))
+					frappe.msgprint(_("Quotation {0} not of type {1}")
+						.format(d.prevdoc_docname, self.order_type))
 
 	def validate_order_type(self):
 		super(SalesOrder, self).validate_order_type()
 
 	def validate_delivery_date(self):
-		if self.order_type == 'Sales' and not self.delivery_date:
-			frappe.throw(_("Please enter 'Expected Delivery Date'"))
+		self.final_delivery_date = None
+		if self.order_type == 'Sales':
+			for d in self.get("items"):
+				if not d.delivery_date:
+					frappe.throw(_("Row #{0}: Please enter Delivery Date against item {1}")
+						.format(d.idx, d.item_code))
+
+				if getdate(self.transaction_date) > getdate(d.delivery_date):
+					frappe.msgprint(_("Expected Delivery Date should be after Sales Order Date"),
+						indicator='orange', title=_('Warning'))
+
+				if not self.final_delivery_date or \
+					(d.delivery_date and getdate(d.delivery_date) > getdate(self.final_delivery_date)):
+						self.final_delivery_date = d.delivery_date
 
 		self.validate_sales_mntc_quotation()
 
@@ -122,7 +130,7 @@
 		super(SalesOrder, self).validate_warehouse()
 
 		for d in self.get("items"):
-			if (frappe.db.get_value("Item", d.item_code, "is_stock_item")==1 or
+			if (frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 or
 				(self.has_product_bundle(d.item_code) and self.product_bundle_has_stock_item(d.item_code))) \
 				and not d.warehouse and not cint(d.delivered_by_supplier):
 				frappe.throw(_("Delivery warehouse required for stock item {0}").format(d.item_code),
@@ -337,11 +345,14 @@
 
 		return items
 
-
 	def on_recurring(self, reference_doc):
 		mcount = month_map[reference_doc.recurring_type]
-		self.set("delivery_date", get_next_date(reference_doc.delivery_date, mcount,
-						cint(reference_doc.repeat_on_day_of_month)))
+		for d in self.get("items"):
+			reference_delivery_date = frappe.db.get_value("Sales Order Item",
+				{"parent": reference_doc.name, "item_code": d.item_code, "idx": d.idx}, "delivery_date")
+
+			d.set("delivery_date",
+				get_next_date(reference_delivery_date, mcount, cint(reference_doc.repeat_on_day_of_month)))
 
 def get_list_context(context=None):
 	from erpnext.controllers.website_list_for_contact import get_list_context
@@ -424,7 +435,6 @@
 			},
 			"field_map":{
 				"name" : "sales_order",
-				"delivery_date" : "expected_end_date",
 				"base_grand_total" : "estimated_costing",
 			}
 		},
@@ -615,12 +625,17 @@
 	from frappe.desk.calendar import get_event_conditions
 	conditions = get_event_conditions("Sales Order", filters)
 
-	data = frappe.db.sql("""select name, customer_name, status, delivery_status, billing_status, delivery_date
-		from `tabSales Order`
-		where (ifnull(delivery_date, '0000-00-00')!= '0000-00-00') \
-				and (delivery_date between %(start)s and %(end)s)
-				and docstatus < 2
-				{conditions}
+	data = frappe.db.sql("""
+		select
+			so.name, so.customer_name, so.status,
+			so.delivery_status, so.billing_status, so_item.delivery_date
+		from
+			`tabSales Order` so, `tabSales Order Item` so_item
+		where so.name = so_item.parent
+			and (ifnull(so_item.delivery_date, '0000-00-00')!= '0000-00-00') \
+			and (so_item.delivery_date between %(start)s and %(end)s)
+			and so.docstatus < 2
+			{conditions}
 		""".format(conditions=conditions), {
 			"start": start,
 			"end": end
@@ -660,7 +675,7 @@
 		target.run_method("calculate_taxes_and_totals")
 
 	def update_item(source, target, source_parent):
-		target.schedule_date = source_parent.delivery_date
+		target.schedule_date = source.delivery_date
 		target.qty = flt(source.qty) - flt(source.ordered_qty)
 		target.stock_qty = (flt(source.qty) - flt(source.ordered_qty)) * flt(source.conversion_factor)
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js
index d1ac6d9..0ee9cf3 100644
--- a/erpnext/selling/doctype/sales_order/sales_order_list.js
+++ b/erpnext/selling/doctype/sales_order/sales_order_list.js
@@ -1,14 +1,14 @@
 frappe.listview_settings['Sales Order'] = {
-	add_fields: ["base_grand_total", "customer_name", "currency", "delivery_date", "per_delivered", "per_billed",
-		"status", "order_type"],
+	add_fields: ["base_grand_total", "customer_name", "currency", "final_delivery_date",
+		"per_delivered", "per_billed", "status", "order_type", "name"],
 	get_indicator: function(doc) {
 		if(doc.status==="Closed"){
 			return [__("Closed"), "green", "status,=,Closed"];
 
 		} else if (doc.order_type !== "Maintenance"
-			&& flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
+			&& flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.final_delivery_date) < 0) {
 			// to bill & overdue
-			return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"];
+			return [__("Overdue"), "red", "per_delivered,<,100|final_delivery_date,<,Today|status,!=,Closed"];
 
 		} else if (doc.order_type !== "Maintenance"
 			&& flt(doc.per_delivered, 2) < 100 && doc.status!=="Closed") {
diff --git a/erpnext/selling/doctype/sales_order/test_records.json b/erpnext/selling/doctype/sales_order/test_records.json
index 12e953a..6cbd6c2 100644
--- a/erpnext/selling/doctype/sales_order/test_records.json
+++ b/erpnext/selling/doctype/sales_order/test_records.json
@@ -7,7 +7,6 @@
   "customer": "_Test Customer", 
   "customer_group": "_Test Customer Group", 
   "customer_name": "_Test Customer", 
-  "delivery_date": "2013-02-23", 
   "doctype": "Sales Order", 
   "base_grand_total": 1000.0, 
   "grand_total": 1000.0, 
@@ -23,6 +22,7 @@
     "doctype": "Sales Order Item", 
     "item_code": "_Test Item Home Desktop 100", 
     "item_name": "CPU", 
+    "delivery_date": "2013-02-23", 
     "parentfield": "items", 
     "qty": 10.0, 
     "rate": 100.0, 
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 0417e5e..8c07118 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -512,7 +512,6 @@
 
 	so.company = args.company or "_Test Company"
 	so.customer = args.customer or "_Test Customer"
-	so.delivery_date = add_days(so.transaction_date, 10)
 	so.currency = args.currency or "INR"
 	if args.selling_price_list:
 		so.selling_price_list = args.selling_price_list
@@ -533,6 +532,9 @@
 			"rate": args.rate or 100
 		})
 
+	for d in so.get("items"):
+		d.delivery_date = add_days(so.transaction_date, 10)
+
 	if not args.do_not_save:
 		so.insert()
 		if not args.do_not_submit:
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 2aae911..f14f50d 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -18,7 +18,7 @@
    "allow_on_submit": 0, 
    "bold": 1, 
    "collapsible": 0, 
-   "columns": 4, 
+   "columns": 3, 
    "fieldname": "item_code", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -205,6 +205,36 @@
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
+   "columns": 2, 
+   "fieldname": "delivery_date", 
+   "fieldtype": "Date", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Delivery Date", 
+   "length": 0, 
+   "no_copy": 1, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
    "columns": 0, 
    "fieldname": "column_break_7", 
    "fieldtype": "Column Break", 
@@ -324,7 +354,7 @@
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
-   "columns": 2, 
+   "columns": 1, 
    "fieldname": "qty", 
    "fieldtype": "Float", 
    "hidden": 0, 
@@ -1933,7 +1963,7 @@
  "istable": 1, 
  "max_attachments": 0, 
  "menu_index": 0, 
- "modified": "2017-05-10 17:14:48.277982", 
+ "modified": "2017-07-18 18:26:36.870342", 
  "modified_by": "Administrator", 
  "module": "Selling", 
  "name": "Sales Order Item", 
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 1baa843..271755e 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -547,7 +547,7 @@
    "in_standard_filter": 0,
    "label": "Sales Monthly History",
    "length": 0,
-   "no_copy": 0,
+   "no_copy": 1,
    "permlevel": 0,
    "precision": "",
    "print_hide": 0,
@@ -577,7 +577,7 @@
    "in_standard_filter": 0,
    "label": "Sales Target",
    "length": 0,
-   "no_copy": 0,
+   "no_copy": 1,
    "options": "default_currency",
    "permlevel": 0,
    "precision": "",
@@ -637,7 +637,7 @@
    "in_standard_filter": 0,
    "label": "Total Monthly Sales",
    "length": 0,
-   "no_copy": 0,
+   "no_copy": 1,
    "options": "default_currency",
    "permlevel": 0,
    "precision": "",
@@ -1961,7 +1961,7 @@
  "istable": 0,
  "max_attachments": 0,
  "menu_index": 0,
- "modified": "2017-07-17 15:50:27.218951",
+ "modified": "2017-07-19 03:16:27.171189",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index c8a0507..28f3a56 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -340,7 +340,7 @@
 	from frappe.utils.goal import get_monthly_results
 	import json
 	filter_str = "company = '{0}' and status != 'Draft'".format(frappe.db.escape(company))
-	month_to_value_dict = get_monthly_results("Sales Invoice", "grand_total", "posting_date", filter_str, "sum")
+	month_to_value_dict = get_monthly_results("Sales Invoice", "base_grand_total", "posting_date", filter_str, "sum")
 
 	frappe.db.sql(('''
 		update tabCompany set sales_monthly_history = %s where name=%s
diff --git a/erpnext/tests/ui/data/test_fixtures.js b/erpnext/tests/ui/make_fixtures.js
similarity index 87%
rename from erpnext/tests/ui/data/test_fixtures.js
rename to erpnext/tests/ui/make_fixtures.js
index 2ba5db5..0c5b4be 100644
--- a/erpnext/tests/ui/data/test_fixtures.js
+++ b/erpnext/tests/ui/make_fixtures.js
@@ -1,4 +1,11 @@
 $.extend(frappe.test_data, {
+	// "Fiscal Year": {
+	// 	"2017-18": [
+	// 		{"year": "2017-18"},
+	// 		{"year_start_date": "2017-04-01"},
+	// 		{"year_end_date": "2018-03-31"},
+	// 	]
+	// },
 	"Customer": {
 		"Test Customer 1": [
 			{customer_name: "Test Customer 1"}
@@ -198,19 +205,20 @@
 			{title: "Test Term 2"}
 		]
 	},
-	"Sales Taxes and Charges Template": {
-		"TEST In State GST": [
-			{title: "TEST In State GST"},
-			{taxes:[
-				[
-					{charge_type:"On Net Total"},
-					{account_head:"CGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
-				],
-				[
-					{charge_type:"On Net Total"},
-					{account_head:"SGST - "+frappe.get_abbr(frappe.defaults.get_default("Company")) }
-				]
-			]}
-		]
-	}
+});
+
+
+// this is a script that creates all fixtures
+// called as a test
+QUnit.module('fixture');
+
+QUnit.test('Make fixtures', assert => {
+	// create all fixtures first
+	assert.expect(0);
+	let done = assert.async();
+	let tasks = [];
+	Object.keys(frappe.test_data).forEach(function(doctype) {
+		tasks.push(function() { return frappe.tests.setup_doctype(doctype); });
+	});
+	frappe.run_serially(tasks).then(() => done());
 });
diff --git a/erpnext/tests/ui/selling/_test_lead.js b/erpnext/tests/ui/selling/_test_lead.js
deleted file mode 100644
index 2b895d9..0000000
--- a/erpnext/tests/ui/selling/_test_lead.js
+++ /dev/null
@@ -1,18 +0,0 @@
-QUnit.module("sales");
-
-QUnit.test("test: lead", function (assert) {
-	assert.expect(1);
-	let done = assert.async();
-	let random = frappe.utils.get_random(10);
-	frappe.run_serially([
-		() => frappe.tests.setup_doctype("Lead"),
-		() => frappe.set_route("List", "Lead"),
-		() => frappe.new_doc("Lead"),
-		() => cur_frm.set_value("lead_name", random),
-		() => cur_frm.save(),
-		() => {
-			assert.ok(cur_frm.doc.lead_name.includes(random));
-			return done();
-		}
-	]);
-});
diff --git a/erpnext/tests/ui/selling/_test_opportunity.js b/erpnext/tests/ui/selling/_test_opportunity.js
deleted file mode 100644
index 716a36e..0000000
--- a/erpnext/tests/ui/selling/_test_opportunity.js
+++ /dev/null
@@ -1,19 +0,0 @@
-QUnit.test("test: opportunity", function (assert) {
-	assert.expect(1);
-	let done = assert.async();
-	frappe.run_serially([
-		() => {
-			return frappe.tests.make("Opportunity", [{
-				enquiry_from: "Lead"
-			},
-			{
-				lead: "LEAD-00002"
-			}
-			]);
-		},
-		() => {
-			assert.ok(cur_frm.doc.lead === "LEAD-00002");
-			return done();
-		}
-	]);
-});
diff --git a/erpnext/tests/ui/selling/_test_quotation.js b/erpnext/tests/ui/selling/_test_quotation.js
deleted file mode 100644
index 62dd05d..0000000
--- a/erpnext/tests/ui/selling/_test_quotation.js
+++ /dev/null
@@ -1,81 +0,0 @@
-QUnit.test("test: quotation", function (assert) {
-	assert.expect(18);
-	let done = assert.async();
-	frappe.run_serially([
-		() => frappe.tests.setup_doctype("Customer"),
-		() => frappe.tests.setup_doctype("Item"),
-		() => frappe.tests.setup_doctype("Address"),
-		() => frappe.tests.setup_doctype("Contact"),
-		() => frappe.tests.setup_doctype("Price List"),
-		() => frappe.tests.setup_doctype("Terms and Conditions"),
-		() => frappe.tests.setup_doctype("Sales Taxes and Charges Template"),
-		() => {
-			return frappe.tests.make("Quotation", [{
-				customer: "Test Customer 1"
-			},
-			{
-				items: [
-					[{
-						"item_code": "Test Product 1"
-					},
-					{
-						"qty": 5
-					}
-					]
-				]
-			}
-			]);
-		},
-		() => {
-			// get_item_details
-			assert.ok(cur_frm.doc.items[0].item_name == "Test Product 1", "Added Test Product 1");
-
-			// calculate_taxes_and_totals
-			assert.ok(cur_frm.doc.grand_total === 500, "Total Amount is correct");
-		},
-		() => cur_frm.set_value("customer_address", "Test1-Billing"),
-		() => cur_frm.set_value("shipping_address_name", "Test1-Warehouse"),
-		() => cur_frm.set_value("contact_person", "Contact 1-Test Customer 1"),
-		() => cur_frm.set_value("currency", "USD"),
-		() => frappe.timeout(0.3),
-		() => cur_frm.set_value("selling_price_list", "Test-Selling-USD"),
-		() => frappe.timeout(0.5),
-		() => cur_frm.doc.items[0].rate = 200,
-		() => frappe.timeout(0.3),
-		() => cur_frm.set_value("tc_name", "Test Term 1"),
-		() => cur_frm.set_value("taxes_and_charges", "TEST In State GST"),
-		() => frappe.timeout(0.3),
-		() => cur_frm.save(),
-		() => {
-			// Check Address and Contact Info
-			assert.ok(cur_frm.doc.address_display.includes("Billing Street 1"), "Address Changed");
-			assert.ok(cur_frm.doc.shipping_address.includes("Warehouse Street 1"), "Address Changed");
-			assert.ok(cur_frm.doc.contact_display == "Contact 1", "Contact info changed");
-
-			// Check Currency
-			assert.ok(cur_frm.doc_currency == "USD", "Currency Changed");
-			assert.ok(cur_frm.doc.selling_price_list == "Test-Selling-USD", "Price List Changed");
-			assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually");
-			assert.ok(cur_frm.doc.total == 1000, "New Total Calculated");
-
-			// Check Terms and Condtions
-			assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked");
-
-			// Check Taxes
-			assert.ok(cur_frm.doc.taxes[0].account_head.includes("CGST"));
-			assert.ok(cur_frm.doc.taxes[1].account_head.includes("SGST"));
-			assert.ok(cur_frm.doc.grand_total == 1180, "Tax Amount Added to Total");
-			assert.ok(cur_frm.doc.taxes_and_charges == "TEST In State GST", "Tax Template Selected");
-		},
-		() => frappe.timeout(0.3),
-		() => cur_frm.print_doc(),
-		() => frappe.timeout(1),
-		() => assert.ok($('.btn-print-print').is(':visible'), "Print Format Available"),
-		() => assert.ok(RegExp(/QTN-\d\d\d\d\d/g).test($("#header-html small").text())),
-		() => assert.ok($(".important .text-right.value").text().includes("$ 1,180.00")),
-		() => assert.ok($(".section-break+ .section-break .column-break:nth-child(1) .data-field:nth-child(1) .value").text().includes("Billing Street 1"), "Print Preview Works As Expected"),
-		() => frappe.timeout(0.3),
-		() => cur_frm.print_doc(),
-		() => done()
-	]);
-});
\ No newline at end of file
diff --git a/erpnext/tests/ui/tests.txt b/erpnext/tests/ui/tests.txt
new file mode 100644
index 0000000..42233dc
--- /dev/null
+++ b/erpnext/tests/ui/tests.txt
@@ -0,0 +1,5 @@
+erpnext/tests/ui/make_fixtures.js #long
+erpnext/accounts/doctype/account/test_account.js
+erpnext/crm/doctype/lead/test_lead.js
+erpnext/crm/doctype/opportunity/test_opportunity.js
+erpnext/selling/doctype/quotation/test_quotation.js
\ No newline at end of file