diff --git a/crm/api/activities.py b/crm/api/activities.py index f915d808..7f6fb689 100644 --- a/crm/api/activities.py +++ b/crm/api/activities.py @@ -6,6 +6,8 @@ from frappe import _ from frappe.desk.form.load import get_docinfo from frappe.query_builder import JoinType +from crm.fcrm.doctype.crm_call_log.crm_call_log import parse_call_log + @frappe.whitelist() def get_activities(name): @@ -439,6 +441,8 @@ def get_linked_calls(name): ], ) + calls = [parse_call_log(call) for call in calls] if calls else [] + return {"calls": calls, "notes": notes, "tasks": tasks} diff --git a/crm/api/doc.py b/crm/api/doc.py index 844bdd98..a6d1b163 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -306,6 +306,7 @@ def get_data( ) or [] ) + data = parse_list_data(data, doctype) if view_type == "kanban": if not rows: @@ -479,6 +480,13 @@ def get_data( } +def parse_list_data(data, doctype): + _list = get_controller(doctype) + if hasattr(_list, "parse_list_data"): + data = _list.parse_list_data(data) + return data + + def convert_filter_to_tuple(doctype, filters): if isinstance(filters, dict): filters_items = filters.items() diff --git a/crm/fcrm/doctype/crm_call_log/crm_call_log.py b/crm/fcrm/doctype/crm_call_log/crm_call_log.py index 42713c3e..42388085 100644 --- a/crm/fcrm/doctype/crm_call_log/crm_call_log.py +++ b/crm/fcrm/doctype/crm_call_log/crm_call_log.py @@ -4,6 +4,9 @@ import frappe from frappe.model.document import Document +from crm.integrations.api import get_contact_by_phone_number +from crm.utils import seconds_to_duration + class CRMCallLog(Document): @staticmethod @@ -77,6 +80,9 @@ class CRMCallLog(Document): ] return {"columns": columns, "rows": rows} + def parse_list_data(calls): + return [parse_call_log(call) for call in calls] if calls else [] + def has_link(self, doctype, name): for link in self.links: if link.link_doctype == doctype and link.link_name == name: @@ -89,6 +95,69 @@ class CRMCallLog(Document): self.append("links", {"link_doctype": reference_doctype, "link_name": reference_name}) +def parse_call_log(call): + call["show_recording"] = False + call["duration"] = seconds_to_duration(call.get("duration")) + if call.get("type") == "Incoming": + call["activity_type"] = "incoming_call" + contact = get_contact_by_phone_number(call.get("from")) + receiver = ( + frappe.db.get_values("User", call.get("receiver"), ["full_name", "user_image"])[0] + if call.get("receiver") + else [None, None] + ) + call["caller"] = { + "label": contact.get("full_name", "Unknown"), + "image": contact.get("image"), + } + call["receiver"] = { + "label": receiver[0], + "image": receiver[1], + } + elif call.get("type") == "Outgoing": + call["activity_type"] = "outgoing_call" + contact = get_contact_by_phone_number(call.get("to")) + caller = ( + frappe.db.get_values("User", call.get("caller"), ["full_name", "user_image"])[0] + if call.get("caller") + else [None, None] + ) + call["caller"] = { + "label": caller[0], + "image": caller[1], + } + call["receiver"] = { + "label": contact.get("full_name", "Unknown"), + "image": contact.get("image"), + } + + return call + + +@frappe.whitelist() +def get_call_log(name): + call = frappe.get_cached_doc( + "CRM Call Log", + name, + fields=[ + "name", + "caller", + "receiver", + "duration", + "type", + "status", + "from", + "to", + "note", + "recording_url", + "reference_doctype", + "reference_docname", + "creation", + ], + ).as_dict() + return parse_call_log(call) + + @frappe.whitelist() def create_lead_from_call_log(call_log): lead = frappe.new_doc("CRM Lead") diff --git a/crm/utils/__init__.py b/crm/utils/__init__.py index e4ef8ccd..cd4a4ff8 100644 --- a/crm/utils/__init__.py +++ b/crm/utils/__init__.py @@ -1,4 +1,5 @@ import phonenumbers +from frappe.utils import floor from phonenumbers import NumberParseException from phonenumbers import PhoneNumberFormat as PNF @@ -58,3 +59,37 @@ def are_same_phone_number(number1, number2, default_region="IN", validate=True): except phonenumbers.NumberParseException: return False + + +def seconds_to_duration(seconds): + if not seconds: + return "0s" + + hours = floor(seconds // 3600) + minutes = floor((seconds % 3600) // 60) + seconds = floor((seconds % 3600) % 60) + + # 1h 0m 0s -> 1h + # 0h 1m 0s -> 1m + # 0h 0m 1s -> 1s + # 1h 1m 0s -> 1h 1m + # 1h 0m 1s -> 1h 1s + # 0h 1m 1s -> 1m 1s + # 1h 1m 1s -> 1h 1m 1s + + if hours and minutes and seconds: + return f"{hours}h {minutes}m {seconds}s" + elif hours and minutes: + return f"{hours}h {minutes}m" + elif hours and seconds: + return f"{hours}h {seconds}s" + elif minutes and seconds: + return f"{minutes}m {seconds}s" + elif hours: + return f"{hours}h" + elif minutes: + return f"{minutes}m" + elif seconds: + return f"{seconds}s" + else: + return "0s" diff --git a/frontend/src/components/Activities/Activities.vue b/frontend/src/components/Activities/Activities.vue index 2d0b93d9..392bf4b5 100644 --- a/frontend/src/components/Activities/Activities.vue +++ b/frontend/src/components/Activities/Activities.vue @@ -484,7 +484,7 @@ import CommunicationArea from '@/components/CommunicationArea.vue' import WhatsappTemplateSelectorModal from '@/components/Modals/WhatsappTemplateSelectorModal.vue' import AllModals from '@/components/Activities/AllModals.vue' import FilesUploader from '@/components/FilesUploader/FilesUploader.vue' -import { timeAgo, formatDate, secondsToDuration, startCase } from '@/utils' +import { timeAgo, formatDate, startCase } from '@/utils' import { globalStore } from '@/stores/global' import { usersStore } from '@/stores/users' import { contactsStore } from '@/stores/contacts' @@ -544,40 +544,6 @@ const all_activities = createResource({ cache: ['activity', doc.value.data.name], auto: true, transform: ([versions, calls, notes, tasks, attachments]) => { - if (calls?.length) { - calls.forEach((doc) => { - doc.show_recording = false - doc.activity_type = - doc.type === 'Incoming' ? 'incoming_call' : 'outgoing_call' - doc.duration = secondsToDuration(doc.duration) - if (doc.type === 'Incoming') { - doc.caller = { - label: - getContact(doc.from)?.full_name || - getLeadContact(doc.from)?.full_name || - 'Unknown', - image: - getContact(doc.from)?.image || getLeadContact(doc.from)?.image, - } - doc.receiver = { - label: getUser(doc.receiver).full_name, - image: getUser(doc.receiver).user_image, - } - } else { - doc.caller = { - label: getUser(doc.caller).full_name, - image: getUser(doc.caller).user_image, - } - doc.receiver = { - label: - getContact(doc.to)?.full_name || - getLeadContact(doc.to)?.full_name || - 'Unknown', - image: getContact(doc.to)?.image || getLeadContact(doc.to)?.image, - } - } - }) - } return { versions, calls, notes, tasks, attachments } }, }) diff --git a/frontend/src/components/Modals/CallLogModal.vue b/frontend/src/components/Modals/CallLogModal.vue index 23c2eeec..4fcd0940 100644 --- a/frontend/src/components/Modals/CallLogModal.vue +++ b/frontend/src/components/Modals/CallLogModal.vue @@ -85,7 +85,8 @@