Merge branch 'develop' of https://github.com/frappe/erpnext into develop
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index acfc660..9979377 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -332,6 +332,7 @@
    "label": "Reference"
   },
   {
+   "depends_on": "eval:doc.docstatus==0",
    "fieldname": "get_outstanding_invoice",
    "fieldtype": "Button",
    "label": "Get Outstanding Invoice"
@@ -575,7 +576,7 @@
   }
  ],
  "is_submittable": 1,
- "modified": "2019-11-06 12:59:43.151721",
+ "modified": "2019-12-08 13:02:30.016610",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Entry",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 3bb3df8..7b2061a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -830,7 +830,11 @@
 			)
 
 	def make_gle_for_rounding_adjustment(self, gl_entries):
-		if self.rounding_adjustment:
+		# if rounding adjustment in small and conversion rate is also small then
+		# base_rounding_adjustment may become zero due to small precision
+		# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
+		#	then base_rounding_adjustment becomes zero and error is thrown in GL Entry
+		if self.rounding_adjustment and self.base_rounding_adjustment:
 			round_off_account, round_off_cost_center = \
 				get_round_off_account_and_cost_center(self.company)
 
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index d024a31..c4d64a7 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -953,7 +953,7 @@
 			)
 
 	def make_gle_for_rounding_adjustment(self, gl_entries):
-		if flt(self.rounding_adjustment, self.precision("rounding_adjustment")):
+		if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment:
 			round_off_account, round_off_cost_center = \
 				get_round_off_account_and_cost_center(self.company)
 
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 15bc97c..6768dfa 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "hash",
  "creation": "2013-05-24 19:29:06",
  "doctype": "DocType",
@@ -43,6 +44,7 @@
   "base_amount",
   "pricing_rules",
   "is_free_item",
+  "is_fixed_asset",
   "section_break_29",
   "net_rate",
   "net_amount",
@@ -708,11 +710,20 @@
    "fieldname": "against_blanket_order",
    "fieldtype": "Check",
    "label": "Against Blanket Order"
+  },
+  {
+   "default": "0",
+   "fetch_from": "item_code.is_fixed_asset",
+   "fieldname": "is_fixed_asset",
+   "fieldtype": "Check",
+   "label": "Is Fixed Asset",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
- "modified": "2019-11-19 14:10:52.865006",
+ "links": [],
+ "modified": "2019-12-06 13:17:12.142799",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order Item",
diff --git a/erpnext/buying/setup_wizard_slide/add_a_few_suppliers/add_a_few_suppliers.json b/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json
similarity index 78%
rename from erpnext/buying/setup_wizard_slide/add_a_few_suppliers/add_a_few_suppliers.json
rename to erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json
index 006d139..d3adcb7 100644
--- a/erpnext/buying/setup_wizard_slide/add_a_few_suppliers/add_a_few_suppliers.json
+++ b/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json
@@ -3,18 +3,18 @@
  "app": "ERPNext",
  "creation": "2019-11-15 14:45:32.626641",
  "docstatus": 0,
- "doctype": "Setup Wizard Slide",
+ "doctype": "Onboarding Slide",
  "domains": [],
  "help_links": [
   {
-   "label": "Supplier",
+   "label": "Learn More",
    "video_id": "zsrrVDk6VBs"
   }
  ],
  "idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/supplier.png",
+ "image_src": "/assets/erpnext/images/illustrations/supplier-onboard.png",
  "max_count": 3,
- "modified": "2019-11-26 18:26:25.498325",
+ "modified": "2019-12-03 22:53:50.552445",
  "modified_by": "Administrator",
  "name": "Add A Few Suppliers",
  "owner": "Administrator",
@@ -44,6 +44,5 @@
  ],
  "slide_order": 50,
  "slide_title": "Add A Few Suppliers",
- "slide_type": "Create",
- "submit_method": ""
+ "slide_type": "Create"
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
index bc4a1b4..7a9727f 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py
@@ -5,9 +5,10 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import date_diff, add_days, getdate
+from frappe.utils import date_diff, add_days, getdate, cint
 from frappe.model.document import Document
-from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, get_holidays_for_employee
+from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, \
+	get_holidays_for_employee, create_additional_leave_ledger_entry
 
 class CompensatoryLeaveRequest(Document):
 
@@ -25,16 +26,14 @@
 			frappe.throw(_("Leave Type is madatory"))
 
 	def validate_attendance(self):
-		query = """select attendance_date, status
-			from `tabAttendance` where
-			attendance_date between %(work_from_date)s and %(work_end_date)s
-			and docstatus=1 and status = 'Present' and employee=%(employee)s"""
+		attendance = frappe.get_all('Attendance',
+			filters={
+				'attendance_date': ['between', (self.work_from_date, self.work_end_date)],
+				'status': 'Present',
+				'docstatus': 1,
+				'employee': self.employee
+			}, fields=['attendance_date', 'status'])
 
-		attendance = frappe.db.sql(query, {
-			"work_from_date": self.work_from_date,
-			"work_end_date": self.work_end_date,
-			"employee": self.employee
-		}, as_dict=True)
 		if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1:
 			frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
 
@@ -50,13 +49,19 @@
 			date_difference -= 0.5
 		leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
 		if leave_period:
-			leave_allocation = self.exists_allocation_for_period(leave_period)
+			leave_allocation = self.get_existing_allocation_for_period(leave_period)
 			if leave_allocation:
 				leave_allocation.new_leaves_allocated += date_difference
-				leave_allocation.submit()
+				leave_allocation.validate()
+				leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
+				leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
+
+				# generate additional ledger entry for the new compensatory leaves off
+				create_additional_leave_ledger_entry(leave_allocation, date_difference, add_days(self.work_end_date, 1))
+
 			else:
 				leave_allocation = self.create_leave_allocation(leave_period, date_difference)
-			self.db_set("leave_allocation", leave_allocation.name)
+			self.leave_allocation=leave_allocation.name
 		else:
 			frappe.throw(_("There is no leave period in between {0} and {1}").format(self.work_from_date, self.work_end_date))
 
@@ -68,11 +73,16 @@
 			leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation)
 			if leave_allocation:
 				leave_allocation.new_leaves_allocated -= date_difference
-				if leave_allocation.total_leaves_allocated - date_difference <= 0:
-					leave_allocation.total_leaves_allocated = 0
-				leave_allocation.submit()
+				if leave_allocation.new_leaves_allocated - date_difference <= 0:
+					leave_allocation.new_leaves_allocated = 0
+				leave_allocation.validate()
+				leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
+				leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
 
-	def exists_allocation_for_period(self, leave_period):
+				# create reverse entry on cancelation
+				create_additional_leave_ledger_entry(leave_allocation, date_difference * -1, add_days(self.work_end_date, 1))
+
+	def get_existing_allocation_for_period(self, leave_period):
 		leave_allocation = frappe.db.sql("""
 			select name
 			from `tabLeave Allocation`
@@ -95,17 +105,18 @@
 
 	def create_leave_allocation(self, leave_period, date_difference):
 		is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
-		allocation = frappe.new_doc("Leave Allocation")
-		allocation.employee = self.employee
-		allocation.employee_name = self.employee_name
-		allocation.leave_type = self.leave_type
-		allocation.from_date = add_days(self.work_end_date, 1)
-		allocation.to_date = leave_period[0].to_date
-		allocation.new_leaves_allocated = date_difference
-		allocation.total_leaves_allocated = date_difference
-		allocation.description = self.reason
-		if is_carry_forward == 1:
-			allocation.carry_forward = True
-		allocation.save(ignore_permissions = True)
+		allocation = frappe.get_doc(dict(
+			doctype="Leave Allocation",
+			employee=self.employee,
+			employee_name=self.employee_name,
+			leave_type=self.leave_type,
+			from_date=add_days(self.work_end_date, 1),
+			to_date=leave_period[0].to_date,
+			carry_forward=cint(is_carry_forward),
+			new_leaves_allocated=date_difference,
+			total_leaves_allocated=date_difference,
+			description=self.reason
+		))
+		allocation.insert(ignore_permissions=True)
 		allocation.submit()
-		return allocation
+		return allocation
\ No newline at end of file
diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
index f2ca1f4..1615ab3 100644
--- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
+++ b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py
@@ -5,37 +5,128 @@
 
 import frappe
 import unittest
+from frappe.utils import today, add_months, add_days
+from erpnext.hr.doctype.attendance_request.test_attendance_request import get_employee
+from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
+from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
 
-# class TestCompensatoryLeaveRequest(unittest.TestCase):
-# 	def get_compensatory_leave_request(self):
-# 		return frappe.get_doc('Compensatory Leave Request', dict(
-# 			employee = employee,
-# 			work_from_date = today,
-# 			work_to_date = today,
-# 			reason = 'test'
-# 		)).insert()
-#
-# 	def test_creation_of_leave_allocation(self):
-# 		employee = get_employee()
-# 		today = get_today()
-#
-# 		compensatory_leave_request = self.get_compensatory_leave_request(today)
-#
-# 		before = get_leave_balance(employee, compensatory_leave_request.leave_type)
-#
-# 		compensatory_leave_request.submit()
-#
-# 		self.assertEqual(get_leave_balance(employee, compensatory_leave_request.leave_type), before + 1)
-#
-# 	def test_max_compensatory_leave(self):
-# 		employee = get_employee()
-# 		today = get_today()
-#
-# 		compensatory_leave_request = self.get_compensatory_leave_request()
-#
-# 		frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 0)
-#
-# 		self.assertRaises(MaxLeavesLimitCrossed, compensatory_leave_request.submit)
-#
-# 		frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 10)
-#
+class TestCompensatoryLeaveRequest(unittest.TestCase):
+	def setUp(self):
+		frappe.db.sql(''' delete from `tabCompensatory Leave Request`''')
+		frappe.db.sql(''' delete from `tabLeave Ledger Entry`''')
+		frappe.db.sql(''' delete from `tabLeave Allocation`''')
+		frappe.db.sql(''' delete from `tabAttendance` where attendance_date in {0} '''.format((today(), add_days(today(), -1)))) #nosec
+		create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company")
+		create_holiday_list()
+
+		employee = get_employee()
+		employee.holiday_list = "_Test Compensatory Leave"
+		employee.save()
+
+	def test_leave_balance_on_submit(self):
+		''' check creation of leave allocation on submission of compensatory leave request '''
+		employee = get_employee()
+		mark_attendance(employee)
+		compensatory_leave_request = get_compensatory_leave_request(employee.name)
+
+		before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today())
+		compensatory_leave_request.submit()
+
+		self.assertEqual(get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, add_days(today(), 1)), before + 1)
+
+	def test_leave_allocation_update_on_submit(self):
+		employee = get_employee()
+		mark_attendance(employee, date=add_days(today(), -1))
+		compensatory_leave_request = get_compensatory_leave_request(employee.name, leave_date=add_days(today(), -1))
+		compensatory_leave_request.submit()
+
+		# leave allocation creation on submit
+		leaves_allocated = frappe.db.get_value('Leave Allocation', {
+				'name': compensatory_leave_request.leave_allocation
+			}, ['total_leaves_allocated'])
+		self.assertEqual(leaves_allocated, 1)
+
+		mark_attendance(employee)
+		compensatory_leave_request = get_compensatory_leave_request(employee.name)
+		compensatory_leave_request.submit()
+
+		# leave allocation updates on submission of second compensatory leave request
+		leaves_allocated = frappe.db.get_value('Leave Allocation', {
+				'name': compensatory_leave_request.leave_allocation
+			}, ['total_leaves_allocated'])
+		self.assertEqual(leaves_allocated, 2)
+
+	def test_creation_of_leave_ledger_entry_on_submit(self):
+		''' check creation of leave ledger entry on submission of leave request '''
+		employee = get_employee()
+		mark_attendance(employee)
+		compensatory_leave_request = get_compensatory_leave_request(employee.name)
+		compensatory_leave_request.submit()
+
+		filters = dict(transaction_name=compensatory_leave_request.leave_allocation)
+		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters)
+
+		self.assertEquals(len(leave_ledger_entry), 1)
+		self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
+		self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
+		self.assertEquals(leave_ledger_entry[0].leaves, 1)
+
+		# check reverse leave ledger entry on cancellation
+		compensatory_leave_request.cancel()
+		leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters, order_by = 'creation desc')
+
+		self.assertEquals(len(leave_ledger_entry), 2)
+		self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
+		self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
+		self.assertEquals(leave_ledger_entry[0].leaves, -1)
+
+def get_compensatory_leave_request(employee, leave_date=today()):
+	prev_comp_leave_req = frappe.db.get_value('Compensatory Leave Request',
+		dict(leave_type='Compensatory Off',
+			work_from_date=leave_date,
+			work_end_date=leave_date,
+			employee=employee), 'name')
+	if prev_comp_leave_req:
+		return frappe.get_doc('Compensatory Leave Request', prev_comp_leave_req)
+
+	return frappe.get_doc(dict(
+			doctype='Compensatory Leave Request',
+			employee=employee,
+			leave_type='Compensatory Off',
+			work_from_date=leave_date,
+			work_end_date=leave_date,
+			reason='test'
+		)).insert()
+
+def mark_attendance(employee, date=today(), status='Present'):
+	if not frappe.db.exists(dict(doctype='Attendance', employee=employee.name, attendance_date=date, status='Present')):
+		attendance = frappe.get_doc({
+				"doctype": "Attendance",
+				"employee": employee.name,
+				"attendance_date": date,
+				"status": status
+		})
+		attendance.save()
+		attendance.submit()
+
+def create_holiday_list():
+	if frappe.db.exists("Holiday List", "_Test Compensatory Leave"):
+		return
+
+	holiday_list = frappe.get_doc({
+		"doctype": "Holiday List",
+		"from_date": add_months(today(), -3),
+		"to_date": add_months(today(), 3),
+		"holidays": [
+			{
+				"description": "Test Holiday",
+				"holiday_date": today()
+			},
+			{
+				"description": "Test Holiday 1",
+				"holiday_date": add_days(today(), -1)
+			}
+		],
+		"holiday_list_name": "_Test Compensatory Leave"
+	})
+	holiday_list.save()
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
index 874ae7a..d13bb45 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -69,10 +69,14 @@
 
 	def validate_allocation_overlap(self):
 		leave_allocation = frappe.db.sql("""
-			select name from `tabLeave Allocation`
-			where employee=%s and leave_type=%s and docstatus=1
-			and to_date >= %s and from_date <= %s""",
-			(self.employee, self.leave_type, self.from_date, self.to_date))
+			SELECT
+				name
+			FROM `tabLeave Allocation`
+			WHERE
+				employee=%s AND leave_type=%s
+				AND name <> %s AND docstatus=1
+				AND to_date >= %s AND from_date <= %s""",
+			(self.employee, self.leave_type, self.name, self.from_date, self.to_date))
 
 		if leave_allocation:
 			frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}")
diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js
index db3819e..e32d570 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.js
+++ b/erpnext/hr/doctype/leave_application/leave_application.js
@@ -170,7 +170,7 @@
 				frm.set_value('to_date', '');
 				return;
 			}
-				// server call is done to include holidays in leave days calculations
+			// server call is done to include holidays in leave days calculations
 			return frappe.call({
 				method: 'erpnext.hr.doctype.leave_application.leave_application.get_number_of_leave_days',
 				args: {
@@ -193,7 +193,7 @@
 
 	set_leave_approver: function(frm) {
 		if(frm.doc.employee) {
-				// server call is done to include holidays in leave days calculations
+			// server call is done to include holidays in leave days calculations
 			return frappe.call({
 				method: 'erpnext.hr.doctype.leave_application.leave_application.get_leave_approver',
 				args: {
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 0e66305..65fcbf7 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -549,10 +549,10 @@
 			leave_days += leave_entry.leaves
 
 		elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \
-			and not skip_expiry_leaves(leave_entry, to_date):
+			and leave_entry.is_expired and not skip_expiry_leaves(leave_entry, to_date):
 			leave_days += leave_entry.leaves
 
-		else:
+		elif leave_entry.transaction_type == 'Leave Application':
 			if leave_entry.from_date < getdate(from_date):
 				leave_entry.from_date = from_date
 			if leave_entry.to_date > getdate(to_date):
@@ -579,14 +579,15 @@
 def get_leave_entries(employee, leave_type, from_date, to_date):
 	''' Returns leave entries between from_date and to_date '''
 	return frappe.db.sql("""
-		select employee, leave_type, from_date, to_date, leaves, transaction_type, is_carry_forward, transaction_name
-		from `tabLeave Ledger Entry`
-		where employee=%(employee)s and leave_type=%(leave_type)s
-			and docstatus=1
-			and leaves<0
-			and (from_date between %(from_date)s and %(to_date)s
-				or to_date between %(from_date)s and %(to_date)s
-				or (from_date < %(from_date)s and to_date > %(to_date)s))
+		SELECT
+			employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type,
+			is_carry_forward, is_expired
+		FROM `tabLeave Ledger Entry`
+		WHERE employee=%(employee)s AND leave_type=%(leave_type)s
+			AND docstatus=1 AND leaves<0
+			AND (from_date between %(from_date)s AND %(to_date)s
+				OR to_date between %(from_date)s AND %(to_date)s
+				OR (from_date < %(from_date)s AND to_date > %(to_date)s))
 	""", {
 		"from_date": from_date,
 		"to_date": to_date,
@@ -773,4 +774,4 @@
 		leave_approver = frappe.db.get_value('Department Approver', {'parent': department,
 			'parentfield': 'leave_approvers', 'idx': 1}, 'approver')
 
-	return leave_approver
+	return leave_approver
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py
index 38ae808..b9c0210 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.py
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.py
@@ -301,7 +301,7 @@
 			to_date = add_days(date, 2),
 			company = "_Test Company",
 			docstatus = 1,
-            status = "Approved"
+			status = "Approved"
 		))
 		leave_application.submit()
 
@@ -314,7 +314,7 @@
 			to_date = add_days(date, 8),
 			company = "_Test Company",
 			docstatus = 1,
-            status = "Approved"
+			status = "Approved"
 		))
 		self.assertRaises(frappe.ValidationError, leave_application.insert)
 
diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.py b/erpnext/hr/doctype/leave_period/test_leave_period.py
index 850a08d..1762cf9 100644
--- a/erpnext/hr/doctype/leave_period/test_leave_period.py
+++ b/erpnext/hr/doctype/leave_period/test_leave_period.py
@@ -43,10 +43,18 @@
 		leave_period.grant_leave_allocation(employee=employee_doc_name)
 		self.assertEqual(get_leave_balance_on(employee_doc_name, leave_type, today()), 20)
 
-def create_leave_period(from_date, to_date):
+def create_leave_period(from_date, to_date, company=None):
+	leave_period = frappe.db.get_value('Leave Period',
+		dict(company=company or erpnext.get_default_company(),
+			from_date=from_date,
+			to_date=to_date,
+			is_active=1), 'name')
+	if leave_period:
+		return frappe.get_doc("Leave Period", leave_period)
+
 	leave_period = frappe.get_doc({
 		"doctype": "Leave Period",
-		"company": erpnext.get_default_company(),
+		"company": company or erpnext.get_default_company(),
 		"from_date": from_date,
 		"to_date": to_date,
 		"is_active": 1
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index 1464a77..c3e8d27 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -321,11 +321,11 @@
 				if new_allocation == allocation.total_leaves_allocated:
 					continue
 				allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False)
-				create_earned_leave_ledger_entry(allocation, earned_leaves, today)
+				create_additional_leave_ledger_entry(allocation, earned_leaves, today)
 
-def create_earned_leave_ledger_entry(allocation, earned_leaves, date):
-	''' Create leave ledger entry based on the earned leave frequency '''
-	allocation.new_leaves_allocated = earned_leaves
+def create_additional_leave_ledger_entry(allocation, leaves, date):
+	''' Create leave ledger entry for leave types '''
+	allocation.new_leaves_allocated = leaves
 	allocation.from_date = date
 	allocation.unused_leaves = 0
 	allocation.create_leave_ledger_entry()
@@ -389,6 +389,7 @@
 
 def get_holidays_for_employee(employee, start_date, end_date):
 	holiday_list = get_holiday_list_for_employee(employee)
+
 	holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
 		where
 			parent=%(holiday_list)s
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 5579954..e3ece56 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -606,6 +606,7 @@
 				item.image,
 				bom.project,
 				item.stock_uom,
+				item.item_group,
 				item.allow_alternative_item,
 				item_default.default_warehouse,
 				item_default.expense_account as expense_account,
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 25c385f..8876253 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -615,6 +615,9 @@
 
 	doc['mr_items'] = []
 	po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
+	if not po_items:
+		frappe.throw(_("Items are required to pull the raw materials which is associated with it."))
+
 	company = doc.get('company')
 	warehouse = doc.get('for_warehouse')
 
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 25c97d1..3570a0f 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -4,20 +4,16 @@
 	setup(frm) {
 		frm.make_methods = {
 			'Timesheet': () => {
-				let doctype = 'Timesheet';
-				frappe.model.with_doctype(doctype, () => {
-					let new_doc = frappe.model.get_new_doc(doctype);
-
-					// add a new row and set the project
-					let time_log = frappe.model.get_new_doc('Timesheet Detail');
-					time_log.project = frm.doc.name;
-					time_log.parent = new_doc.name;
-					time_log.parentfield = 'time_logs';
-					time_log.parenttype = 'Timesheet';
-					new_doc.time_logs = [time_log];
-
-					frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
-				});
+				open_form(frm, "Timesheet", "Timesheet Detail", "time_logs");
+			},
+			'Purchase Order': () => {
+				open_form(frm, "Purchase Order", "Purchase Order Item", "items");
+			},
+			'Purchase Receipt': () => {
+				open_form(frm, "Purchase Receipt", "Purchase Receipt Item", "items");
+			},
+			'Purchase Invoice': () => {
+				open_form(frm, "Purchase Invoice", "Purchase Invoice Item", "items");
 			},
 		};
 	},
@@ -80,7 +76,7 @@
 				frm.events.set_status(frm, 'Cancelled');
 			}, __('Set Status'));
 		}
-		
+
 		if (frappe.model.can_read("Task")) {
 			frm.add_custom_button(__("Gantt Chart"), function () {
 				frappe.route_options = {
@@ -123,3 +119,20 @@
 	},
 
 });
+
+function open_form(frm, doctype, child_doctype, parentfield) {
+	frappe.model.with_doctype(doctype, () => {
+		let new_doc = frappe.model.get_new_doc(doctype);
+
+		// add a new row and set the project
+		let new_child_doc = frappe.model.get_new_doc(child_doctype);
+		new_child_doc.project = frm.doc.name;
+		new_child_doc.parent = new_doc.name;
+		new_child_doc.parentfield = parentfield;
+		new_child_doc.parenttype = doctype;
+		new_doc[parentfield] = [new_child_doc];
+
+		frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
+	});
+
+}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index c4481c9..e908216 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -188,7 +188,8 @@
 			}, as_dict=True)
 		# check internal overlap
 		for time_log in self.time_logs:
-			if not (time_log.from_time or time_log.to_time): continue
+			if not (time_log.from_time and time_log.to_time
+				and args.from_time and args.to_time): continue
 
 			if (fieldname != 'workstation' or args.get(fieldname) == time_log.get(fieldname)) and \
 				args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or
diff --git a/erpnext/public/images/illustrations/collaboration.png b/erpnext/public/images/illustrations/collaboration.png
deleted file mode 100644
index 12c67e3..0000000
--- a/erpnext/public/images/illustrations/collaboration.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/customer.png b/erpnext/public/images/illustrations/customer.png
deleted file mode 100644
index b2ddbf3..0000000
--- a/erpnext/public/images/illustrations/customer.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/customers-onboard.png b/erpnext/public/images/illustrations/customers-onboard.png
new file mode 100644
index 0000000..4a517bd
--- /dev/null
+++ b/erpnext/public/images/illustrations/customers-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/desk-onboard.png b/erpnext/public/images/illustrations/desk-onboard.png
new file mode 100644
index 0000000..74b632d
--- /dev/null
+++ b/erpnext/public/images/illustrations/desk-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/letterhead-onboard.png b/erpnext/public/images/illustrations/letterhead-onboard.png
new file mode 100644
index 0000000..fdfd16a
--- /dev/null
+++ b/erpnext/public/images/illustrations/letterhead-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/letterhead.png b/erpnext/public/images/illustrations/letterhead.png
deleted file mode 100644
index 37df6d7..0000000
--- a/erpnext/public/images/illustrations/letterhead.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/onboard.png b/erpnext/public/images/illustrations/onboard.png
deleted file mode 100644
index 094aa3f..0000000
--- a/erpnext/public/images/illustrations/onboard.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/product.png b/erpnext/public/images/illustrations/product.png
deleted file mode 100644
index f864b7a..0000000
--- a/erpnext/public/images/illustrations/product.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/products-onboard.png b/erpnext/public/images/illustrations/products-onboard.png
new file mode 100644
index 0000000..2dee203
--- /dev/null
+++ b/erpnext/public/images/illustrations/products-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/supplier-onboard.png b/erpnext/public/images/illustrations/supplier-onboard.png
new file mode 100644
index 0000000..30335f2
--- /dev/null
+++ b/erpnext/public/images/illustrations/supplier-onboard.png
Binary files differ
diff --git a/erpnext/public/images/illustrations/supplier.png b/erpnext/public/images/illustrations/supplier.png
deleted file mode 100644
index 87f7789..0000000
--- a/erpnext/public/images/illustrations/supplier.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/images/illustrations/user.png b/erpnext/public/images/illustrations/user.png
deleted file mode 100644
index 7dd7db2..0000000
--- a/erpnext/public/images/illustrations/user.png
+++ /dev/null
Binary files differ
diff --git a/erpnext/public/js/hub/PageContainer.vue b/erpnext/public/js/hub/PageContainer.vue
index f151add..54c3597 100644
--- a/erpnext/public/js/hub/PageContainer.vue
+++ b/erpnext/public/js/hub/PageContainer.vue
@@ -24,7 +24,7 @@
 function get_route_map() {
 	const read_only_routes = {
 		'marketplace/home': Home,
-		'marketplace/search/:keyword': Search,
+		'marketplace/search/:category/:keyword': Search,
 		'marketplace/category/:category': Category,
 		'marketplace/item/:item': Item,
 		'marketplace/seller/:seller': Seller,
diff --git a/erpnext/public/js/hub/pages/Category.vue b/erpnext/public/js/hub/pages/Category.vue
index 3a0e6bf..057fe8b 100644
--- a/erpnext/public/js/hub/pages/Category.vue
+++ b/erpnext/public/js/hub/pages/Category.vue
@@ -3,6 +3,12 @@
 		class="marketplace-page"
 		:data-page-name="page_name"
 	>
+		<search-input
+			:placeholder="search_placeholder"
+			:on_search="set_search_route"
+			v-model="search_value"
+		/>
+
 		<h5>{{ page_title }}</h5>
 
 		<item-cards-container
@@ -26,7 +32,13 @@
 			item_id_fieldname: 'name',
 
 			// Constants
-			empty_state_message: __(`No items in this category yet.`)
+			empty_state_message: __(`No items in this category yet.`),
+
+			search_value: '',
+
+			// Constants
+			search_placeholder: __('Search for anything ...'),
+
 		};
 	},
 	computed: {
@@ -35,6 +47,7 @@
 		}
 	},
 	created() {
+		this.search_value = '';
 		this.get_items();
 	},
 	methods: {
@@ -51,7 +64,11 @@
 
 		go_to_item_details_page(hub_item_name) {
 			frappe.set_route(`marketplace/item/${hub_item_name}`);
-		}
+		},
+
+		set_search_route() {
+			frappe.set_route('marketplace', 'search', this.category, this.search_value);
+		},
 	}
 }
 </script>
diff --git a/erpnext/public/js/hub/pages/Home.vue b/erpnext/public/js/hub/pages/Home.vue
index 3536569..aaeaa7e 100644
--- a/erpnext/public/js/hub/pages/Home.vue
+++ b/erpnext/public/js/hub/pages/Home.vue
@@ -98,7 +98,7 @@
 		},
 
 		set_search_route() {
-			frappe.set_route('marketplace', 'search', this.search_value);
+			frappe.set_route('marketplace', 'search', 'All', this.search_value);
 		},
 	}
 }
diff --git a/erpnext/public/js/hub/pages/Search.vue b/erpnext/public/js/hub/pages/Search.vue
index 5118a81..1032842 100644
--- a/erpnext/public/js/hub/pages/Search.vue
+++ b/erpnext/public/js/hub/pages/Search.vue
@@ -29,8 +29,10 @@
 		return {
 			page_name: frappe.get_route()[1],
 			items: [],
-			search_value: frappe.get_route()[2],
+			category: frappe.get_route()[2],
+			search_value: frappe.get_route()[3],
 			item_id_fieldname: 'name',
+			filters: {},
 
 			// Constants
 			search_placeholder: __('Search for anything ...'),
@@ -40,7 +42,7 @@
 	computed: {
 		page_title() {
 			return this.items.length
-				? __(`Results for "${this.search_value}"`)
+				? __(`Results for "${this.search_value}" ${this.category !== 'All'? `in category ${this.category}` : ''}`)
 				: __('No Items found.');
 		}
 	},
@@ -49,14 +51,20 @@
 	},
 	methods: {
 		get_items() {
-			hub.call('get_items', { keyword: this.search_value })
+			if (this.category !== 'All') {
+				this.filters['hub_category'] = this.category;
+			}
+			hub.call('get_items', {
+				keyword: this.search_value,
+				filters: this.filters
+			})
 			.then((items) => {
 				this.items = items;
 			})
 		},
 
 		set_route_and_get_items() {
-			frappe.set_route('marketplace', 'search', this.search_value);
+			frappe.set_route('marketplace', 'search', this.category, this.search_value);
 			this.get_items();
 		},
 
diff --git a/erpnext/regional/report/gstr_1/gstr_1.js b/erpnext/regional/report/gstr_1/gstr_1.js
index ce55921..1a7ff2b 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.js
+++ b/erpnext/regional/report/gstr_1/gstr_1.js
@@ -55,14 +55,25 @@
 		report.page.add_inner_button(__("Download as JSON"), function () {
 			var filters = report.get_values();
 
-			const args = {
-				cmd: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
-				data: report.data,
-				report_name: report.report_name,
-				filters: filters
-			};
-
-			open_url_post(frappe.request.url, args);
+			frappe.call({
+				method: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
+				args: {
+					data: report.data,
+					report_name: report.report_name,
+					filters: filters
+				},
+				callback: function(r) {
+					if (r.message) {
+						const args = {
+							cmd: 'erpnext.regional.report.gstr_1.gstr_1.download_json_file',
+							data: r.message.data,
+							report_name: r.message.report_name,
+							report_type: r.message.report_type
+						};
+						open_url_post(frappe.request.url, args);
+					}
+				}
+			});
 		});
 	}
 }
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 090616b..4f9cc7f 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -532,16 +532,9 @@
 		self.columns = self.invoice_columns + self.tax_columns + self.other_columns
 
 @frappe.whitelist()
-def get_json():
-	data = frappe._dict(frappe.local.form_dict)
-
-	del data["cmd"]
-	if "csrf_token" in data:
-		del data["csrf_token"]
-
-	filters = json.loads(data["filters"])
-	report_data = json.loads(data["data"])
-	report_name = data["report_name"]
+def get_json(filters, report_name, data):
+	filters = json.loads(filters)
+	report_data = json.loads(data)
 	gstin = get_company_gstin_number(filters["company"])
 
 	fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
@@ -575,7 +568,11 @@
 		out = get_export_json(res)
 		gst_json["exp"] = out
 
-	download_json_file(report_name, filters["type_of_business"], gst_json)
+	return {
+		'report_name': report_name,
+		'report_type': filters['type_of_business'],
+		'data': gst_json
+	}
 
 def get_b2b_json(res, gstin):
 	inv_type, out = {"Registered Regular": "R", "Deemed Export": "DE", "URD": "URD", "SEZ": "SEZ"}, []
@@ -722,11 +719,15 @@
 	if gstin:
 		return gstin[0]["gstin"]
 	else:
-		frappe.throw(_("Please set valid GSTIN No. in Company Address"))
+		frappe.throw(_("Please set valid GSTIN No. in Company Address for company {0}".format(
+			frappe.bold(company)
+		)))
 
-def download_json_file(filename, report_type, data):
+@frappe.whitelist()
+def download_json_file():
 	''' download json content in a file '''
-	frappe.response['filename'] = frappe.scrub("{0} {1}".format(filename, report_type)) + '.json'
-	frappe.response['filecontent'] = json.dumps(data)
+	data = frappe._dict(frappe.local.form_dict)
+	frappe.response['filename'] = frappe.scrub("{0} {1}".format(data['report_name'], data['report_type'])) + '.json'
+	frappe.response['filecontent'] = data['data']
 	frappe.response['content_type'] = 'application/json'
 	frappe.response['type'] = 'download'
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 7dc58b5..2dae0d8 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -112,7 +112,6 @@
 		let allow_delivery = false;
 
 		if (doc.docstatus==1) {
-			this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
 
 			if(this.frm.has_perm("submit")) {
 				if(doc.status === 'On Hold') {
@@ -136,7 +135,7 @@
 			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")) {
@@ -148,6 +147,8 @@
 						}
 					}
 
+					this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
+
 					// delivery note
 					if(flt(doc.per_delivered, 6) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
 						this.frm.add_custom_button(__('Delivery Note'), () => this.make_delivery_note_based_on_delivery_date(), __('Create'));
@@ -361,7 +362,7 @@
 	},
 
 	toggle_delivery_date: function() {
-		this.frm.fields_dict.items.grid.toggle_reqd("delivery_date", 
+		this.frm.fields_dict.items.grid.toggle_reqd("delivery_date",
 			(this.frm.doc.order_type == "Sales" && !this.frm.doc.skip_delivery_note));
 	},
 
diff --git a/erpnext/selling/setup_wizard_slide/add_a_few_customers/add_a_few_customers.json b/erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json
similarity index 77%
rename from erpnext/selling/setup_wizard_slide/add_a_few_customers/add_a_few_customers.json
rename to erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json
index a0bb6fe..f39fea4 100644
--- a/erpnext/selling/setup_wizard_slide/add_a_few_customers/add_a_few_customers.json
+++ b/erpnext/selling/onboarding_slide/add_a_few_customers/add_a_few_customers.json
@@ -3,18 +3,18 @@
  "app": "ERPNext",
  "creation": "2019-11-15 14:44:10.065014",
  "docstatus": 0,
- "doctype": "Setup Wizard Slide",
+ "doctype": "Onboarding Slide",
  "domains": [],
  "help_links": [
   {
-   "label": "Customers",
+   "label": "Learn More",
    "video_id": "zsrrVDk6VBs"
   }
  ],
  "idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/customer.png",
+ "image_src": "/assets/erpnext/images/illustrations/customers-onboard.png",
  "max_count": 3,
- "modified": "2019-11-26 18:26:15.888794",
+ "modified": "2019-12-03 22:54:28.959549",
  "modified_by": "Administrator",
  "name": "Add A Few Customers",
  "owner": "Administrator",
@@ -44,6 +44,5 @@
  ],
  "slide_order": 40,
  "slide_title": "Add A Few Customers",
- "slide_type": "Create",
- "submit_method": ""
+ "slide_type": "Create"
 }
\ No newline at end of file
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index 0bcddc2..4d2d540 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -283,7 +283,7 @@
 						card.value = card.value *-1
 					card.value = self.fmt_money(card.value,False if key in ("bank_balance", "credit_balance") else True)
 
-					cache.setex(cache_key, card, 24 * 60 * 60)
+					cache.set_value(cache_key, card, expires_in_sec=24 * 60 * 60)
 
 				context.cards.append(card)
 
diff --git "a/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json" "b/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json"
new file mode 100644
index 0000000..bf330d0
--- /dev/null
+++ "b/erpnext/setup/onboarding_slide/welcome_back_to_erpnext\041/welcome_back_to_erpnext\041.json"
@@ -0,0 +1,23 @@
+{
+ "add_more_button": 0,
+ "app": "ERPNext",
+ "creation": "2019-12-04 19:21:39.995776",
+ "docstatus": 0,
+ "doctype": "Onboarding Slide",
+ "domains": [],
+ "help_links": [],
+ "idx": 0,
+ "image_src": "/assets/erpnext/images/illustrations/desk-onboard.png",
+ "is_completed": 0,
+ "max_count": 3,
+ "modified": "2019-12-04 19:21:39.995776",
+ "modified_by": "Administrator",
+ "name": "Welcome back to ERPNext!",
+ "owner": "Administrator",
+ "slide_desc": "<p>Let's continue where you left from!</p>",
+ "slide_fields": [],
+ "slide_module": "Setup",
+ "slide_order": 0,
+ "slide_title": "Welcome back to ERPNext!",
+ "slide_type": "Continue"
+}
\ No newline at end of file
diff --git "a/erpnext/setup/setup_wizard_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json" "b/erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
similarity index 60%
rename from "erpnext/setup/setup_wizard_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
rename to "erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
index 1da9dd4..4ea6985 100644
--- "a/erpnext/setup/setup_wizard_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
+++ "b/erpnext/setup/onboarding_slide/welcome_to_erpnext\041/welcome_to_erpnext\041.json"
@@ -3,20 +3,20 @@
  "app": "ERPNext",
  "creation": "2019-11-26 17:01:26.671859",
  "docstatus": 0,
- "doctype": "Setup Wizard Slide",
+ "doctype": "Onboarding Slide",
  "domains": [],
  "help_links": [],
  "idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/onboard.png",
+ "image_src": "/assets/erpnext/images/illustrations/desk-onboard.png",
  "max_count": 0,
- "modified": "2019-11-26 17:17:29.813299",
+ "modified": "2019-12-03 22:49:12.871260",
  "modified_by": "Administrator",
  "name": "Welcome to ERPNext!",
  "owner": "Administrator",
- "slide_desc": "Setting up an ERP can be overwhelming. But don't worry, we have got your back!<br>\nLet's setup your company.\nThis wizard will help you onboard to ERPNext in a short time!",
+ "slide_desc": "Setting up an ERP can be overwhelming. But don't worry, we have got your back!\nLet's setup your company.\nThis wizard will help you onboard to ERPNext in a short time!",
  "slide_fields": [],
  "slide_module": "Setup",
- "slide_order": 10,
+ "slide_order": 1,
  "slide_title": "Welcome to ERPNext!",
  "slide_type": "Information"
 }
\ No newline at end of file
diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py
index d1c206d..1a86b79 100644
--- a/erpnext/setup/utils.py
+++ b/erpnext/setup/utils.py
@@ -106,7 +106,8 @@
 			# expire in 6 hours
 			response.raise_for_status()
 			value = response.json()["rates"][to_currency]
-			cache.setex(key, value, 6 * 60 * 60)
+
+			cache.set_value(key, value, expires_in_sec=6 * 60 * 60)
 		return flt(value)
 	except:
 		frappe.log_error(title="Get Exchange Rate")
diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py
index 831381c..5341f29 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -65,7 +65,7 @@
 	bin = get_bin_qty(packing_item_code, pi.warehouse)
 	pi.actual_qty = flt(bin.get("actual_qty"))
 	pi.projected_qty = flt(bin.get("projected_qty"))
-	if old_packed_items_map:
+	if old_packed_items_map and old_packed_items_map.get((packing_item_code, main_item_row.item_code)):
 		pi.batch_no = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].batch_no
 		pi.serial_no = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].serial_no
 		pi.warehouse = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].warehouse
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index d9c94fc..4770471 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -249,6 +249,8 @@
 			}, __("Get items from"));
 		}
 
+		frm.events.show_bom_custom_button(frm);
+
 		if (frm.doc.company) {
 			frm.trigger("toggle_display_account_head");
 		}
@@ -262,6 +264,11 @@
 		frm.trigger("setup_quality_inspection");
 	},
 
+	stock_entry_type: function(frm){
+		frm.remove_custom_button('Bill of Materials', "Get items from");
+		frm.events.show_bom_custom_button(frm);
+	},
+
 	purpose: function(frm) {
 		frm.trigger('validate_purpose_consumption');
 		frm.fields_dict.items.grid.refresh();
@@ -398,6 +405,85 @@
 		}
 	},
 
+	show_bom_custom_button: function(frm){
+		if (frm.doc.docstatus === 0 &&
+			['Material Issue', 'Material Receipt', 'Material Transfer', 'Send to Subcontractor'].includes(frm.doc.purpose)) {
+				frm.add_custom_button(__('Bill of Materials'), function() {
+					frm.events.get_items_from_bom(frm);
+				}, __("Get items from"));
+		}
+	},
+
+	get_items_from_bom: function(frm) {
+		let filters = function(){
+			return {filters: { docstatus:1 }};
+		}
+
+		let fields = [
+			{"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"),
+			options:"BOM", reqd: 1, get_query: filters()},
+			{"fieldname":"source_warehouse", "fieldtype":"Link", "label":__("Source Warehouse"),
+			options:"Warehouse"},
+			{"fieldname":"target_warehouse", "fieldtype":"Link", "label":__("Target Warehouse"),
+			options:"Warehouse"},
+			{"fieldname":"qty", "fieldtype":"Float", "label":__("Quantity"),
+			reqd: 1, "default": 1},
+			{"fieldname":"fetch_exploded", "fieldtype":"Check",
+			"label":__("Fetch exploded BOM (including sub-assemblies)"), "default":1},
+			{"fieldname":"fetch", "label":__("Get Items from BOM"), "fieldtype":"Button"}
+		]
+
+		// Exclude field 'Target Warehouse' in case of Material Issue
+		if (frm.doc.purpose == 'Material Issue'){
+			fields.splice(2,1);
+		}
+		// Exclude field 'Source Warehouse' in case of Material Receipt
+		else if(frm.doc.purpose == 'Material Receipt'){
+			fields.splice(1,1);
+		}
+
+		let d = new frappe.ui.Dialog({
+			title: __("Get Items from BOM"),
+			fields: fields
+		});
+		d.get_input("fetch").on("click", function() {
+			let values = d.get_values();
+			if(!values) return;
+			values["company"] = frm.doc.company;
+			if(!frm.doc.company) frappe.throw(__("Company field is required"));
+			frappe.call({
+				method: "erpnext.manufacturing.doctype.bom.bom.get_bom_items",
+				args: values,
+				callback: function(r) {
+					if (!r.message) {
+						frappe.throw(__("BOM does not contain any stock item"));
+					} else {
+						erpnext.utils.remove_empty_first_row(frm, "items");
+						$.each(r.message, function(i, item) {
+							let d = frappe.model.add_child(cur_frm.doc, "Stock Entry Detail", "items");
+							d.item_code = item.item_code;
+							d.item_name = item.item_name;
+							d.item_group = item.item_group;
+							d.s_warehouse = values.source_warehouse;
+							d.t_warehouse = values.target_warehouse;
+							d.uom = item.stock_uom;
+							d.stock_uom = item.stock_uom;
+							d.conversion_factor = item.conversion_factor ? item.conversion_factor : 1;
+							d.qty = item.qty;
+							d.expense_account = item.expense_account;
+							d.project = item.project;
+							frm.events.set_basic_rate(frm, d.doctype, d.name);
+						});
+					}
+					d.hide();
+					refresh_field("items");
+				}
+			});
+
+		});
+		d.show();
+	},
+
 	calculate_basic_amount: function(frm, item) {
 		item.basic_amount = flt(flt(item.transfer_qty) * flt(item.basic_rate),
 			precision("basic_amount", item));
diff --git a/erpnext/stock/setup_wizard_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json b/erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
similarity index 76%
rename from erpnext/stock/setup_wizard_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
rename to erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
index c536f7b..27a3062 100644
--- a/erpnext/stock/setup_wizard_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
+++ b/erpnext/stock/onboarding_slide/add_a_few_products_you_buy_or_sell/add_a_few_products_you_buy_or_sell.json
@@ -3,13 +3,13 @@
  "app": "ERPNext",
  "creation": "2019-11-15 14:41:12.007359",
  "docstatus": 0,
- "doctype": "Setup Wizard Slide",
+ "doctype": "Onboarding Slide",
  "domains": [],
  "help_links": [],
  "idx": 0,
- "image_src": "/assets/erpnext/images/illustrations/product.png",
+ "image_src": "/assets/erpnext/images/illustrations/products-onboard.png",
  "max_count": 3,
- "modified": "2019-11-26 18:26:35.305755",
+ "modified": "2019-12-03 22:54:07.558632",
  "modified_by": "Administrator",
  "name": "Add A Few Products You Buy Or Sell",
  "owner": "Administrator",
@@ -26,15 +26,9 @@
   },
   {
    "align": "",
-   "fieldtype": "Column Break",
-   "reqd": 1
-  },
-  {
-   "align": "",
-   "fieldname": "uom",
-   "fieldtype": "Link",
-   "label": "UOM",
-   "options": "UOM",
+   "fieldname": "item_price",
+   "fieldtype": "Currency",
+   "label": "Item Price",
    "reqd": 1
   },
   {
@@ -44,14 +38,14 @@
   },
   {
    "align": "",
-   "fieldname": "item_price",
-   "fieldtype": "Currency",
-   "label": "Item Price",
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "options": "UOM",
    "reqd": 1
   }
  ],
  "slide_order": 30,
  "slide_title": "Add A Few Products You Buy Or Sell",
- "slide_type": "Create",
- "submit_method": ""
+ "slide_type": "Create"
 }
\ No newline at end of file