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" >
diff --git a/frontend/src/components/ListViews/LeadsListView.vue b/frontend/src/components/ListViews/LeadsListView.vue index 01eae1e0..c8d78b8d 100644 --- a/frontend/src/components/ListViews/LeadsListView.vue +++ b/frontend/src/components/ListViews/LeadsListView.vue @@ -60,6 +60,7 @@ 'creation', 'first_response_time', 'first_responded_on', + 'response_by', ].includes(column.key) " class="truncate text-base" @@ -71,11 +72,11 @@ class="truncate text-base" >
diff --git a/frontend/src/components/Modals/ContactModal.vue b/frontend/src/components/Modals/ContactModal.vue index ffc46972..ba3de768 100644 --- a/frontend/src/components/Modals/ContactModal.vue +++ b/frontend/src/components/Modals/ContactModal.vue @@ -338,7 +338,7 @@ const sections = computed(() => { fields: [ { label: 'Email', - type: 'dropdown', + type: props.contact.name ? 'dropdown' : 'data', name: 'email_id', options: props.contact?.email_ids?.map((email) => { return { @@ -374,7 +374,7 @@ const sections = computed(() => { fields: [ { label: 'Mobile No.', - type: 'dropdown', + type: props.contact.name ? 'dropdown' : 'data', name: 'mobile_no', options: props.contact?.phone_nos?.map((phone) => { return { diff --git a/frontend/src/components/Modals/NoteModal.vue b/frontend/src/components/Modals/NoteModal.vue index 5af22b9b..a0ade1cb 100644 --- a/frontend/src/components/Modals/NoteModal.vue +++ b/frontend/src/components/Modals/NoteModal.vue @@ -8,7 +8,7 @@ { label: editMode ? 'Update' : 'Create', variant: 'solid', - onClick: ({ close }) => updateNote(close), + onClick: () => updateNote(), }, ], }" @@ -67,7 +67,7 @@ const title = ref(null) const editMode = ref(false) let _note = ref({}) -async function updateNote(close) { +async function updateNote() { if ( props.note.title === _note.value.title && props.note.content === _note.value.content @@ -97,7 +97,7 @@ async function updateNote(close) { notes.value.reload() } } - close() + show.value = false } watch( diff --git a/frontend/src/components/Modals/TaskModal.vue b/frontend/src/components/Modals/TaskModal.vue index db8fcfb6..d2b397c4 100644 --- a/frontend/src/components/Modals/TaskModal.vue +++ b/frontend/src/components/Modals/TaskModal.vue @@ -8,7 +8,7 @@ { label: editMode ? 'Update' : 'Create', variant: 'solid', - onClick: ({ close }) => updateTask(close), + onClick: () => updateTask(), }, ], }" @@ -142,7 +142,7 @@ function updateTaskPriority(priority) { _task.value.priority = priority } -async function updateTask(close) { +async function updateTask() { if (!_task.value.assigned_to) { _task.value.assigned_to = getUser().email } @@ -168,7 +168,7 @@ async function updateTask(close) { tasks.value.reload() } } - close() + show.value = false } watch( diff --git a/frontend/src/components/NewDeal.vue b/frontend/src/components/NewDeal.vue index 57805ceb..26f49291 100644 --- a/frontend/src/components/NewDeal.vue +++ b/frontend/src/components/NewDeal.vue @@ -10,10 +10,8 @@ :options="field.options" v-model="newDeal[field.name]" > -