Merge pull request #17556 from Alchez/develop-lead-import-fix

feat(crm): Allow leads to be imported without person name
diff --git a/erpnext/accounts/doctype/account/account.js b/erpnext/accounts/doctype/account/account.js
index bb059f6..f7f1a5f 100644
--- a/erpnext/accounts/doctype/account/account.js
+++ b/erpnext/accounts/doctype/account/account.js
@@ -42,15 +42,14 @@
 				// show / hide convert buttons
 				frm.trigger('add_toolbar_buttons');
 			}
-			frm.add_custom_button(__('Update Account Name / Number'), function () {
-				frm.trigger("update_account_number");
-			});
-		}
-
-		if(!frm.doc.__islocal) {
-			frm.add_custom_button(__('Merge Account'), function () {
-				frm.trigger("merge_account");
-			});
+			if (frm.has_perm('write')) {
+				frm.add_custom_button(__('Update Account Name / Number'), function () {
+					frm.trigger("update_account_number");
+				});
+				frm.add_custom_button(__('Merge Account'), function () {
+					frm.trigger("merge_account");
+				});
+			}
 		}
 	},
 	account_type: function (frm) {
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index ecf67dd..68efe37 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -268,7 +268,7 @@
 
 	new_name = get_account_autoname(account_number, account_name, account.company)
 	if name != new_name:
-		frappe.rename_doc("Account", name, new_name, ignore_permissions=1)
+		frappe.rename_doc("Account", name, new_name, force=1)
 		return new_name
 
 @frappe.whitelist()
@@ -287,7 +287,7 @@
 		frappe.db.set_value("Account", new, "parent_account",
 			frappe.db.get_value("Account", old, "parent_account"))
 
-	frappe.rename_doc("Account", old, new, merge=1, ignore_permissions=1)
+	frappe.rename_doc("Account", old, new, merge=1, force=1)
 
 	return new
 
diff --git a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js
index 36bd29e..29db227 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js
@@ -5,6 +5,12 @@
 frappe.query_reports["Inactive Sales Items"] = {
 	"filters": [
 		{
+			fieldname: "territory",
+			label: __("Territory"),
+			fieldtype: "Link",
+			options: "Territory"
+		},
+		{
 			fieldname: "item",
 			label: __("Item"),
 			fieldtype: "Link",
diff --git a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
index fd169f8f..42761a5 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
@@ -7,13 +7,11 @@
 from frappe import _
 
 def execute(filters=None):
-
 	columns = get_columns()
 	data = get_data(filters)
 	return columns, data
 
 def get_columns():
-
 	columns = [
 		{
 			"fieldname": "territory",
@@ -74,36 +72,37 @@
 
 
 def get_data(filters):
-
 	data = []
 	items = get_items(filters)
+	territories = get_territories(filters)
 	sales_invoice_data = get_sales_details(filters)
 
-	for item in items:
-		row = {
+	for territory in territories:
+		for item in items:
+			row = {
+				"territory": territory.name,
 				"item_group": item.item_group,
 				"item": item.name,
 				"item_name": item.item_name
-		}
+			}
 
-		if sales_invoice_data.get(item.name):
-			item_obj = sales_invoice_data[item.name]
-			if item_obj.days_since_last_order > cint(filters['days']):
-				row.update({
-					"territory": item_obj.territory,
-					"customer": item_obj.customer,
-					"last_order_date": item_obj.last_order_date,
-					"qty": item_obj.qty,
-					"days_since_last_order": item_obj.days_since_last_order
-				})
+			if sales_invoice_data.get((territory.name,item.name)):
+				item_obj = sales_invoice_data[(territory.name,item.name)]
+				if item_obj.days_since_last_order > cint(filters['days']):
+					row.update({
+						"territory": item_obj.territory,
+						"customer": item_obj.customer,
+						"last_order_date": item_obj.last_order_date,
+						"qty": item_obj.qty,
+						"days_since_last_order": item_obj.days_since_last_order
+					})
 
-		data.append(row)
+			data.append(row)
 
 	return data
 
 
 def get_sales_details(filters):
-
 	data = []
 	item_details_map = {}
 
@@ -118,12 +117,21 @@
 		.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
 
 	for d in sales_data:
-		item_details_map.setdefault(d.item_name, d)
+		item_details_map.setdefault((d.territory,d.item_name), d)
 
 	return item_details_map
 
-def get_items(filters):
+def get_territories(filters):
 
+	filter_dict = {}
+	if filters.get("territory"):
+		filter_dict.update({'name': filters['territory']})
+
+	territories = frappe.get_all("Territory", fields=["name"], filters=filter_dict)
+
+	return territories
+
+def get_items(filters):
 	filters_dict = {
 		"disabled": 0,
 		"is_stock_item": 1
diff --git a/erpnext/hr/doctype/training_event/training_event.py b/erpnext/hr/doctype/training_event/training_event.py
index 1b13b70..5064f03 100644
--- a/erpnext/hr/doctype/training_event/training_event.py
+++ b/erpnext/hr/doctype/training_event/training_event.py
@@ -5,9 +5,19 @@
 from __future__ import unicode_literals
 import frappe
 from frappe.model.document import Document
+from frappe import _
+from frappe.utils import time_diff_in_seconds
 from erpnext.hr.doctype.employee.employee import get_employee_emails
 
 class TrainingEvent(Document):
 	def validate(self):
+		self.set_employee_emails()
+		self.validate_period()
+
+	def set_employee_emails(self):
 		self.employee_emails = ', '.join(get_employee_emails([d.employee
 			for d in self.employees]))
+
+	def validate_period(self):
+		if time_diff_in_seconds(self.end_time, self.start_time) <= 0:
+			frappe.throw(_('End time cannot be before start time'))
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 61db3e1..a66fac3 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -485,16 +485,17 @@
 	projects =  get_projects_for_collect_progress("Daily", fields)
 
 	for project in projects:
-		if not check_project_update_exists(project.name, project.get("daily_time_to_send")):
+		if allow_to_make_project_update(project.name, project.get("daily_time_to_send"), "Daily"):
 			send_project_update_email_to_users(project.name)
 
 def twice_daily_reminder():
 	fields = ["first_email", "second_email"]
 	projects =  get_projects_for_collect_progress("Twice Daily", fields)
+	fields.remove("name")
 
 	for project in projects:
 		for d in fields:
-			if not check_project_update_exists(project.name, project.get(d)):
+			if allow_to_make_project_update(project.name, project.get(d), "Twicely"):
 				send_project_update_email_to_users(project.name)
 
 def weekly_reminder():
@@ -506,14 +507,19 @@
 		if current_day != project.day_to_send:
 			continue
 
-		if not check_project_update_exists(project.name, project.get("weekly_time_to_send")):
+		if allow_to_make_project_update(project.name, project.get("weekly_time_to_send"), "Weekly"):
 			send_project_update_email_to_users(project.name)
 
-def check_project_update_exists(project, time):
+def allow_to_make_project_update(project, time, frequency):
 	data = frappe.db.sql(""" SELECT name from `tabProject Update`
-		WHERE project = %s and date = %s and time >= %s """, (project, today(), time))
+		WHERE project = %s and date = %s """, (project, today()))
 
-	return True if data and data[0][0] else False
+	# len(data) > 1 condition is checked for twicely frequency
+	if data and (frequency in ['Daily', 'Weekly'] or len(data) > 1):
+		return False
+
+	if get_time(nowtime()) >= get_time(time):
+		return True
 
 def get_projects_for_collect_progress(frequency, fields):
 	fields.extend(["name"])