diff --git a/crm/api/doc.py b/crm/api/doc.py
index dbadf320..335c511c 100644
--- a/crm/api/doc.py
+++ b/crm/api/doc.py
@@ -125,7 +125,6 @@ def get_list_data(doctype: str, filters: dict, order_by: str):
}
-@frappe.whitelist()
def get_doctype_fields(doctype):
not_allowed_fieldtypes = [
"Section Break",
diff --git a/crm/fcrm/doctype/crm_communication_status/__init__.py b/crm/fcrm/doctype/crm_communication_status/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/crm/fcrm/doctype/crm_communication_status/crm_communication_status.js b/crm/fcrm/doctype/crm_communication_status/crm_communication_status.js
new file mode 100644
index 00000000..33e5fd43
--- /dev/null
+++ b/crm/fcrm/doctype/crm_communication_status/crm_communication_status.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+// frappe.ui.form.on("CRM Communication Status", {
+// refresh(frm) {
+
+// },
+// });
diff --git a/crm/fcrm/doctype/crm_communication_status/crm_communication_status.json b/crm/fcrm/doctype/crm_communication_status/crm_communication_status.json
new file mode 100644
index 00000000..be7aeb93
--- /dev/null
+++ b/crm/fcrm/doctype/crm_communication_status/crm_communication_status.json
@@ -0,0 +1,47 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:status",
+ "creation": "2023-12-13 13:25:07.213100",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "status"
+ ],
+ "fields": [
+ {
+ "fieldname": "status",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Status",
+ "reqd": 1,
+ "unique": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2023-12-13 13:28:38.746199",
+ "modified_by": "Administrator",
+ "module": "FCRM",
+ "name": "CRM Communication Status",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/crm/fcrm/doctype/crm_communication_status/crm_communication_status.py b/crm/fcrm/doctype/crm_communication_status/crm_communication_status.py
new file mode 100644
index 00000000..017a6548
--- /dev/null
+++ b/crm/fcrm/doctype/crm_communication_status/crm_communication_status.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CRMCommunicationStatus(Document):
+ pass
diff --git a/crm/fcrm/doctype/crm_communication_status/test_crm_communication_status.py b/crm/fcrm/doctype/crm_communication_status/test_crm_communication_status.py
new file mode 100644
index 00000000..85f289cc
--- /dev/null
+++ b/crm/fcrm/doctype/crm_communication_status/test_crm_communication_status.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestCRMCommunicationStatus(FrappeTestCase):
+ pass
diff --git a/crm/fcrm/doctype/crm_deal/api.py b/crm/fcrm/doctype/crm_deal/api.py
index a995e65a..5e1e562e 100644
--- a/crm/fcrm/doctype/crm_deal/api.py
+++ b/crm/fcrm/doctype/crm_deal/api.py
@@ -1,6 +1,7 @@
import frappe
from frappe import _
+from crm.api.doc import get_doctype_fields
@frappe.whitelist()
def get_deal(name):
@@ -25,4 +26,5 @@ def get_deal(name):
fields=["contact", "is_primary"],
)
+ deal["doctype_fields"] = get_doctype_fields("CRM Deal")
return deal
diff --git a/crm/fcrm/doctype/crm_deal/crm_deal.json b/crm/fcrm/doctype/crm_deal/crm_deal.json
index 64b8829c..6ebc6ba3 100644
--- a/crm/fcrm/doctype/crm_deal/crm_deal.json
+++ b/crm/fcrm/doctype/crm_deal/crm_deal.json
@@ -30,6 +30,7 @@
"sla_creation",
"column_break_pfvq",
"sla_status",
+ "communication_status",
"response_details_section",
"response_by",
"column_break_hpvj",
@@ -195,11 +196,18 @@
"fieldname": "first_responded_on",
"fieldtype": "Datetime",
"label": "First Responded On"
+ },
+ {
+ "default": "Open",
+ "fieldname": "communication_status",
+ "fieldtype": "Link",
+ "label": "Communication Status",
+ "options": "CRM Communication Status"
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-12-11 12:37:51.198228",
+ "modified": "2023-12-13 13:50:55.235109",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Deal",
diff --git a/crm/fcrm/doctype/crm_deal/crm_deal.py b/crm/fcrm/doctype/crm_deal/crm_deal.py
index ac215ed5..c978eee6 100644
--- a/crm/fcrm/doctype/crm_deal/crm_deal.py
+++ b/crm/fcrm/doctype/crm_deal/crm_deal.py
@@ -5,6 +5,8 @@ import frappe
from frappe import _
from frappe.model.document import Document
+from crm.fcrm.doctype.crm_service_level_agreement.utils import get_sla
+
class CRMDeal(Document):
def before_validate(self):
@@ -55,7 +57,7 @@ class CRMDeal(Document):
"""
Find an SLA to apply to the deal.
"""
- sla = get_sla("CRM Deal")
+ sla = get_sla(self)
if not sla:
return
self.sla = sla.name
@@ -139,6 +141,7 @@ class CRMDeal(Document):
"mobile_no",
"deal_owner",
"sla_status",
+ "response_by",
"first_response_time",
"first_responded_on",
"modified",
@@ -175,8 +178,3 @@ def set_primary_contact(deal, contact):
deal.save()
return True
-def get_sla(doctype):
- sla = frappe.db.exists("CRM Service Level Agreement", {"apply_on": doctype, "enabled": 1})
- if not sla:
- return None
- return frappe.get_cached_doc("CRM Service Level Agreement", sla)
diff --git a/crm/fcrm/doctype/crm_holiday/__init__.py b/crm/fcrm/doctype/crm_holiday/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/crm/fcrm/doctype/crm_holiday/crm_holiday.json b/crm/fcrm/doctype/crm_holiday/crm_holiday.json
new file mode 100644
index 00000000..fc0f24f3
--- /dev/null
+++ b/crm/fcrm/doctype/crm_holiday/crm_holiday.json
@@ -0,0 +1,57 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2023-12-14 11:16:15.476366",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "date",
+ "column_break_xzyo",
+ "weekly_off",
+ "section_break_zenz",
+ "description"
+ ],
+ "fields": [
+ {
+ "fieldname": "date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_xzyo",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "weekly_off",
+ "fieldtype": "Check",
+ "label": "Weekly Off"
+ },
+ {
+ "fieldname": "section_break_zenz",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Text Editor",
+ "in_list_view": 1,
+ "label": "Description",
+ "reqd": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2023-12-14 11:17:41.745419",
+ "modified_by": "Administrator",
+ "module": "FCRM",
+ "name": "CRM Holiday",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/crm/fcrm/doctype/crm_holiday/crm_holiday.py b/crm/fcrm/doctype/crm_holiday/crm_holiday.py
new file mode 100644
index 00000000..d77b735d
--- /dev/null
+++ b/crm/fcrm/doctype/crm_holiday/crm_holiday.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CRMHoliday(Document):
+ pass
diff --git a/crm/fcrm/doctype/crm_holiday_list/__init__.py b/crm/fcrm/doctype/crm_holiday_list/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.js b/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.js
new file mode 100644
index 00000000..914d43de
--- /dev/null
+++ b/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+// frappe.ui.form.on("CRM Holiday List", {
+// refresh(frm) {
+
+// },
+// });
diff --git a/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.json b/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.json
new file mode 100644
index 00000000..a6a8ab6b
--- /dev/null
+++ b/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.json
@@ -0,0 +1,111 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:holiday_list_name",
+ "creation": "2023-12-14 11:09:12.876640",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "holiday_list_name",
+ "from_date",
+ "to_date",
+ "column_break_qwqc",
+ "total_holidays",
+ "add_weekly_holidays_section",
+ "weekly_off",
+ "add_to_holidays",
+ "holidays_section",
+ "holidays",
+ "clear_table"
+ ],
+ "fields": [
+ {
+ "fieldname": "holiday_list_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Holiday List Name",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "From Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "To Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_qwqc",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "total_holidays",
+ "fieldtype": "Int",
+ "label": "Total Holidays"
+ },
+ {
+ "fieldname": "add_weekly_holidays_section",
+ "fieldtype": "Section Break",
+ "label": "Add Weekly Holidays"
+ },
+ {
+ "fieldname": "weekly_off",
+ "fieldtype": "Select",
+ "label": "Weekly Off",
+ "options": "\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday"
+ },
+ {
+ "fieldname": "add_to_holidays",
+ "fieldtype": "Button",
+ "label": "Add to Holidays"
+ },
+ {
+ "fieldname": "holidays_section",
+ "fieldtype": "Section Break",
+ "label": "Holidays"
+ },
+ {
+ "fieldname": "clear_table",
+ "fieldtype": "Button",
+ "label": "Clear Table"
+ },
+ {
+ "fieldname": "holidays",
+ "fieldtype": "Table",
+ "label": "Holidays",
+ "options": "CRM Holiday"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2023-12-14 11:18:27.236817",
+ "modified_by": "Administrator",
+ "module": "FCRM",
+ "name": "CRM Holiday List",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.py b/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.py
new file mode 100644
index 00000000..45c8b101
--- /dev/null
+++ b/crm/fcrm/doctype/crm_holiday_list/crm_holiday_list.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class CRMHolidayList(Document):
+ pass
diff --git a/crm/fcrm/doctype/crm_holiday_list/test_crm_holiday_list.py b/crm/fcrm/doctype/crm_holiday_list/test_crm_holiday_list.py
new file mode 100644
index 00000000..87754859
--- /dev/null
+++ b/crm/fcrm/doctype/crm_holiday_list/test_crm_holiday_list.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestCRMHolidayList(FrappeTestCase):
+ pass
diff --git a/crm/fcrm/doctype/crm_lead/api.py b/crm/fcrm/doctype/crm_lead/api.py
index f51be6fa..26c58cff 100644
--- a/crm/fcrm/doctype/crm_lead/api.py
+++ b/crm/fcrm/doctype/crm_lead/api.py
@@ -1,6 +1,7 @@
import frappe
from frappe import _
+from crm.api.doc import get_doctype_fields
@frappe.whitelist()
def get_lead(name):
@@ -13,4 +14,5 @@ def get_lead(name):
frappe.throw(_("Lead not found"), frappe.DoesNotExistError)
lead = lead.pop()
+ lead["doctype_fields"] = get_doctype_fields("CRM Lead")
return lead
\ No newline at end of file
diff --git a/crm/fcrm/doctype/crm_lead/crm_lead.json b/crm/fcrm/doctype/crm_lead/crm_lead.json
index c37c8835..4a02bd1f 100644
--- a/crm/fcrm/doctype/crm_lead/crm_lead.json
+++ b/crm/fcrm/doctype/crm_lead/crm_lead.json
@@ -40,6 +40,7 @@
"sla_creation",
"column_break_ffnp",
"sla_status",
+ "communication_status",
"response_details_section",
"response_by",
"column_break_pweh",
@@ -257,12 +258,19 @@
"fieldname": "first_responded_on",
"fieldtype": "Datetime",
"label": "First Responded On"
+ },
+ {
+ "default": "Open",
+ "fieldname": "communication_status",
+ "fieldtype": "Link",
+ "label": "Communication Status",
+ "options": "CRM Communication Status"
}
],
"image_field": "image",
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-12-10 13:54:53.630114",
+ "modified": "2023-12-13 13:50:40.055487",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Lead",
diff --git a/crm/fcrm/doctype/crm_lead/crm_lead.py b/crm/fcrm/doctype/crm_lead/crm_lead.py
index f8a0fb6d..ccc0b607 100644
--- a/crm/fcrm/doctype/crm_lead/crm_lead.py
+++ b/crm/fcrm/doctype/crm_lead/crm_lead.py
@@ -6,6 +6,7 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils import has_gravatar, validate_email_address
+from crm.fcrm.doctype.crm_service_level_agreement.utils import get_sla
class CRMLead(Document):
@@ -124,6 +125,19 @@ class CRMLead(Document):
"contacts": [{"contact": contact}],
}
)
+
+ if self.first_responded_on:
+ deal.update(
+ {
+ "sla_creation": self.sla_creation,
+ "response_by": self.response_by,
+ "sla_status": self.sla_status,
+ "communication_status": self.communication_status,
+ "first_response_time": self.first_response_time,
+ "first_responded_on": self.first_responded_on
+ }
+ )
+
deal.insert(ignore_permissions=True)
return deal.name
@@ -131,7 +145,7 @@ class CRMLead(Document):
"""
Find an SLA to apply to the lead.
"""
- sla = get_sla("CRM Lead")
+ sla = get_sla(self)
if not sla:
return
self.sla = sla.name
@@ -219,6 +233,7 @@ class CRMLead(Document):
"lead_owner",
"first_name",
"sla_status",
+ "response_by",
"first_response_time",
"first_responded_on",
"modified",
@@ -239,8 +254,3 @@ def convert_to_deal(lead):
lead.save()
return deal
-def get_sla(doctype):
- sla = frappe.db.exists("CRM Service Level Agreement", {"apply_on": doctype, "enabled": 1})
- if not sla:
- return None
- return frappe.get_cached_doc("CRM Service Level Agreement", sla)
diff --git a/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.js b/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.js
index d1c3549c..77448ff4 100644
--- a/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.js
+++ b/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.js
@@ -6,16 +6,3 @@
// },
// });
-
-frappe.ui.form.on("CRM Service Level Priority", {
- priorities_add: function (frm, cdt, cdn) {
- if (frm.doc.apply_on == "CRM Deal") {
- frappe.model.set_value(
- cdt,
- cdn,
- "reference_doctype",
- "CRM Deal Status"
- );
- }
- },
-});
diff --git a/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.json b/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.json
index 59603dd6..e563ccea 100644
--- a/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.json
+++ b/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.json
@@ -6,15 +6,20 @@
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
- "sla_name",
"apply_on",
+ "column_break_uxua",
+ "sla_name",
"enabled",
"default",
- "column_break_uxua",
+ "section_break_nevd",
+ "start_date",
+ "end_date",
+ "column_break_pzjg",
"condition",
"section_break_ufaf",
"priorities",
"section_break_rmgo",
+ "holiday_list",
"working_hours"
],
"fields": [
@@ -60,7 +65,8 @@
},
{
"fieldname": "section_break_ufaf",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Response and Follow Up"
},
{
"fieldname": "priorities",
@@ -71,7 +77,8 @@
},
{
"fieldname": "section_break_rmgo",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Working Hours"
},
{
"fieldname": "working_hours",
@@ -79,11 +86,36 @@
"label": "Working Hours",
"options": "CRM Service Day",
"reqd": 1
+ },
+ {
+ "fieldname": "section_break_nevd",
+ "fieldtype": "Section Break",
+ "label": "Validity"
+ },
+ {
+ "fieldname": "start_date",
+ "fieldtype": "Date",
+ "label": "Start Date"
+ },
+ {
+ "fieldname": "column_break_pzjg",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "end_date",
+ "fieldtype": "Date",
+ "label": "End Date"
+ },
+ {
+ "fieldname": "holiday_list",
+ "fieldtype": "Link",
+ "label": "Holiday List",
+ "options": "CRM Holiday List"
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-12-04 16:13:24.638239",
+ "modified": "2023-12-15 11:50:29.956775",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Service Level Agreement",
diff --git a/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.py b/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.py
index 2debf7f1..882faad9 100644
--- a/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.py
+++ b/crm/fcrm/doctype/crm_service_level_agreement/crm_service_level_agreement.py
@@ -1,7 +1,8 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-# import frappe
+import frappe
+from frappe import _
from datetime import timedelta
from frappe.model.document import Document
from frappe.utils import (
@@ -12,12 +13,27 @@ from frappe.utils import (
now_datetime,
time_diff_in_seconds,
)
+from crm.fcrm.doctype.crm_service_level_agreement.utils import get_context
class CRMServiceLevelAgreement(Document):
+ def validate(self):
+ self.validate_condition()
+
+ def validate_condition(self):
+ if not self.condition:
+ return
+ try:
+ temp_doc = frappe.new_doc(self.apply_on)
+ frappe.safe_eval(self.condition, None, get_context(temp_doc))
+ except Exception as e:
+ frappe.throw(
+ _("The Condition '{0}' is invalid: {1}").format(self.condition, str(e))
+ )
+
def apply(self, doc: Document):
self.handle_new(doc)
- self.handle_status(doc)
+ self.handle_communication_status(doc)
self.handle_targets(doc)
self.handle_sla_status(doc)
@@ -27,14 +43,14 @@ class CRMServiceLevelAgreement(Document):
creation = doc.sla_creation or now_datetime()
doc.sla_creation = creation
- def handle_status(self, doc: Document):
- if doc.is_new() or not doc.has_value_changed("status"):
+ def handle_communication_status(self, doc: Document):
+ if doc.is_new() or not doc.has_value_changed("communication_status"):
return
self.set_first_responded_on(doc)
self.set_first_response_time(doc)
def set_first_responded_on(self, doc: Document):
- if doc.status != self.get_default_priority():
+ if doc.communication_status != self.get_default_priority():
doc.first_responded_on = (
doc.first_responded_on or now_datetime()
)
@@ -51,10 +67,10 @@ class CRMServiceLevelAgreement(Document):
def set_response_by(self, doc: Document):
start_time = doc.sla_creation
- status = doc.status
+ communication_status = doc.communication_status
priorities = self.get_priorities()
- priority = priorities.get(status)
+ priority = priorities.get(communication_status)
if not priority or doc.response_by:
return
diff --git a/crm/fcrm/doctype/crm_service_level_agreement/utils.py b/crm/fcrm/doctype/crm_service_level_agreement/utils.py
new file mode 100644
index 00000000..e3388195
--- /dev/null
+++ b/crm/fcrm/doctype/crm_service_level_agreement/utils.py
@@ -0,0 +1,62 @@
+import frappe
+from frappe.model.document import Document
+from frappe.query_builder import JoinType
+from frappe.utils.safe_exec import get_safe_globals
+
+DOCTYPE = "CRM Service Level Agreement"
+
+def get_sla(doc: Document) -> Document:
+ """
+ Get Service Level Agreement for `doc`
+
+ :param doc: Lead/Deal to use
+ :return: Applicable SLA
+ """
+ check_permissions(DOCTYPE, None)
+ SLA = frappe.qb.DocType(DOCTYPE)
+ Priority = frappe.qb.DocType("CRM Service Level Priority")
+ priority = doc.communication_status
+ q = (
+ frappe.qb.from_(SLA)
+ .select(SLA.name, SLA.condition)
+ .where(SLA.apply_on == doc.doctype)
+ .where(SLA.enabled == True)
+ )
+ if priority:
+ q = (
+ q.join(Priority, JoinType.inner)
+ .on(Priority.parent == SLA.name)
+ .where(Priority.priority == priority)
+ )
+ sla_list = q.run(as_dict=True)
+ res = None
+ for sla in sla_list:
+ cond = sla.get("condition")
+ if not cond or frappe.safe_eval(cond, None, get_context(doc)):
+ res = sla
+ break
+ return res
+
+def check_permissions(doctype, parent):
+ user = frappe.session.user
+ permissions = ("select", "read")
+ has_select_permission, has_read_permission = [
+ frappe.has_permission(doctype, perm, user=user, parent_doctype=parent)
+ for perm in permissions
+ ]
+
+ if not has_select_permission and not has_read_permission:
+ frappe.throw(f"Insufficient Permission for {doctype}", frappe.PermissionError)
+
+def get_context(d: Document) -> dict:
+ """
+ Get safe context for `safe_eval`
+
+ :param doc: `Document` to add in context
+ :return: Context with `doc` and safe variables
+ """
+ utils = get_safe_globals().get("frappe").get("utils")
+ return {
+ "doc": d.as_dict(),
+ "frappe": frappe._dict(utils=utils),
+ }
\ No newline at end of file
diff --git a/crm/fcrm/doctype/crm_service_level_priority/crm_service_level_priority.json b/crm/fcrm/doctype/crm_service_level_priority/crm_service_level_priority.json
index 294b94a7..ede5c8c0 100644
--- a/crm/fcrm/doctype/crm_service_level_priority/crm_service_level_priority.json
+++ b/crm/fcrm/doctype/crm_service_level_priority/crm_service_level_priority.json
@@ -7,9 +7,11 @@
"engine": "InnoDB",
"field_order": [
"default_priority",
- "reference_doctype",
+ "column_break_grod",
"priority",
- "first_response_time"
+ "section_break_anyl",
+ "first_response_time",
+ "column_break_bwgs"
],
"fields": [
{
@@ -21,10 +23,10 @@
},
{
"fieldname": "priority",
- "fieldtype": "Dynamic Link",
+ "fieldtype": "Link",
"in_list_view": 1,
"label": "Priority",
- "options": "reference_doctype",
+ "options": "CRM Communication Status",
"reqd": 1
},
{
@@ -35,17 +37,22 @@
"reqd": 1
},
{
- "default": "CRM Lead Status",
- "fieldname": "reference_doctype",
- "fieldtype": "Link",
- "label": "DocType",
- "options": "DocType"
+ "fieldname": "column_break_grod",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_anyl",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_bwgs",
+ "fieldtype": "Column Break"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-12-04 14:05:42.838493",
+ "modified": "2023-12-15 11:49:54.424029",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Service Level Priority",
diff --git a/crm/fcrm/workspace/frappe_crm/frappe_crm.json b/crm/fcrm/workspace/frappe_crm/frappe_crm.json
index 164fa444..f7e4c319 100644
--- a/crm/fcrm/workspace/frappe_crm/frappe_crm.json
+++ b/crm/fcrm/workspace/frappe_crm/frappe_crm.json
@@ -1,6 +1,6 @@
{
"charts": [],
- "content": "[{\"id\":\"1nr6UkvDiL\",\"type\":\"header\",\"data\":{\"text\":\"PORTAL\",\"col\":12}},{\"id\":\"1hyi8SysUY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"CRM Portal Page\",\"col\":3}},{\"id\":\"ktENiGaqXQ\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"VgeWLYOuAS\",\"type\":\"paragraph\",\"data\":{\"text\":\"SHORTCUTS\",\"col\":12}},{\"id\":\"A66FpG-K3T\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leads\",\"col\":3}},{\"id\":\"n9b6N5ebOj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Deals\",\"col\":3}},{\"id\":\"sGHTXrludH\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Organizations\",\"col\":3}},{\"id\":\"uXZNCdqxy0\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Contacts\",\"col\":3}},{\"id\":\"v1kkMwlntf\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"SLA\",\"col\":3}},{\"id\":\"TZ7cULX3Tk\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"zpySv0nGVQ\",\"type\":\"paragraph\",\"data\":{\"text\":\"META\",\"col\":12}},{\"id\":\"fa-uKzobpp\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead Statuses\",\"col\":3}},{\"id\":\"hxoZghUHP2\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Deal Statuses\",\"col\":3}},{\"id\":\"HbgghUpc8N\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead Sources\",\"col\":3}},{\"id\":\"8cPs7Fohb4\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Industries\",\"col\":3}}]",
+ "content": "[{\"id\":\"1nr6UkvDiL\",\"type\":\"header\",\"data\":{\"text\":\"PORTAL\",\"col\":12}},{\"id\":\"1hyi8SysUY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"CRM Portal Page\",\"col\":3}},{\"id\":\"ktENiGaqXQ\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"VgeWLYOuAS\",\"type\":\"paragraph\",\"data\":{\"text\":\"SHORTCUTS\",\"col\":12}},{\"id\":\"A66FpG-K3T\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leads\",\"col\":3}},{\"id\":\"n9b6N5ebOj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Deals\",\"col\":3}},{\"id\":\"sGHTXrludH\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Organizations\",\"col\":3}},{\"id\":\"uXZNCdqxy0\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Contacts\",\"col\":3}},{\"id\":\"v1kkMwlntf\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"SLA\",\"col\":3}},{\"id\":\"TZ7cULX3Tk\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"zpySv0nGVQ\",\"type\":\"paragraph\",\"data\":{\"text\":\"META\",\"col\":12}},{\"id\":\"fa-uKzobpp\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead Statuses\",\"col\":3}},{\"id\":\"hxoZghUHP2\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Deal Statuses\",\"col\":3}},{\"id\":\"HbgghUpc8N\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead Sources\",\"col\":3}},{\"id\":\"8cPs7Fohb4\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Industries\",\"col\":3}},{\"id\":\"ApHOcISpiJ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Communication Statuses\",\"col\":3}}]",
"creation": "2023-11-27 13:55:17.090361",
"custom_blocks": [],
"docstatus": 0,
@@ -13,7 +13,7 @@
"is_hidden": 0,
"label": "Frappe CRM",
"links": [],
- "modified": "2023-12-11 13:06:13.532693",
+ "modified": "2023-12-13 19:59:33.129412",
"modified_by": "Administrator",
"module": "FCRM",
"name": "Frappe CRM",
@@ -25,6 +25,14 @@
"roles": [],
"sequence_id": 1.0,
"shortcuts": [
+ {
+ "color": "Grey",
+ "doc_view": "List",
+ "label": "Communication Statuses",
+ "link_to": "CRM Communication Status",
+ "stats_filter": "[]",
+ "type": "DocType"
+ },
{
"color": "Grey",
"doc_view": "List",
diff --git a/crm/install.py b/crm/install.py
index 77979f73..51468333 100644
--- a/crm/install.py
+++ b/crm/install.py
@@ -10,11 +10,12 @@ def before_install():
def after_install():
add_default_lead_statuses()
add_default_deal_statuses()
+ add_default_communication_statuses()
frappe.db.commit()
def add_default_lead_statuses():
statuses = {
- "Open": {
+ "New": {
"color": "gray",
"position": 1,
},
@@ -90,4 +91,15 @@ def add_default_deal_statuses():
doc.deal_status = status
doc.color = statuses[status]["color"]
doc.position = statuses[status]["position"]
- doc.insert()
\ No newline at end of file
+ doc.insert()
+
+def add_default_communication_statuses():
+ statuses = ["Open", "Replied"]
+
+ for status in statuses:
+ if frappe.db.exists("CRM Communication Status", status):
+ continue
+
+ doc = frappe.new_doc("CRM Communication Status")
+ doc.status = status
+ doc.insert()
diff --git a/frontend/src/components/ListViews/DealsListView.vue b/frontend/src/components/ListViews/DealsListView.vue
index 72efab98..a02b6996 100644
--- a/frontend/src/components/ListViews/DealsListView.vue
+++ b/frontend/src/components/ListViews/DealsListView.vue
@@ -51,6 +51,7 @@
'creation',
'first_response_time',
'first_responded_on',
+ 'response_by',
].includes(column.key)
"
class="truncate text-base"
@@ -62,11 +63,11 @@
class="truncate text-base"
>