Merge pull request #17415 from deepeshgarg007/notification

feat: Track purchase receipt status in material request
diff --git a/erpnext/buying/report/procurement_tracker/__init__.py b/erpnext/buying/report/procurement_tracker/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/report/procurement_tracker/__init__.py
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.js b/erpnext/buying/report/procurement_tracker/procurement_tracker.js
new file mode 100644
index 0000000..283d56c
--- /dev/null
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.js
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Procurement Tracker"] = {
+	"filters": [
+		{
+			fieldname: "company",
+			label: __("Company"),
+			fieldtype: "Link",
+			options: "Company",
+			default: frappe.defaults.get_user_default("Company"),
+		},
+		{
+			fieldname: "cost_center",
+			label: __("Cost Center"),
+			fieldtype: "Link",
+			options: "Cost Center",
+		},
+		{
+			fieldname: "project",
+			label: __("Project"),
+			fieldtype: "Link",
+			options: "Project",
+		},
+		{
+			fieldname: "from_date",
+			label: __("From Date"),
+			fieldtype: "Date",
+			default: frappe.defaults.get_user_default("year_start_date"),
+		},
+		{
+			fieldname:"to_date",
+			label: __("To Date"),
+			fieldtype: "Date",
+			default: frappe.defaults.get_user_default("year_end_date"),
+		},
+	]
+}
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.json b/erpnext/buying/report/procurement_tracker/procurement_tracker.json
new file mode 100644
index 0000000..028736c
--- /dev/null
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.json
@@ -0,0 +1,20 @@
+{
+ "add_total_row": 1,
+ "creation": "2019-03-29 17:05:45.196949",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2019-03-29 17:18:06.678728",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Procurement Tracker",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Purchase Order",
+ "report_name": "Procurement Tracker",
+ "report_type": "Script Report",
+ "roles": []
+}
\ No newline at end of file
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
new file mode 100644
index 0000000..d3ee447
--- /dev/null
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
@@ -0,0 +1,282 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+
+def execute(filters=None):
+	columns = get_columns(filters)
+	data = get_data(filters)
+	return columns, data
+
+def get_columns(filters):
+	columns = [
+		{
+			"label": _("Material Request Date"),
+			"fieldname": "material_request_date",
+			"fieldtype": "Date",
+			"width": 140
+		},
+		{
+			"label": _("Material Request No"),
+			"options": "Material Request",
+			"fieldname": "material_request_no",
+			"fieldtype": "Link",
+			"width": 140
+		},
+		{
+			"label": _("Cost Center"),
+			"options": "Cost Center",
+			"fieldname": "cost_center",
+			"fieldtype": "Link",
+			"width": 140
+		},
+		{
+			"label": _("Project"),
+			"options": "Project",
+			"fieldname": "project",
+			"fieldtype": "Link",
+			"width": 140
+		},
+		{
+			"label": _("Requesting Site"),
+			"options": "Warehouse",
+			"fieldname": "requesting_site",
+			"fieldtype": "Link",
+			"width": 140
+		},
+		{
+			"label": _("Requestor"),
+			"options": "Employee",
+			"fieldname": "requestor",
+			"fieldtype": "Link",
+			"width": 140
+		},
+		{
+			"label": _("Description"),
+			"fieldname": "description",
+			"fieldtype": "Data",
+			"width": 200
+		},
+		{
+			"label": _("Quantity"),
+			"fieldname": "quantity",
+			"fieldtype": "Int",
+			"width": 140
+		},
+		{
+			"label": _("Unit of Measure"),
+			"options": "UOM",
+			"fieldname": "unit_of_measurement",
+			"fieldtype": "Link",
+			"width": 140
+		},
+		{
+			"label": _("Status"),
+			"fieldname": "status",
+			"fieldtype": "data",
+			"width": 140
+		},
+		{
+			"label": _("Purchase Order Date"),
+			"fieldname": "purchase_order_date",
+			"fieldtype": "Date",
+			"width": 140
+		},
+		{
+			"label": _("Purchase Order"),
+			"options": "Purchase Order",
+			"fieldname": "purchase_order",
+			"fieldtype": "Link",
+			"width": 140
+		},
+		{
+			"label": _("Supplier"),
+			"options": "Supplier",
+			"fieldname": "supplier",
+			"fieldtype": "Link",
+			"width": 140
+		},
+		{
+			"label": _("Estimated Cost"),
+			"fieldname": "estimated_cost",
+			"fieldtype": "Float",
+			"width": 140
+		},
+		{
+			"label": _("Actual Cost"),
+			"fieldname": "actual_cost",
+			"fieldtype": "Float",
+			"width": 140
+		},
+		{
+			"label": _("Purchase Order Amount"),
+			"fieldname": "purchase_order_amt",
+			"fieldtype": "Float",
+			"width": 140
+		},
+		{
+			"label": _("Purchase Order Amount(Company Currency)"),
+			"fieldname": "purchase_order_amt_usd",
+			"fieldtype": "Float",
+			"width": 140
+		},
+		{
+			"label": _("Expected Delivery Date"),
+			"fieldname": "expected_delivery_date",
+			"fieldtype": "Date",
+			"width": 140
+		},
+		{
+			"label": _("Actual Delivery Date"),
+			"fieldname": "actual_delivery_date",
+			"fieldtype": "Date",
+			"width": 140
+		},
+	]
+	return columns
+
+def get_conditions(filters):
+	conditions = ""
+
+	if filters.get("company"):
+		conditions += " AND company='%s'"% filters.get('company')
+
+	if filters.get("cost_center") or filters.get("project"):
+		conditions += """
+			AND (cost_center='%s'
+			OR project='%s')
+			"""% (filters.get('cost_center'), filters.get('project'))
+
+	if filters.get("from_date"):
+		conditions += "AND transaction_date>=%s"% filters.get('from_date')
+
+	if filters.get("to_date"):
+		conditions += "AND transaction_date<=%s"% filters.get('to_date')
+	return conditions
+
+def get_data(filters):
+	conditions = get_conditions(filters)
+	purchase_order_entry = get_po_entries(conditions)
+	mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
+	pr_records = get_mapped_pr_records()
+	pi_records = get_mapped_pi_records()
+	print(pi_records)
+
+	procurement_record=[]
+	if procurement_record_against_mr:
+		procurement_record += procurement_record_against_mr
+	for po in purchase_order_entry:
+		# fetch material records linked to the purchase order item
+		mr_record = mr_records.get(po.material_request_item, [{}])[0]
+		procurement_detail = {
+			"material_request_date": mr_record.get('transaction_date'),
+			"cost_center": po.cost_center,
+			"project": po.project,
+			"requesting_site": po.warehouse,
+			"requestor": po.owner,
+			"material_request_no": po.material_request,
+			"description": po.description,
+			"quantity": po.qty,
+			"unit_of_measurement": po.stock_uom,
+			"status": po.status,
+			"purchase_order_date": po.transaction_date,
+			"purchase_order": po.parent,
+			"supplier": po.supplier,
+			"estimated_cost": mr_record.get('amount'),
+			"actual_cost": pi_records.get(po.name),
+			"purchase_order_amt": po.amount,
+			"purchase_order_amt_in_company_currency": po.base_amount,
+			"expected_delivery_date": po.schedule_date,
+			"actual_delivery_date": pr_records.get(po.name)
+		}
+		procurement_record.append(procurement_detail)
+	return procurement_record
+
+def get_mapped_mr_details(conditions):
+	mr_records = {}
+	mr_details = frappe.db.sql("""
+		SELECT
+			mr.transaction_date,
+			mr.per_ordered,
+			mr_item.name,
+			mr_item.parent,
+			mr_item.amount
+		FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
+		WHERE
+			mr.per_ordered>=0
+			AND mr.name=mr_item.parent
+			AND mr.docstatus=1
+			{conditions}
+		""".format(conditions=conditions), as_dict=1) #nosec
+
+	procurement_record_against_mr = []
+	for record in mr_details:
+		if record.per_ordered:
+			mr_records.setdefault(record.name, []).append(frappe._dict(record))
+		else:
+			procurement_record_details = dict(
+				material_request_date=record.transaction_date,
+				material_request_no=record.parent,
+				estimated_cost=record.amount
+			)
+			procurement_record_against_mr.append(procurement_record_details)
+	return mr_records, procurement_record_against_mr
+
+def get_mapped_pi_records():
+	return frappe._dict(frappe.db.sql("""
+		SELECT
+			pi_item.po_detail,
+			pi_item.base_amount
+		FROM `tabPurchase Invoice Item` as pi_item
+		INNER JOIN `tabPurchase Order` as po
+		ON pi_item.`purchase_order` = po.`name`
+		WHERE
+			pi_item.docstatus = 1
+			AND po.status not in ("Closed","Completed","Cancelled")
+			AND pi_item.po_detail IS NOT NULL
+		"""))
+
+def get_mapped_pr_records():
+	return frappe._dict(frappe.db.sql("""
+		SELECT
+			pr_item.purchase_order_item,
+			pr.posting_date
+		FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
+		WHERE
+			pr.docstatus=1
+			AND pr.name=pr_item.parent
+			AND pr_item.purchase_order_item IS NOT NULL
+			AND pr.status not in  ("Closed","Completed","Cancelled")
+		"""))
+
+def get_po_entries(conditions):
+	return frappe.db.sql("""
+		SELECT
+			po_item.name,
+			po_item.parent,
+			po_item.cost_center,
+			po_item.project,
+			po_item.warehouse,
+			po_item.material_request,
+			po_item.material_request_item,
+			po_item.description,
+			po_item.stock_uom,
+			po_item.qty,
+			po_item.amount,
+			po_item.base_amount,
+			po_item.schedule_date,
+			po.transaction_date,
+			po.supplier,
+			po.status,
+			po.owner
+		FROM `tabPurchase Order` po, `tabPurchase Order Item` po_item
+		WHERE
+			po.docstatus = 1
+			AND po.name = po_item.parent
+			AND po.status not in  ("Closed","Completed","Cancelled")
+			{conditions}
+		GROUP BY
+			po.name,po_item.item_code
+		""".format(conditions=conditions), as_dict=1) #nosec
\ No newline at end of file
diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
new file mode 100644
index 0000000..4a13f38
--- /dev/null
+++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
@@ -0,0 +1,67 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import unittest
+from datetime import datetime
+import frappe
+from erpnext.buying.report.procurement_tracker.procurement_tracker import execute
+from erpnext.stock.doctype.material_request.test_material_request import make_material_request
+from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+class TestProcurementTracker(unittest.TestCase):
+	def test_result_for_procurement_tracker(self):
+		filters = {
+			'company': '_Test Procurement Company',
+			'cost_center': '_Test Cost Center - _TC'
+		}
+		expected_data = self.generate_expected_data()
+		report = execute(filters)
+
+		length = len(report[1])
+		self.assertEqual(expected_data, report[1][length-1])
+
+	def generate_expected_data(self):
+		if not frappe.db.exists("Company", "_Test Procurement Company"):
+			frappe.get_doc(dict(
+				doctype="Company",
+				company_name="_Test Procurement Company",
+				abbr="_TPC",
+				default_currency="INR",
+				country="India"
+				)).insert()
+		warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company")
+		mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse)
+		po = make_purchase_order(mr.name)
+		po.supplier = "_Test Supplier"
+		po.get("items")[0].cost_center = "_Test Cost Center - _TC"
+		po.submit()
+		pr = make_purchase_receipt(po.name)
+		pr.submit()
+		frappe.db.commit()
+		date_obj = datetime.date(datetime.now())
+
+		expected_data = {
+			"material_request_date": date_obj,
+			"cost_center": "_Test Cost Center - _TC",
+			"project": None,
+			"requesting_site": "_Test Procurement Warehouse - _TPC",
+			"requestor": "Administrator",
+			"material_request_no": mr.name,
+			"description": '_Test Item 1',
+			"quantity": 10.0,
+			"unit_of_measurement": "_Test UOM",
+			"status": "To Bill",
+			"purchase_order_date": date_obj,
+			"purchase_order": po.name,
+			"supplier": "_Test Supplier",
+			"estimated_cost": 0.0,
+			"actual_cost": None,
+			"purchase_order_amt": 5000.0,
+			"purchase_order_amt_in_company_currency": 300000.0,
+			"expected_delivery_date": date_obj,
+			"actual_delivery_date": date_obj
+		}
+		return expected_data
\ No newline at end of file
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js
index 6ab523e..58ce422 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.js
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.js
@@ -4,5 +4,18 @@
 frappe.ui.form.on('HR Settings', {
 	refresh: function(frm) {
 
+	},
+
+	encrypt_salary_slips_in_emails: function(frm) {
+		let encrypt_state = frm.doc.encrypt_salary_slips_in_emails;
+		frm.set_df_property('password_policy', 'reqd', encrypt_state);
+	},
+
+	validate: function(frm) {
+		let policy = frm.doc.password_policy;
+		if (policy.includes(' ') || policy.includes('--')) {
+			frappe.msgprint("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically");
+		}
+		frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-'));
 	}
 });
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 225785d..5502ce8 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -1,5 +1,6 @@
 {
  "allow_copy": 0, 
+ "allow_events_in_timeline": 0, 
  "allow_guest_to_view": 0, 
  "allow_import": 0, 
  "allow_rename": 0, 
@@ -13,10 +14,12 @@
  "fields": [
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "employee_settings", 
    "fieldtype": "Section Break", 
    "hidden": 0, 
@@ -43,12 +46,14 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
    "default": "", 
    "description": "Enter retirement age in years", 
+   "fetch_if_empty": 0, 
    "fieldname": "retirement_age", 
    "fieldtype": "Data", 
    "hidden": 0, 
@@ -76,12 +81,14 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
    "default": "Naming Series", 
    "description": "Employee record is created using selected field. ", 
+   "fetch_if_empty": 0, 
    "fieldname": "emp_created_by", 
    "fieldtype": "Select", 
    "hidden": 0, 
@@ -109,11 +116,13 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
    "default": "", 
+   "fetch_if_empty": 0, 
    "fieldname": "leave_approval_notification_template", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -142,10 +151,12 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "leave_status_notification_template", 
    "fieldtype": "Link", 
    "hidden": 0, 
@@ -174,10 +185,12 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "column_break_4", 
    "fieldtype": "Column Break", 
    "hidden": 0, 
@@ -204,11 +217,13 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
    "description": "Don't send Employee Birthday Reminders", 
+   "fetch_if_empty": 0, 
    "fieldname": "stop_birthday_reminders", 
    "fieldtype": "Check", 
    "hidden": 0, 
@@ -235,10 +250,12 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "maintain_bill_work_hours_same", 
    "fieldtype": "Check", 
    "hidden": 0, 
@@ -266,11 +283,13 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
    "default": "1", 
+   "fetch_if_empty": 0, 
    "fieldname": "leave_approver_mandatory_in_leave_application", 
    "fieldtype": "Check", 
    "hidden": 0, 
@@ -298,11 +317,13 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
    "default": "1", 
+   "fetch_if_empty": 0, 
    "fieldname": "expense_approver_mandatory_in_expense_claim", 
    "fieldtype": "Check", 
    "hidden": 0, 
@@ -330,10 +351,13 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "depends_on": "", 
+   "fetch_if_empty": 0, 
    "fieldname": "payroll_settings", 
    "fieldtype": "Section Break", 
    "hidden": 0, 
@@ -360,11 +384,13 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
    "description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day", 
+   "fetch_if_empty": 0, 
    "fieldname": "include_holidays_in_total_working_days", 
    "fieldtype": "Check", 
    "hidden": 0, 
@@ -391,12 +417,14 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
    "default": "1", 
    "description": "Emails salary slip to employee based on preferred email selected in Employee", 
+   "fetch_if_empty": 0, 
    "fieldname": "email_salary_slip_to_employee", 
    "fieldtype": "Check", 
    "hidden": 0, 
@@ -424,10 +452,82 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "depends_on": "eval: doc.email_salary_slip_to_employee == 1;", 
+   "description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.", 
+   "fetch_if_empty": 0, 
+   "fieldname": "encrypt_salary_slips_in_emails", 
+   "fieldtype": "Check", 
+   "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": "Encrypt Salary Slips in Emails", 
+   "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, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1", 
+   "description": "<b>Example:</b> SAL-{first_name}-{date_of_birth.year} <br>This will generate a password like SAL-Jane-1972", 
+   "fetch_if_empty": 0, 
+   "fieldname": "password_policy", 
+   "fieldtype": "Data", 
+   "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": "Password Policy", 
+   "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, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "max_working_hours_against_timesheet", 
    "fieldtype": "Float", 
    "hidden": 0, 
@@ -455,10 +555,12 @@
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "leave_settings", 
    "fieldtype": "Section Break", 
    "hidden": 0, 
@@ -481,14 +583,17 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }, 
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
    "columns": 0, 
+   "fetch_if_empty": 0, 
    "fieldname": "show_leaves_of_all_department_members_in_calendar", 
    "fieldtype": "Check", 
    "hidden": 0, 
@@ -511,22 +616,21 @@
    "reqd": 0, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }
  ], 
  "has_web_view": 0, 
- "hide_heading": 0, 
  "hide_toolbar": 0, 
  "icon": "fa fa-cog", 
  "idx": 1, 
- "image_view": 0, 
  "in_create": 0, 
  "is_submittable": 0, 
  "issingle": 1, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-05-03 15:36:13.015466", 
- "modified_by": "Administrator", 
+ "modified": "2019-04-25 15:08:12.983571", 
+ "modified_by": "shivam@example.com", 
  "module": "HR", 
  "name": "HR Settings", 
  "owner": "Administrator", 
@@ -553,9 +657,9 @@
  ], 
  "quick_entry": 0, 
  "read_only": 0, 
- "read_only_onload": 0, 
  "show_name_in_global_search": 0, 
  "sort_order": "ASC", 
  "track_changes": 0, 
- "track_seen": 0
+ "track_seen": 0, 
+ "track_views": 0
 }
\ No newline at end of file
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.py b/erpnext/hr/doctype/hr_settings/hr_settings.py
index 964eaee..78095b3 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.py
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.py
@@ -5,11 +5,21 @@
 
 from __future__ import unicode_literals
 import frappe
+from frappe import _
 
 from frappe.model.document import Document
 
 class HRSettings(Document):
 	def validate(self):
+		self.set_naming_series()
+		self.validate_password_policy()
+
+	def set_naming_series(self):
 		from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
 		set_by_naming_series("Employee", "employee_number",
 			self.get("emp_created_by")=="Naming Series", hide_name_field=True)
+
+	def validate_password_policy(self):
+		if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails:
+			if not self.password_policy:
+				frappe.throw(_("Password policy for Salary Slips is not set"))
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index fbfa3af..5f6ddfc 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -520,13 +520,20 @@
 
 	def email_salary_slip(self):
 		receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")
+		hr_settings = frappe.get_single("HR Settings")
+		message = "Please see attachment"
+		password = None
+		if hr_settings.encrypt_salary_slips_in_emails:
+			password = generate_password_for_pdf(hr_settings.password_policy, self.employee)
+			message += """<br>Note: Your salary slip is password protected,
+				the password to unlock the PDF is of the format {0}. """.format(hr_settings.password_policy)
 
 		if receiver:
 			email_args = {
 				"recipients": [receiver],
-				"message": _("Please see attachment"),
+				"message": _(message),
 				"subject": 'Salary Slip - from {0} to {1}'.format(self.start_date, self.end_date),
-				"attachments": [frappe.attach_print(self.doctype, self.name, file_name=self.name)],
+				"attachments": [frappe.attach_print(self.doctype, self.name, file_name=self.name, password=password)],
 				"reference_doctype": self.doctype,
 				"reference_name": self.name
 				}
@@ -843,3 +850,7 @@
 		for ss in linked_ss:
 			ss_doc = frappe.get_doc("Salary Slip", ss)
 			frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "")
+
+def generate_password_for_pdf(policy_template, employee):
+	employee = frappe.get_doc("Employee", employee)
+	return policy_template.format(**employee.as_dict())
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 01270e4..bdc1ed4 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -512,6 +512,7 @@
 erpnext.patches.v11_0.move_leave_approvers_from_employee #13-06-2018
 erpnext.patches.v11_0.update_department_lft_rgt
 erpnext.patches.v11_0.add_default_email_template_for_leave
+execute:frappe.reload_doc("HR", "doctype", "HR Settings")
 erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018
 erpnext.patches.v11_0.uom_conversion_data #30-06-2018
 erpnext.patches.v10_0.taxes_issue_with_pos
diff --git a/erpnext/patches/v11_0/set_default_email_template_in_hr.py b/erpnext/patches/v11_0/set_default_email_template_in_hr.py
index e895eae..14954fb 100644
--- a/erpnext/patches/v11_0/set_default_email_template_in_hr.py
+++ b/erpnext/patches/v11_0/set_default_email_template_in_hr.py
@@ -2,7 +2,6 @@
 import frappe
 
 def execute():
-
 	hr_settings = frappe.get_single("HR Settings")
 	hr_settings.leave_approval_notification_template = "Leave Approval Notification"
 	hr_settings.leave_status_notification_template = "Leave Status Notification"
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 7dc54d0..79cdc1a 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -629,7 +629,8 @@
 		"item_code": args.item_code or "_Test Item",
 		"qty": args.qty or 10,
 		"schedule_date": args.schedule_date or today(),
-		"warehouse": args.warehouse or "_Test Warehouse - _TC"
+		"warehouse": args.warehouse or "_Test Warehouse - _TC",
+		"cost_center": args.cost_center or "_Test Cost Center - _TC"
 	})
 	mr.insert()
 	if not args.do_not_submit:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index f8d91a7..d106ed1 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -129,6 +129,10 @@
 		if self.purpose not in valid_purposes:
 			frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
 
+		if self.job_card and self.purpose != 'Material Transfer for Manufacture':
+			frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry")
+				.format(self.job_card))
+
 	def set_transfer_qty(self):
 		for item in self.get("items"):
 			if not flt(item.qty):