feat: sla based on customer/group/territory
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index c72dacf..b272f60 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -264,7 +264,6 @@
 		"erpnext.projects.doctype.project.project.send_project_status_email_to_users",
 		"erpnext.quality_management.doctype.quality_review.quality_review.review",
 		"erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status",
-		"erpnext.support.doctype.issue.issue.set_service_level_agreement_status"
 	],
 	"daily_long": [
 		"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"
diff --git a/erpnext/patches/v12_0/set_priority_for_support.py b/erpnext/patches/v12_0/set_priority_for_support.py
index f04b45a..53aed92 100644
--- a/erpnext/patches/v12_0/set_priority_for_support.py
+++ b/erpnext/patches/v12_0/set_priority_for_support.py
@@ -6,10 +6,11 @@
 	priorities = frappe.get_meta("Issue").get_field("priority").options.split("\n")
 
 	for priority in priorities:
-		frappe.get_doc({
-			"doctype": "Issue Priority",
-			"name": priority
-		}).insert(ignore_permissions=True)
+		if not frappe.db.exists("Issue Priority", priority):
+			frappe.get_doc({
+				"doctype": "Issue Priority",
+				"name": priority
+			}).insert(ignore_permissions=True)
 
 	frappe.reload_doc("support", "doctype", "issue")
 	frappe.reload_doc("support", "doctype", "service_level")
diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py
index 388ddca..f62613e 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.py
+++ b/erpnext/setup/doctype/customer_group/customer_group.py
@@ -8,7 +8,7 @@
 
 from frappe.utils.nestedset import NestedSet
 class CustomerGroup(NestedSet):
-	nsm_parent_field = 'parent_customer_group';
+	nsm_parent_field = 'parent_customer_group'
 
 	def on_update(self):
 		self.validate_name_with_customer()
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index 9ee981d..7c93973 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -1,6 +1,25 @@
 frappe.ui.form.on("Issue", {
 	onload: function(frm) {
 		frm.email_field = "raised_by";
+		if (frm.doc.service_level_agreement) {
+			frappe.call({
+				method: "erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_priorities",
+				args: {
+					name: frm.doc.service_level_agreement,
+				},
+				callback: function (r) {
+					if (r && r.message) {
+						frm.set_query('priority', function() {
+							return {
+								filters: {
+									"name": ["in", r.message],
+								}
+							};
+						});
+					}
+				}
+			});
+		}
 	},
 
 	refresh: function (frm) {
@@ -43,24 +62,6 @@
 				frm.save();
 			});
 		}
-
-		frappe.call({
-			method: "erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_priorities",
-			args: {
-				name: frm.doc.service_level_agreement,
-			},
-			callback: function (r) {
-				if (r && r.message) {
-					frm.set_query('priority', function() {
-						return {
-							filters: {
-								"name": ["in", r.message],
-							}
-						};
-					});
-				}
-			}
-		});
 	},
 
 	priority: function(frm) {
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 734b443..525f829 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -77,11 +77,9 @@
 
 	def update_agreement_status(self):
 		current_time = frappe.flags.current_time or now_datetime()
-		if self.service_level_agreement and self.agreement_fulfilled == "Ongoing":
-			response_time_diff = round(time_diff_in_hours(self.response_by, current_time), 2)
-			resolution_time_diff = round(time_diff_in_hours(self.resolution_by, current_time), 2)
 
-			if response_time_diff < 0 or resolution_time_diff < 0:
+		if self.service_level_agreement and self.agreement_fulfilled == "Ongoing":
+			if self.response_by_variance < 0 or self.resolution_by_variance < 0:
 				self.agreement_fulfilled = "Failed"
 			else:
 				self.agreement_fulfilled = "Fulfilled"
@@ -232,33 +230,27 @@
 
 	return current_date_time
 
-def set_service_level_agreement_status():
-	issues = frappe.get_list("Issue", filters={"status": "Open", "agreement_fulfilled": "Ongoing"})
-	for issue in issues:
-		doc = frappe.get_doc("Issue", issue.name)
-		if doc.service_level_agreement and doc.agreement_fulfilled == "Ongoing":
-			response_time_diff = round(time_diff_in_hours(doc.response_by, now_datetime()), 2)
-			resolution_time_diff = round(time_diff_in_hours(doc.resolution_by, now_datetime()), 2)
-			if response_time_diff < 0 or resolution_time_diff < 0:
-				frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed")
-			else:
-				frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Fulfilled")
-
 def set_service_level_agreement_variance(issue=None):
-	filters = {"status": "Open", "agreement_fulfilled": "Ongoing"}
+	current_time = frappe.flags.current_time or now_datetime()
 
+	filters = {"status": "Open", "agreement_fulfilled": "Ongoing"}
 	if issue:
 		filters = {"name": issue}
 
-	issues = frappe.get_list("Issue", filters=filters)
-	for issue in issues:
+	for issue in frappe.get_list("Issue", filters=filters):
 		doc = frappe.get_doc("Issue", issue.name)
-		if not doc.first_responded_on:
-			variance = round(time_diff_in_hours(doc.response_by, now_datetime()), 2)
+
+		if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer
+			variance = round(time_diff_in_hours(doc.response_by, current_time), 2)
 			frappe.db.set_value("Issue", doc.name, "response_by_variance", variance)
-		if not doc.resolution_date:
-			variance = round(time_diff_in_hours(doc.resolution_by, now_datetime()), 2)
+			if variance < 0:
+				frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed")
+
+		if not doc.resolution_date: # resolution_date set when issue has been closed
+			variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2)
 			frappe.db.set_value("Issue", doc.name, "resolution_by_variance", variance)
+			if variance < 0:
+				frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed")
 
 def get_list_context(context=None):
 	return {
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
index c68d385..5b6b7cc 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
@@ -6,12 +6,15 @@
  "engine": "InnoDB",
  "field_order": [
   "service_level",
-  "customer",
   "default_service_level_agreement",
   "holiday_list",
   "column_break_2",
   "employee_group",
   "default_priority",
+  "apply_to_section",
+  "apply_to",
+  "column_break_10",
+  "entity",
   "agreement_details_section",
   "start_date",
   "active",
@@ -24,16 +27,6 @@
  ],
  "fields": [
   {
-   "depends_on": "eval: !doc.default_service_level_agreement;",
-   "fieldname": "customer",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Customer",
-   "options": "Customer",
-   "set_only_once": 1
-  },
-  {
    "default": "0",
    "depends_on": "eval: !doc.customer;",
    "fieldname": "default_service_level_agreement",
@@ -133,9 +126,34 @@
    "fieldtype": "Check",
    "label": "Active",
    "read_only": 1
+  },
+  {
+   "collapsible_depends_on": "eval: !doc.default_service_level_agreement;",
+   "fieldname": "apply_to_section",
+   "fieldtype": "Section Break",
+   "label": "Apply To"
+  },
+  {
+   "fieldname": "apply_to",
+   "fieldtype": "Select",
+   "in_standard_filter": 1,
+   "label": "Apply To",
+   "options": "\nCustomer\nCustomer Group\nTerritory"
+  },
+  {
+   "fieldname": "column_break_10",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "entity",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Entity",
+   "options": "apply_to"
   }
  ],
- "modified": "2019-06-06 12:56:24.545060",
+ "modified": "2019-06-07 00:30:34.755416",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Service Level Agreement",
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
index 4ff0312..ccc287b 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -45,14 +45,14 @@
 
 def get_active_service_level_agreement_for(priority, customer=None, service_level_agreement=None):
 	filters = [
-		["Service Level Agreement", "active", "=", 1]
+		["Service Level Agreement", "active", "=", 1],
 	]
 
 	if priority:
 		filters.append(["Service Level Priority", "priority", "=", priority])
 
 	or_filters = [
-		["Service Level Agreement", "customer", "=", customer]
+		["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]]
 	]
 	if service_level_agreement:
 		or_filters = [
@@ -62,10 +62,18 @@
 	or_filters.append(["Service Level Agreement", "default_service_level_agreement", "=", 1])
 
 	agreement = frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters,
-		fields=["name", "default_priority", "customer"])
+		fields=["name", "default_priority"], debug=True)
 
 	return agreement[0] if agreement else None
 
 @frappe.whitelist()
 def get_service_level_agreement_priorities(name):
-	return [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])]
\ No newline at end of file
+	return [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])]
+
+def get_customer_group(customer):
+	if customer:
+		return frappe.db.get_value("Customer", customer, "customer_group")
+
+def get_customer_territory(customer):
+	if customer:
+		return frappe.db.get_value("Customer", customer, "territory")
\ No newline at end of file
diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
index 2679f97..88e1ee4 100644
--- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
@@ -10,136 +10,116 @@
 class TestServiceLevelAgreement(unittest.TestCase):
 
 	def test_service_level_agreement(self):
-		test_make_service_level_agreement = make_service_level_agreement()
-		test_get_service_level_agreement = get_service_level_agreement()
+		make_service_level()
 
-		self.assertEqual(test_make_service_level_agreement.name, test_get_service_level_agreement.name)
-		self.assertEqual(test_make_service_level_agreement.customer, test_get_service_level_agreement.customer)
-		self.assertEqual(test_make_service_level_agreement.default_service_level_agreement, test_get_service_level_agreement.default_service_level_agreement)
 
-def make_service_level_agreement():
-	make_service_level()
+		# Default Service Level Agreement
+		create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1,
+			service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
+			apply_to=None, entity=None, response_time=4, resolution_time=6)
+		get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1)
 
-	# Default Service Level Agreement
-	default_service_level_agreement = frappe.get_doc({
-		"doctype": "Service Level Agreement",
-		"service_level_agreement_name": "Default Service Level Agreement",
-		"default_service_level_agreement": 1,
-		"service_level": "__Test Service Level",
-		"holiday_list": "__Test Holiday List",
-		"employee_group": "_Test Employee Group",
-		"start_date": frappe.utils.getdate(),
-		"end_date": frappe.utils.add_to_date(frappe.utils.getdate(), days=100),
-		"priorities": [
-			{
-				"priority": "Low",
-				"response_time": 4,
-				"response_time_period": "Hour",
-				"resolution_time": 6,
-				"resolution_time_period": "Hour",
-			},
-			{
-				"priority": "Medium",
-				"response_time": 4,
-				"default_priority": 1,
-				"response_time_period": "Hour",
-				"resolution_time": 6,
-				"resolution_time_period": "Hour",
-			},
-			{
-				"priority": "High",
-				"response_time": 4,
-				"response_time_period": "Hour",
-				"resolution_time": 6,
-				"resolution_time_period": "Hour",
-			}
-		],
-		"support_and_resolution": [
-			{
-				"workday": "Monday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
-			},
-			{
-				"workday": "Tuesday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
-			},
-			{
-				"workday": "Wednesday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
-			},
-			{
-				"workday": "Thursday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
-			},
-			{
-				"workday": "Friday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
-			},
-			{
-				"workday": "Saturday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
-			},
-			{
-				"workday": "Sunday",
-				"start_time": "10:00:00",
-				"end_time": "18:00:00",
-			}
-		]
-	})
+		self.assertEqual(create_default_service_level_agreement.name, get_default_service_level_agreement.name)
+		self.assertEqual(create_default_service_level_agreement.apply_to, get_default_service_level_agreement.apply_to)
+		self.assertEqual(create_default_service_level_agreement.entity, get_default_service_level_agreement.entity)
+		self.assertEqual(create_default_service_level_agreement.default_service_level_agreement, get_default_service_level_agreement.default_service_level_agreement)
 
-	default_service_level_agreement_exists = frappe.db.exists("Service Level Agreement", "SLA-Default Service Level Agreement")
 
-	if not default_service_level_agreement_exists:
-		default_service_level_agreement.insert(ignore_permissions=True)
+		# Service Level Agreement for Customer
+		customer = frappe.get_doc({
+			"doctype": "Customer",
+			"customer_name": "_Test Customer",
+			"customer_group": "Commercial",
+			"customer_type": "Individual",
+			"territory": "Rest Of The World"
+		})
+		if not frappe.db.exists("Customer", "_Test Customer"):
+			customer.insert(ignore_permissions=True)
+		else:
+			customer = frappe.get_doc("Customer", "_Test Customer")
 
-	customer = frappe.get_doc({
-		"doctype": "Customer",
-		"customer_name": "_Test Customer",
-		"customer_group": "Commercial",
-		"customer_type": "Individual",
-		"territory": "Rest Of The World"
-	})
-	if not frappe.db.exists("Customer", "_Test Customer"):
-		customer.insert(ignore_permissions=True)
+		create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
+			service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
+			apply_to="Customer", entity=customer.name, response_time=2, resolution_time=3)
+		get_customer_service_level_agreement = get_service_level_agreement(apply_to="Customer", entity=customer.name)
+
+		self.assertEqual(create_customer_service_level_agreement.name, get_customer_service_level_agreement.name)
+		self.assertEqual(create_customer_service_level_agreement.apply_to, get_customer_service_level_agreement.apply_to)
+		self.assertEqual(create_customer_service_level_agreement.entity, get_customer_service_level_agreement.entity)
+		self.assertEqual(create_customer_service_level_agreement.default_service_level_agreement, get_customer_service_level_agreement.default_service_level_agreement)
+
+
+		# Service Level Agreement for Customer Group
+		customer_group = create_customer_group()
+		create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
+			service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
+			apply_to="Customer Group", entity=customer_group.name, response_time=4, resolution_time=6)
+		get_customer_group_service_level_agreement = get_service_level_agreement(apply_to="Customer Group", entity=customer_group.name)
+
+		self.assertEqual(create_customer_group_service_level_agreement.name, get_customer_group_service_level_agreement.name)
+		self.assertEqual(create_customer_group_service_level_agreement.apply_to, get_customer_group_service_level_agreement.apply_to)
+		self.assertEqual(create_customer_group_service_level_agreement.entity, get_customer_group_service_level_agreement.entity)
+		self.assertEqual(create_customer_group_service_level_agreement.default_service_level_agreement, get_customer_group_service_level_agreement.default_service_level_agreement)
+
+
+		# Service Level Agreement for Territory
+		territory = create_territory()
+		create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
+			service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
+			apply_to="Territory", entity=territory.name, response_time=2, resolution_time=3)
+		get_territory_service_level_agreement = get_service_level_agreement(apply_to="Territory", entity=territory.name)
+
+		self.assertEqual(create_territory_service_level_agreement.name, get_territory_service_level_agreement.name)
+		self.assertEqual(create_territory_service_level_agreement.apply_to, get_territory_service_level_agreement.apply_to)
+		self.assertEqual(create_territory_service_level_agreement.entity, get_territory_service_level_agreement.entity)
+		self.assertEqual(create_territory_service_level_agreement.default_service_level_agreement, get_territory_service_level_agreement.default_service_level_agreement)
+
+
+def get_service_level_agreement(default_service_level_agreement=None, apply_to=None, entity=None):
+	if default_service_level_agreement:
+		filters = {"default_service_level_agreement": default_service_level_agreement}
 	else:
-		customer = frappe.get_doc("Customer", "_Test Customer")
+		filters = {"apply_to": apply_to, "entity": entity}
+
+	service_level_agreement = frappe.get_doc("Service Level Agreement", filters)
+	print(service_level_agreement)
+	return service_level_agreement
+
+def create_service_level_agreement(default_service_level_agreement, service_level, holiday_list, employee_group,
+	response_time, apply_to, entity, resolution_time):
 
 	service_level_agreement = frappe.get_doc({
 		"doctype": "Service Level Agreement",
-		"service_level_agreement_name": "_Test Service Level Agreement",
-		"customer": customer.customer_name,
-		"service_level": "_Test Service Level",
-		"holiday_list": "__Test Holiday List",
-		"employee_group": "_Test Employee Group",
+		"default_service_level_agreement": default_service_level_agreement,
+		"service_level": service_level,
+		"holiday_list": holiday_list,
+		"employee_group": employee_group,
+		"apply_to": apply_to,
+		"entity": entity,
 		"start_date": frappe.utils.getdate(),
 		"end_date": frappe.utils.add_to_date(frappe.utils.getdate(), days=100),
 		"priorities": [
 			{
 				"priority": "Low",
-				"response_time": 2,
-				"response_time_period": "Day",
-				"resolution_time": 3,
-				"resolution_time_period": "Day",
+				"response_time": response_time,
+				"response_time_period": "Hour",
+				"resolution_time": resolution_time,
+				"resolution_time_period": "Hour",
 			},
 			{
 				"priority": "Medium",
-				"response_time": 2,
-				"response_time_period": "Day",
-				"resolution_time": 3,
-				"resolution_time_period": "Day",
+				"response_time": response_time,
+				"default_priority": 1,
+				"response_time_period": "Hour",
+				"resolution_time": resolution_time,
+				"resolution_time_period": "Hour",
 			},
 			{
 				"priority": "High",
-				"response_time": 2,
-				"response_time_period": "Day",
-				"resolution_time": 3,
-				"resolution_time_period": "Day",
+				"response_time": response_time,
+				"response_time_period": "Hour",
+				"resolution_time": resolution_time,
+				"resolution_time_period": "Hour",
 			}
 		],
 		"support_and_resolution": [
@@ -181,14 +161,32 @@
 		]
 	})
 
-	service_level_agreement_exists = frappe.db.exists("Service Level Agreement", {"service_level_agreement_name": "_Test Service Level Agreement"})
+	service_level_agreement_exists = frappe.db.exists("Service Level Agreement", service_level_agreement.name)
 
 	if not service_level_agreement_exists:
 		service_level_agreement.insert(ignore_permissions=True)
 		return service_level_agreement
 	else:
-		return frappe.get_doc("Service Level Agreement", "SLA-_Test Service Level Agreement")
+		return frappe.get_doc("Service Level Agreement", service_level_agreement.name)
 
-def get_service_level_agreement():
-	service_level_agreement = frappe.get_doc("Service Level Agreement", "SLA-_Test Service Level Agreement")
-	return service_level_agreement
\ No newline at end of file
+def create_customer_group():
+	customer_group = frappe.get_doc({
+		"doctype": "Customer Group",
+		"customer_group_name": "_Test SLA Customer Group"
+	})
+
+	if not frappe.db.exists("Customer Group", {"customer_group_name": "_Test SLA Customer Group"}):
+		customer_group.insert()
+
+	return customer_group.name
+
+def create_territory():
+	territory = frappe.get_doc({
+		"doctype": "Territory",
+		"territory_name": "_Test SLA Territory",
+	})
+
+	if not frappe.db.exists("Territory", {"territory_name": "_Test SLA Territory"}):
+		territory.insert()
+
+	return territory.name
\ No newline at end of file