feat: dynamic conditions for applying SLA (#26806)

diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index e4c4af0..b48925d 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -235,8 +235,7 @@
 				self.set_response_and_resolution_time()
 
 	def set_response_and_resolution_time(self, priority=None, service_level_agreement=None):
-		service_level_agreement = get_active_service_level_agreement_for(priority=priority,
-			customer=self.customer, service_level_agreement=service_level_agreement)
+		service_level_agreement = get_active_service_level_agreement_for(self)
 
 		if not service_level_agreement:
 			if frappe.db.get_value("Issue", self.name, "service_level_agreement"):
@@ -247,7 +246,8 @@
 			frappe.throw(_("This Service Level Agreement is specific to Customer {0}").format(service_level_agreement.customer))
 
 		self.service_level_agreement = service_level_agreement.name
-		self.priority = service_level_agreement.default_priority if not priority else priority
+		if not self.priority:
+			self.priority = service_level_agreement.default_priority
 
 		priority = get_priority(self)
 
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 939c199..1678f04 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json
@@ -18,6 +18,10 @@
   "entity_type",
   "column_break_10",
   "entity",
+  "filters_section",
+  "condition",
+  "column_break_15",
+  "condition_description",
   "agreement_details_section",
   "start_date",
   "active",
@@ -171,10 +175,30 @@
    "fieldtype": "Table",
    "label": "Pause SLA On",
    "options": "Pause SLA On Status"
+  },
+  {
+   "fieldname": "filters_section",
+   "fieldtype": "Section Break",
+   "label": "Assignment Condition"
+  },
+  {
+   "fieldname": "column_break_15",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "condition",
+   "fieldtype": "Code",
+   "label": "Condition",
+   "options": "Python"
+  },
+  {
+   "fieldname": "condition_description",
+   "fieldtype": "HTML",
+   "options": "<p><strong>Condition Examples:</strong></p>\n<pre>doc.status==\"Open\"<br>doc.due_date==nowdate()<br>doc.total &gt; 40000\n</pre>"
   }
  ],
  "links": [],
- "modified": "2020-06-10 12:30:15.050785",
+ "modified": "2021-07-27 11:16:45.596579",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Service Level Agreement",
@@ -208,4 +232,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
\ No newline at end of file
+}
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 70c4696..ec0237e 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -3,10 +3,12 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
+
 import frappe
 from frappe.model.document import Document
 from frappe import _
-from frappe.utils import getdate, get_weekdays, get_link_to_form
+from frappe.utils import getdate, get_weekdays, get_link_to_form, nowdate
+from frappe.utils.safe_exec import get_safe_globals
 
 class ServiceLevelAgreement(Document):
 
@@ -14,6 +16,7 @@
 		self.validate_doc()
 		self.check_priorities()
 		self.check_support_and_resolution()
+		self.validate_condition()
 
 	def check_priorities(self):
 		default_priority = []
@@ -92,6 +95,14 @@
 			if frappe.db.exists("Service Level Agreement", {"entity_type": self.entity_type, "entity": self.entity, "name": ["!=", self.name]}):
 				frappe.throw(_("Service Level Agreement with Entity Type {0} and Entity {1} already exists.").format(self.entity_type, self.entity))
 
+	def validate_condition(self):
+		temp_doc = frappe.new_doc('Issue')
+		if self.condition:
+			try:
+				frappe.safe_eval(self.condition, None, get_context(temp_doc))
+			except Exception:
+				frappe.throw(_("The Condition '{0}' is invalid").format(self.condition))
+
 	def get_service_level_agreement_priority(self, priority):
 		priority = frappe.get_doc("Service Level Priority", {"priority": priority, "parent": self.name})
 
@@ -112,7 +123,7 @@
 		if doc.end_date and getdate(doc.end_date) < getdate(frappe.utils.getdate()):
 			frappe.db.set_value("Service Level Agreement", service_level_agreement.name, "active", 0)
 
-def get_active_service_level_agreement_for(priority, customer=None, service_level_agreement=None):
+def get_active_service_level_agreement_for(doc):
 	if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
 		return
 
@@ -121,23 +132,42 @@
 		["Service Level Agreement", "enable", "=", 1]
 	]
 
-	if priority:
-		filters.append(["Service Level Priority", "priority", "=", priority])
+	if doc.get('priority'):
+		filters.append(["Service Level Priority", "priority", "=", doc.get('priority')])
 
+	customer = doc.get('customer')
 	or_filters = [
 		["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]]
 	]
+
+	service_level_agreement = doc.get('service_level_agreement')
 	if service_level_agreement:
 		or_filters = [
-			["Service Level Agreement", "name", "=", service_level_agreement],
+			["Service Level Agreement", "name", "=", doc.get('service_level_agreement')],
 		]
 
-	or_filters.append(["Service Level Agreement", "default_service_level_agreement", "=", 1])
+	default_sla_filter = filters + [["Service Level Agreement", "default_service_level_agreement", "=", 1]]
+	default_sla = frappe.get_all("Service Level Agreement", filters=default_sla_filter,
+		fields=["name", "default_priority", "condition"])
 
-	agreement = frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters,
-		fields=["name", "default_priority"])
+	filters += [["Service Level Agreement", "default_service_level_agreement", "=", 0]]
+	agreements = frappe.get_all("Service Level Agreement", filters=filters, or_filters=or_filters,
+		fields=["name", "default_priority", "condition"])
+	
+	# check if the current document on which SLA is to be applied fulfills all the conditions
+	filtered_agreements = []
+	for agreement in agreements:
+		condition = agreement.get('condition')
+		if not condition or (condition and frappe.safe_eval(condition, None, get_context(doc))):
+			filtered_agreements.append(agreement)
 
-	return agreement[0] if agreement else None
+	# if any default sla
+	filtered_agreements += default_sla
+
+	return filtered_agreements[0] if filtered_agreements else None
+
+def get_context(doc):
+	return {"doc": doc.as_dict(), "nowdate": nowdate, "frappe": frappe._dict(utils=get_safe_globals().get("frappe").get("utils"))}
 
 def get_customer_group(customer):
 	if customer: