From 8d3011832be06f651085994b148022ff635f4e0d Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Wed, 23 Feb 2022 12:59:38 +0530 Subject: [PATCH 01/18] fix: call status fix --- .../erpnext_integrations/exotel_integration.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py index 167fcb7165..c4f6636239 100644 --- a/erpnext/erpnext_integrations/exotel_integration.py +++ b/erpnext/erpnext_integrations/exotel_integration.py @@ -33,10 +33,23 @@ def handle_end_call(**kwargs): @frappe.whitelist(allow_guest=True) def handle_missed_call(**kwargs): - update_call_log(kwargs, 'Missed') + status = "" + CallType = kwargs.get("CallType") + DialCallStatus = kwargs.get("DialCallStatus") + + if CallType == "incomplete" and DialCallStatus == "no-answer": + status = 'No Answer' + elif CallType == "client-hangup" and DialCallStatus == "canceled": + status = 'Canceled' + + update_call_log(kwargs, status) def update_call_log(call_payload, status='Ringing', call_log=None): call_log = call_log or get_call_log(call_payload) + + # for a new sid, call_log and get_call_log will be empty so create a new log + if not call_log: + call_log = create_call_log(call_payload) if call_log: call_log.status = status call_log.to = call_payload.get('DialWhomNumber') From 006606c437463c77b8445102ed49fc7dc45dcb65 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Wed, 23 Feb 2022 18:45:50 +0530 Subject: [PATCH 02/18] fix: added employee name to call log --- erpnext/telephony/doctype/call_log/call_log.json | 14 ++++++++++++-- erpnext/telephony/doctype/call_log/call_log.py | 9 +++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/erpnext/telephony/doctype/call_log/call_log.json b/erpnext/telephony/doctype/call_log/call_log.json index 1d6c39edf6..a3dbb02213 100644 --- a/erpnext/telephony/doctype/call_log/call_log.json +++ b/erpnext/telephony/doctype/call_log/call_log.json @@ -1,7 +1,7 @@ { "actions": [], "autoname": "field:id", - "creation": "2019-06-05 12:07:02.634534", + "creation": "2022-02-21 11:54:58.414784", "doctype": "DocType", "engine": "InnoDB", "field_order": [ @@ -9,6 +9,7 @@ "id", "from", "to", + "employee_call_directed_to", "medium", "start_time", "end_time", @@ -134,15 +135,23 @@ "fieldname": "call_details_section", "fieldtype": "Section Break", "label": "Call Details" + }, + { + "depends_on": "to", + "fieldname": "employee_call_directed_to", + "fieldtype": "Data", + "label": "Employee Call Directed To", + "read_only": 1 } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2021-02-08 14:23:28.744844", + "modified": "2022-02-23 18:45:06.932571", "modified_by": "Administrator", "module": "Telephony", "name": "Call Log", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -164,6 +173,7 @@ ], "sort_field": "creation", "sort_order": "DESC", + "states": [], "title_field": "from", "track_changes": 1, "track_views": 1 diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 0c24484bdf..7d86d12cf4 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -33,6 +33,15 @@ class CallLog(Document): if lead: self.add_link(link_type='Lead', link_name=lead) + # Add Employee Name + if self.is_incoming_call(): + # Taking the last 10 digits of the number + emp_number_reversed = (self.get("to"))[-1:-11:-1] + emp_number = emp_number_reversed[-1::-1] + + emp_name = frappe.get_all("Employee", filters={"cell_number":["like","%"+emp_number+"%"]}, fields=["first_name", "middle_name", "last_name"]) + self.employee_call_directed_to = (emp_name[0].get("first_name") or '') + ' ' + (emp_name[0].get("middle_name") or '') + ' ' + (emp_name[0].get("last_name") or '') + def after_insert(self): self.trigger_call_popup() From fd20713bd764eae7696458d6062b2ecb2d121561 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Fri, 25 Feb 2022 12:01:53 +0530 Subject: [PATCH 03/18] fix: added field to show called group, user_id --- erpnext/telephony/doctype/call_log/call_log.json | 9 ++++++++- erpnext/telephony/doctype/call_log/call_log.py | 12 ++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/erpnext/telephony/doctype/call_log/call_log.json b/erpnext/telephony/doctype/call_log/call_log.json index a3dbb02213..c0f022b020 100644 --- a/erpnext/telephony/doctype/call_log/call_log.json +++ b/erpnext/telephony/doctype/call_log/call_log.json @@ -10,6 +10,7 @@ "from", "to", "employee_call_directed_to", + "employee_user_id", "medium", "start_time", "end_time", @@ -142,12 +143,18 @@ "fieldtype": "Data", "label": "Employee Call Directed To", "read_only": 1 + }, + { + "fieldname": "employee_user_id", + "fieldtype": "Data", + "hidden": 1, + "label": "Employee User Id" } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-02-23 18:45:06.932571", + "modified": "2022-02-23 19:47:04.310577", "modified_by": "Administrator", "module": "Telephony", "name": "Call Log", diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 7d86d12cf4..787015169b 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -39,8 +39,9 @@ class CallLog(Document): emp_number_reversed = (self.get("to"))[-1:-11:-1] emp_number = emp_number_reversed[-1::-1] - emp_name = frappe.get_all("Employee", filters={"cell_number":["like","%"+emp_number+"%"]}, fields=["first_name", "middle_name", "last_name"]) - self.employee_call_directed_to = (emp_name[0].get("first_name") or '') + ' ' + (emp_name[0].get("middle_name") or '') + ' ' + (emp_name[0].get("last_name") or '') + employee = frappe.get_all("Employee", filters={"cell_number":["like","%"+emp_number+"%"]}, fields=["first_name", "middle_name", "last_name", "user_id"]) + self.employee_call_directed_to = get_employee_name(employee[0]) + self.employee_user_id = employee[0].get("user_id") or '' def after_insert(self): self.trigger_call_popup() @@ -93,6 +94,13 @@ class CallLog(Document): for email in emails: frappe.publish_realtime('show_call_popup', self, user=email) +def get_employee_name(emp): + employee_name = '' + for name in ['first_name', 'middle_name', 'last_name']: + if emp.get(name): + employee_name += (' ' if employee_name else '') + emp.get(name) + return employee_name + @frappe.whitelist() def add_call_summary(call_log, summary): From 00b0f10100a2d38e73cd415c7f0839b68cbaa62d Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Fri, 25 Feb 2022 13:38:57 +0530 Subject: [PATCH 04/18] fix: added type of call select field, additional status for agent rejecting call --- erpnext/erpnext_integrations/exotel_integration.py | 2 ++ erpnext/public/js/call_popup/call_popup.js | 13 ++++++++++++- erpnext/telephony/doctype/call_log/call_log.json | 11 +++++++++-- erpnext/telephony/doctype/call_log/call_log.py | 4 +++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py index c4f6636239..a94596933d 100644 --- a/erpnext/erpnext_integrations/exotel_integration.py +++ b/erpnext/erpnext_integrations/exotel_integration.py @@ -41,6 +41,8 @@ def handle_missed_call(**kwargs): status = 'No Answer' elif CallType == "client-hangup" and DialCallStatus == "canceled": status = 'Canceled' + elif CallType == "incomplete" and DialCallStatus == "failed": + status = 'Failed' update_call_log(kwargs, status) diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index c954f12ac6..2addeae0e5 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -141,6 +141,15 @@ class CallPopup { 'fieldtype': 'Section Break', 'hide_border': 1, }, { + 'fieldname': 'type_of_call', + 'label': 'Type Of Call', + 'fieldtype': 'Select', + 'options': '\nFrappe Cloud Queries/Plan\nEnterprise Plans\nPartnership\nSupport\nBilling/Renewal\nOpen source / Junk', + 'default': 'Frappe Cloud Queries/Plan', + },{ + 'fieldtype': 'Section Break', + 'hide_border': 1, + },{ 'fieldtype': 'Small Text', 'label': __('Call Summary'), 'fieldname': 'call_summary', @@ -149,10 +158,12 @@ class CallPopup { 'label': __('Save'), 'click': () => { const call_summary = this.call_details.get_value('call_summary'); + const call_type = this.call_details.get_value('type_of_call'); if (!call_summary) return; - frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary', { + frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary_and_call_type', { 'call_log': this.call_log.name, 'summary': call_summary, + 'call_type': call_type, }).then(() => { this.close_modal(); frappe.show_alert({ diff --git a/erpnext/telephony/doctype/call_log/call_log.json b/erpnext/telephony/doctype/call_log/call_log.json index c0f022b020..615e069e72 100644 --- a/erpnext/telephony/doctype/call_log/call_log.json +++ b/erpnext/telephony/doctype/call_log/call_log.json @@ -22,6 +22,7 @@ "recording_url", "recording_html", "section_break_11", + "type_of_call", "summary", "section_break_19", "links" @@ -105,7 +106,8 @@ }, { "fieldname": "summary", - "fieldtype": "Small Text" + "fieldtype": "Small Text", + "label": "Summary" }, { "fieldname": "section_break_11", @@ -149,12 +151,17 @@ "fieldtype": "Data", "hidden": 1, "label": "Employee User Id" + }, + { + "fieldname": "type_of_call", + "fieldtype": "Data", + "label": "Type Of Call" } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-02-23 19:47:04.310577", + "modified": "2022-02-25 13:37:48.156501", "modified_by": "Administrator", "module": "Telephony", "name": "Call Log", diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 787015169b..e55d2906c9 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -103,8 +103,10 @@ def get_employee_name(emp): @frappe.whitelist() -def add_call_summary(call_log, summary): +def add_call_summary_and_call_type(call_log, summary, call_type): doc = frappe.get_doc('Call Log', call_log) + doc.type_of_call = call_type + doc.save() doc.add_comment('Comment', frappe.bold(_('Call Summary')) + '

' + summary) def get_employees_with_number(number): From a91fb8892954309a292276fb2fc116ed1cf76fa4 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Fri, 25 Feb 2022 13:45:26 +0530 Subject: [PATCH 05/18] fix: sider fixes --- erpnext/public/js/call_popup/call_popup.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 2addeae0e5..b7d3485b65 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -146,10 +146,10 @@ class CallPopup { 'fieldtype': 'Select', 'options': '\nFrappe Cloud Queries/Plan\nEnterprise Plans\nPartnership\nSupport\nBilling/Renewal\nOpen source / Junk', 'default': 'Frappe Cloud Queries/Plan', - },{ + }, { 'fieldtype': 'Section Break', 'hide_border': 1, - },{ + }, { 'fieldtype': 'Small Text', 'label': __('Call Summary'), 'fieldname': 'call_summary', From 5b79496f057c79163fcea8f165914d109cdbc35f Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Fri, 25 Feb 2022 16:52:25 +0530 Subject: [PATCH 06/18] fix: call type doctype, fixes --- .../exotel_integration.py | 10 ++-- erpnext/public/js/call_popup/call_popup.js | 11 ++-- .../telephony/doctype/call_log/call_log.json | 18 +++--- .../telephony/doctype/call_log/call_log.py | 6 +- .../doctype/telephony_call_type/__init__.py | 0 .../telephony_call_type.js | 8 +++ .../telephony_call_type.json | 58 +++++++++++++++++++ .../telephony_call_type.py | 8 +++ .../test_telephony_call_type.py | 8 +++ 9 files changed, 105 insertions(+), 22 deletions(-) create mode 100644 erpnext/telephony/doctype/telephony_call_type/__init__.py create mode 100644 erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js create mode 100644 erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json create mode 100644 erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py create mode 100644 erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py index a94596933d..502dbed4f7 100644 --- a/erpnext/erpnext_integrations/exotel_integration.py +++ b/erpnext/erpnext_integrations/exotel_integration.py @@ -34,14 +34,14 @@ def handle_end_call(**kwargs): @frappe.whitelist(allow_guest=True) def handle_missed_call(**kwargs): status = "" - CallType = kwargs.get("CallType") - DialCallStatus = kwargs.get("DialCallStatus") + call_type = kwargs.get("CallType") + dial_call_status = kwargs.get("DialCallStatus") - if CallType == "incomplete" and DialCallStatus == "no-answer": + if call_type == "incomplete" and dial_call_status == "no-answer": status = 'No Answer' - elif CallType == "client-hangup" and DialCallStatus == "canceled": + elif call_type == "client-hangup" and dial_call_status == "canceled": status = 'Canceled' - elif CallType == "incomplete" and DialCallStatus == "failed": + elif call_type == "incomplete" and dial_call_status == "failed": status = 'Failed' update_call_log(kwargs, status) diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 2addeae0e5..4d69c4ba59 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -141,11 +141,10 @@ class CallPopup { 'fieldtype': 'Section Break', 'hide_border': 1, }, { - 'fieldname': 'type_of_call', - 'label': 'Type Of Call', - 'fieldtype': 'Select', - 'options': '\nFrappe Cloud Queries/Plan\nEnterprise Plans\nPartnership\nSupport\nBilling/Renewal\nOpen source / Junk', - 'default': 'Frappe Cloud Queries/Plan', + 'fieldname': 'call_type', + 'label': 'Call Type', + 'fieldtype': 'Link', + 'options': 'Telephony Call Type', },{ 'fieldtype': 'Section Break', 'hide_border': 1, @@ -158,7 +157,7 @@ class CallPopup { 'label': __('Save'), 'click': () => { const call_summary = this.call_details.get_value('call_summary'); - const call_type = this.call_details.get_value('type_of_call'); + const call_type = this.call_details.get_value('call_type'); if (!call_summary) return; frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary_and_call_type', { 'call_log': this.call_log.name, diff --git a/erpnext/telephony/doctype/call_log/call_log.json b/erpnext/telephony/doctype/call_log/call_log.json index 615e069e72..cd749e8a01 100644 --- a/erpnext/telephony/doctype/call_log/call_log.json +++ b/erpnext/telephony/doctype/call_log/call_log.json @@ -9,7 +9,7 @@ "id", "from", "to", - "employee_call_directed_to", + "call_received_by", "employee_user_id", "medium", "start_time", @@ -139,13 +139,6 @@ "fieldtype": "Section Break", "label": "Call Details" }, - { - "depends_on": "to", - "fieldname": "employee_call_directed_to", - "fieldtype": "Data", - "label": "Employee Call Directed To", - "read_only": 1 - }, { "fieldname": "employee_user_id", "fieldtype": "Data", @@ -156,12 +149,19 @@ "fieldname": "type_of_call", "fieldtype": "Data", "label": "Type Of Call" + }, + { + "depends_on": "to", + "fieldname": "call_received_by", + "fieldtype": "Data", + "label": "Call Received By", + "read_only": 1 } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-02-25 13:37:48.156501", + "modified": "2022-02-25 14:37:48.575230", "modified_by": "Administrator", "module": "Telephony", "name": "Call Log", diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index e55d2906c9..7b81a29fc1 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -38,9 +38,11 @@ class CallLog(Document): # Taking the last 10 digits of the number emp_number_reversed = (self.get("to"))[-1:-11:-1] emp_number = emp_number_reversed[-1::-1] + employee = frappe.get_all("Employee", filters={ + "cell_number": ["like", "%"+emp_number+"%"] + }, fields=["first_name", "middle_name", "last_name", "user_id"]) - employee = frappe.get_all("Employee", filters={"cell_number":["like","%"+emp_number+"%"]}, fields=["first_name", "middle_name", "last_name", "user_id"]) - self.employee_call_directed_to = get_employee_name(employee[0]) + self.call_received_by = get_employee_name(employee[0]) self.employee_user_id = employee[0].get("user_id") or '' def after_insert(self): diff --git a/erpnext/telephony/doctype/telephony_call_type/__init__.py b/erpnext/telephony/doctype/telephony_call_type/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js new file mode 100644 index 0000000000..efba2b86ff --- /dev/null +++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.js @@ -0,0 +1,8 @@ +// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Telephony Call Type', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json new file mode 100644 index 0000000000..603709e98f --- /dev/null +++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.json @@ -0,0 +1,58 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:call_type", + "creation": "2022-02-25 16:13:37.321312", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "call_type", + "amended_from" + ], + "fields": [ + { + "fieldname": "call_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Call Type", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Telephony Call Type", + "print_hide": 1, + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2022-02-25 16:14:07.087461", + "modified_by": "Administrator", + "module": "Telephony", + "name": "Telephony Call Type", + "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/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py new file mode 100644 index 0000000000..5abb3180df --- /dev/null +++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py @@ -0,0 +1,8 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class TelephonyCallType(Document): + pass diff --git a/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py b/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py new file mode 100644 index 0000000000..c16d03e097 --- /dev/null +++ b/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py @@ -0,0 +1,8 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestTelephonyCallType(unittest.TestCase): + pass From 1ab5b7a811674b808d42ab8b7b2ad924531cc247 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 28 Feb 2022 12:47:05 +0530 Subject: [PATCH 07/18] fix: linter, sider fixes --- erpnext/public/js/call_popup/call_popup.js | 2 +- .../doctype/telephony_call_type/telephony_call_type.py | 1 + .../doctype/telephony_call_type/test_telephony_call_type.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 119aad7e6e..2dbe999e05 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -145,7 +145,7 @@ class CallPopup { 'label': 'Call Type', 'fieldtype': 'Link', 'options': 'Telephony Call Type', - },{ + }, { 'fieldtype': 'Section Break', 'hide_border': 1, }, { diff --git a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py index 5abb3180df..944ffef36f 100644 --- a/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py +++ b/erpnext/telephony/doctype/telephony_call_type/telephony_call_type.py @@ -4,5 +4,6 @@ # import frappe from frappe.model.document import Document + class TelephonyCallType(Document): pass diff --git a/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py b/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py index c16d03e097..b3c19c3910 100644 --- a/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py +++ b/erpnext/telephony/doctype/telephony_call_type/test_telephony_call_type.py @@ -4,5 +4,6 @@ # import frappe import unittest + class TestTelephonyCallType(unittest.TestCase): pass From b19305aa8350c810535c94dc6af7ba0b9e2dde89 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Tue, 22 Mar 2022 16:22:09 +0530 Subject: [PATCH 08/18] fix: used get_employees_with_number, strip_number methods --- erpnext/hr/doctype/employee/employee.json | 7 +++-- .../telephony/doctype/call_log/call_log.py | 27 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index d592a9c79e..8a12f3b1d0 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -4,7 +4,7 @@ "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", - "creation": "2013-03-07 09:04:18", + "creation": "2022-02-21 11:54:09.632218", "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, @@ -813,11 +813,12 @@ "idx": 24, "image_field": "image", "links": [], - "modified": "2021-06-17 11:31:37.730760", + "modified": "2022-03-22 13:44:37.088519", "modified_by": "Administrator", "module": "HR", "name": "Employee", "name_case": "Title Case", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -857,7 +858,9 @@ ], "search_fields": "employee_name", "show_name_in_global_search": 1, + "show_title_field_in_link": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "title_field": "employee_name" } \ No newline at end of file diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 7b81a29fc1..f3c941ed9c 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -36,14 +36,11 @@ class CallLog(Document): # Add Employee Name if self.is_incoming_call(): # Taking the last 10 digits of the number - emp_number_reversed = (self.get("to"))[-1:-11:-1] - emp_number = emp_number_reversed[-1::-1] - employee = frappe.get_all("Employee", filters={ - "cell_number": ["like", "%"+emp_number+"%"] - }, fields=["first_name", "middle_name", "last_name", "user_id"]) + employee_number = strip_number(self.get('to')) + employee = get_employees_with_number(self.get("to")) - self.call_received_by = get_employee_name(employee[0]) - self.employee_user_id = employee[0].get("user_id") or '' + self.call_received_by = employee[0].get("name") + self.employee_user_id = employee[0].get("user_id") def after_insert(self): self.trigger_call_popup() @@ -78,7 +75,8 @@ class CallLog(Document): def trigger_call_popup(self): if self.is_incoming_call(): scheduled_employees = get_scheduled_employees_for_popup(self.medium) - employee_emails = get_employees_with_number(self.to) + employees = get_employees_with_number(self.to) + employee_emails = [employee.get("user_id") for employee in employees] # check if employees with matched number are scheduled to receive popup emails = set(scheduled_employees).intersection(employee_emails) @@ -115,18 +113,17 @@ def get_employees_with_number(number): number = strip_number(number) if not number: return [] - employee_emails = frappe.cache().hget('employees_with_number', number) - if employee_emails: return employee_emails + employee_doc_name_and_emails = frappe.cache().hget('employees_with_number', number) + if employee_doc_name_and_emails: return employee_doc_name_and_emails - employees = frappe.get_all('Employee', filters={ + employee_doc_name_and_emails = frappe.get_all('Employee', filters={ 'cell_number': ['like', '%{}%'.format(number)], 'user_id': ['!=', ''] - }, fields=['user_id']) + }, fields=['name', 'user_id']) - employee_emails = [employee.user_id for employee in employees] - frappe.cache().hset('employees_with_number', number, employee_emails) + frappe.cache().hset('employees_with_number', number, employee_doc_name_and_emails) - return employee_emails + return employee_doc_name_and_emails def link_existing_conversations(doc, state): """ From e57e7bb02ca1713ab63259d97a2f879bb5f40991 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Sat, 2 Apr 2022 17:16:56 +0530 Subject: [PATCH 09/18] fix: added tests(fixed) --- .pre-commit-config.yaml | 3 + .../telephony/doctype/call_log/call_log.py | 1 - erpnext/tests/test_exotel.py | 199 ++++++++++++++++++ 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 erpnext/tests/test_exotel.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cba6759f61..bc04b772c6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,6 +30,9 @@ repos: rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119 hooks: - id: black + additional_dependencies: [ + 'click==8.0.4' + ] - repo: https://github.com/timothycrosley/isort rev: 5.9.1 diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 21fa9c9ab4..7ef16d76fd 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -35,7 +35,6 @@ class CallLog(Document): # Add Employee Name if self.is_incoming_call(): # Taking the last 10 digits of the number - employee_number = strip_number(self.get("to")) employee = get_employees_with_number(self.get("to")) self.call_received_by = employee[0].get("name") diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py new file mode 100644 index 0000000000..91e8f50c3c --- /dev/null +++ b/erpnext/tests/test_exotel.py @@ -0,0 +1,199 @@ +import os +import time +import unittest + +import frappe +import requests +from frappe.contacts.doctype.contact.test_contact import create_contact + +from erpnext.hr.doctype.employee.test_employee import make_employee + + +class TestExotel(unittest.TestCase): + def setUp(self): + make_employee("test_employee_exotel@company.com", cell_number="9999999999") + phones = [{"phone": "+91 9999999991", "is_primary_phone": 0, "is_primary_mobile_no": 1}] + create_contact("Test Contact", "Mr", phones=phones) + + def test_for_successful_call(self): + data = { + "CallSid": "23c162077629863c1a2d7f29263a162m", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "Created": "Wed, 23 Feb 2022 12:31:59", + "From": "09999999991", + "To": "09999999988", + "CurrentTime": "2022-02-23 12:32:02", + "DialWhomNumber": "09999999999", + "Status": "busy", + "EventType": "Dial", + "AgentEmail": "test_employee_exotel@company.com", + } + end_call_data = { + "CallSid": "23c162077629863c1a2d7f29263a162m", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "ForwardedFrom": "null", + "Created": "Wed, 23 Feb 2022 12:31:59", + "DialCallDuration": "17", + "RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/exotelrecordings/erpnext/23c162077629863c1a2d7f29263a162n.mp3", + "StartTime": "2022-02-23 12:31:58", + "EndTime": "1970-01-01 05:30:00", + "DialCallStatus": "completed", + "CallType": "completed", + "DialWhomNumber": "09999999999", + "ProcessStatus": "null", + "flow_id": "228040", + "tenant_id": "67291", + "From": "09999999991", + "To": "09999999988", + "RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25", + "CurrentTime": "2022-02-23 12:32:25", + "OutgoingPhoneNumber": "09999999988", + "Legs": [ + { + "Number": "09999999999", + "Type": "single", + "OnCallDuration": "10", + "CallerId": "09999999980", + "CauseCode": "NORMAL_CLEARING", + "Cause": "16", + } + ], + } + api_method = "handle_incoming_call" + end_call_api_method = "handle_end_call" + emulate_api_call(data, api_method, end_call_data, end_call_api_method) + + frappe.reload_doctype("Call Log") + call_log = frappe.get_doc( + "Call Log", {"from": "09999999991", "to": "09999999999", "status": "Completed"} + ) + + self.assertEqual(call_log.get("from"), "09999999991") + self.assertEqual(call_log.get("to"), "09999999999") + self.assertEqual(call_log.get("call_received_by"), "EMP-00001") + self.assertEqual(call_log.get("status"), "Completed") + + def test_for_disconnected_call(self): + data = { + "CallSid": "d96421addce69e24bdc7ce5880d1162l", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "ForwardedFrom": "null", + "Created": "Mon, 21 Feb 2022 15:58:12", + "DialCallDuration": "0", + "StartTime": "2022-02-21 15:58:12", + "EndTime": "1970-01-01 05:30:00", + "DialCallStatus": "canceled", + "CallType": "client-hangup", + "DialWhomNumber": "09999999999", + "ProcessStatus": "null", + "flow_id": "228040", + "tenant_id": "67291", + "From": "09999999991", + "To": "09999999988", + "CurrentTime": "2022-02-21 15:58:47", + "OutgoingPhoneNumber": "09999999988", + "Legs": [ + { + "Number": "09999999999", + "Type": "single", + "OnCallDuration": "0", + "CallerId": "09999999980", + "CauseCode": "RING_TIMEOUT", + "Cause": "1003", + } + ], + } + api_method = "handle_missed_call" + emulate_api_call(data, api_method) + + frappe.reload_doctype("Call Log") + call_log = frappe.get_doc( + "Call Log", {"from": "09999999991", "to": "09999999999", "status": "Canceled"} + ) + + self.assertEqual(call_log.get("from"), "09999999991") + self.assertEqual(call_log.get("to"), "09999999999") + self.assertEqual(call_log.get("call_received_by"), "EMP-00001") + self.assertEqual(call_log.get("status"), "Canceled") + + def test_for_call_not_answered(self): + data = { + "CallSid": "fdb67a2b4b2d057b610a52ef43f81622", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "ForwardedFrom": "null", + "Created": "Mon, 21 Feb 2022 15:47:02", + "DialCallDuration": "0", + "StartTime": "2022-02-21 15:47:02", + "EndTime": "1970-01-01 05:30:00", + "DialCallStatus": "no-answer", + "CallType": "incomplete", + "DialWhomNumber": "09999999999", + "ProcessStatus": "null", + "flow_id": "228040", + "tenant_id": "67291", + "From": "09999999991", + "To": "09999999988", + "CurrentTime": "2022-02-21 15:47:40", + "OutgoingPhoneNumber": "09999999988", + "Legs": [ + { + "Number": "09999999999", + "Type": "single", + "OnCallDuration": "0", + "CallerId": "09999999980", + "CauseCode": "RING_TIMEOUT", + "Cause": "1003", + } + ], + } + api_method = "handle_missed_call" + emulate_api_call(data, api_method) + + frappe.reload_doctype("Call Log") + call_log = frappe.get_doc( + "Call Log", {"from": "09999999991", "to": "09999999999", "status": "No Answer"} + ) + + self.assertEqual(call_log.get("from"), "09999999991") + self.assertEqual(call_log.get("to"), "09999999999") + self.assertEqual(call_log.get("call_received_by"), "EMP-00001") + self.assertEqual(call_log.get("status"), "No Answer") + + def tearDown(self): + frappe.db.rollback() + + +def emulate_api_call(data, api_method, end_call_data=None, end_call_api_method=None): + # Build URL + port = frappe.get_site_config().webserver_port or "8000" + + if os.environ.get("CI"): + host = "localhost" + else: + host = frappe.local.site + + url = "http://{site}:{port}/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}".format( + site=host, port=port, api_method=api_method + ) + + if end_call_data: + end_call_url = "http://{site}:{port}/api/method/erpnext.erpnext_integrations.exotel_integration.{end_call_api_method}".format( + site=host, port=port, end_call_api_method=end_call_api_method + ) + + requests.post(url=url, data=data) + time.sleep(3) + + if end_call_data: + requests.post(url=end_call_url, data=end_call_data) + time.sleep(3) + + return From 0e9ebad9c6bc125ffbd0c23403a3601087282453 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 4 Apr 2022 07:23:08 +0530 Subject: [PATCH 10/18] test: Refactor exotel test setup - Remove unnecessary code - Move test data to separate file - Make proper test assertions --- erpnext/tests/exotel_test_data.py | 114 +++++++++++++++++++ erpnext/tests/test_exotel.py | 179 +++++------------------------- 2 files changed, 140 insertions(+), 153 deletions(-) create mode 100644 erpnext/tests/exotel_test_data.py diff --git a/erpnext/tests/exotel_test_data.py b/erpnext/tests/exotel_test_data.py new file mode 100644 index 0000000000..e2ae7f776e --- /dev/null +++ b/erpnext/tests/exotel_test_data.py @@ -0,0 +1,114 @@ +import frappe + +call_initiation_data = frappe._dict({ + "CallSid": "23c162077629863c1a2d7f29263a162m", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "Created": "Wed, 23 Feb 2022 12:31:59", + "From": "09999999991", + "To": "09999999988", + "CurrentTime": "2022-02-23 12:32:02", + "DialWhomNumber": "09999999999", + "Status": "busy", + "EventType": "Dial", + "AgentEmail": "test_employee_exotel@company.com", +}) + +call_end_data = frappe._dict({ + "CallSid": "23c162077629863c1a2d7f29263a162m", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "ForwardedFrom": "null", + "Created": "Wed, 23 Feb 2022 12:31:59", + "DialCallDuration": "17", + "RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/random.mp3", + "StartTime": "2022-02-23 12:31:58", + "EndTime": "1970-01-01 05:30:00", + "DialCallStatus": "completed", + "CallType": "completed", + "DialWhomNumber": "09999999999", + "ProcessStatus": "null", + "flow_id": "228040", + "tenant_id": "67291", + "From": "09999999991", + "To": "09999999988", + "RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25", + "CurrentTime": "2022-02-23 12:32:25", + "OutgoingPhoneNumber": "09999999988", + "Legs": [ + { + "Number": "09999999999", + "Type": "single", + "OnCallDuration": "10", + "CallerId": "09999999980", + "CauseCode": "NORMAL_CLEARING", + "Cause": "16", + } + ], +}) + +call_disconnected_data = frappe._dict({ + "CallSid": "d96421addce69e24bdc7ce5880d1162l", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "ForwardedFrom": "null", + "Created": "Mon, 21 Feb 2022 15:58:12", + "DialCallDuration": "0", + "StartTime": "2022-02-21 15:58:12", + "EndTime": "1970-01-01 05:30:00", + "DialCallStatus": "canceled", + "CallType": "client-hangup", + "DialWhomNumber": "09999999999", + "ProcessStatus": "null", + "flow_id": "228040", + "tenant_id": "67291", + "From": "09999999991", + "To": "09999999988", + "CurrentTime": "2022-02-21 15:58:47", + "OutgoingPhoneNumber": "09999999988", + "Legs": [ + { + "Number": "09999999999", + "Type": "single", + "OnCallDuration": "0", + "CallerId": "09999999980", + "CauseCode": "RING_TIMEOUT", + "Cause": "1003", + } + ], +}) + +call_not_answered_data = frappe._dict({ + "CallSid": "fdb67a2b4b2d057b610a52ef43f81622", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "ForwardedFrom": "null", + "Created": "Mon, 21 Feb 2022 15:47:02", + "DialCallDuration": "0", + "StartTime": "2022-02-21 15:47:02", + "EndTime": "1970-01-01 05:30:00", + "DialCallStatus": "no-answer", + "CallType": "incomplete", + "DialWhomNumber": "09999999999", + "ProcessStatus": "null", + "flow_id": "228040", + "tenant_id": "67291", + "From": "09999999991", + "To": "09999999988", + "CurrentTime": "2022-02-21 15:47:40", + "OutgoingPhoneNumber": "09999999988", + "Legs": [ + { + "Number": "09999999999", + "Type": "single", + "OnCallDuration": "0", + "CallerId": "09999999980", + "CauseCode": "RING_TIMEOUT", + "Cause": "1003", + } + ], +}) \ No newline at end of file diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py index 91e8f50c3c..d2e317ac6c 100644 --- a/erpnext/tests/test_exotel.py +++ b/erpnext/tests/test_exotel.py @@ -16,154 +16,42 @@ class TestExotel(unittest.TestCase): create_contact("Test Contact", "Mr", phones=phones) def test_for_successful_call(self): - data = { - "CallSid": "23c162077629863c1a2d7f29263a162m", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "Created": "Wed, 23 Feb 2022 12:31:59", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-23 12:32:02", - "DialWhomNumber": "09999999999", - "Status": "busy", - "EventType": "Dial", - "AgentEmail": "test_employee_exotel@company.com", - } - end_call_data = { - "CallSid": "23c162077629863c1a2d7f29263a162m", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Wed, 23 Feb 2022 12:31:59", - "DialCallDuration": "17", - "RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/exotelrecordings/erpnext/23c162077629863c1a2d7f29263a162n.mp3", - "StartTime": "2022-02-23 12:31:58", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "completed", - "CallType": "completed", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25", - "CurrentTime": "2022-02-23 12:32:25", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "10", - "CallerId": "09999999980", - "CauseCode": "NORMAL_CLEARING", - "Cause": "16", - } - ], - } + from .exotel_test_data import call_initiation_data, call_end_data api_method = "handle_incoming_call" end_call_api_method = "handle_end_call" - emulate_api_call(data, api_method, end_call_data, end_call_api_method) - frappe.reload_doctype("Call Log") - call_log = frappe.get_doc( - "Call Log", {"from": "09999999991", "to": "09999999999", "status": "Completed"} - ) + emulate_api_call(call_initiation_data, api_method) + emulate_api_call(call_end_data, end_call_api_method) self.assertEqual(call_log.get("from"), "09999999991") self.assertEqual(call_log.get("to"), "09999999999") + call_log = frappe.get_doc("Call Log", call_initiation_data.CallSid) + + self.assertEqual(call_log.get("from"), call_initiation_data.CallFrom) + self.assertEqual(call_log.get("to"), call_initiation_data.DialWhomNumber) self.assertEqual(call_log.get("call_received_by"), "EMP-00001") self.assertEqual(call_log.get("status"), "Completed") def test_for_disconnected_call(self): - data = { - "CallSid": "d96421addce69e24bdc7ce5880d1162l", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Mon, 21 Feb 2022 15:58:12", - "DialCallDuration": "0", - "StartTime": "2022-02-21 15:58:12", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "canceled", - "CallType": "client-hangup", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-21 15:58:47", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "0", - "CallerId": "09999999980", - "CauseCode": "RING_TIMEOUT", - "Cause": "1003", - } - ], - } + from .exotel_test_data import call_disconnected_data api_method = "handle_missed_call" - emulate_api_call(data, api_method) + emulate_api_call(call_disconnected_data, api_method) + call_log = frappe.get_doc("Call Log", call_disconnected_data.CallSid) - frappe.reload_doctype("Call Log") - call_log = frappe.get_doc( - "Call Log", {"from": "09999999991", "to": "09999999999", "status": "Canceled"} - ) - - self.assertEqual(call_log.get("from"), "09999999991") - self.assertEqual(call_log.get("to"), "09999999999") + self.assertEqual(call_log.get("from"), call_disconnected_data.CallFrom) + self.assertEqual(call_log.get("to"), call_disconnected_data.DialWhomNumber) self.assertEqual(call_log.get("call_received_by"), "EMP-00001") self.assertEqual(call_log.get("status"), "Canceled") def test_for_call_not_answered(self): - data = { - "CallSid": "fdb67a2b4b2d057b610a52ef43f81622", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Mon, 21 Feb 2022 15:47:02", - "DialCallDuration": "0", - "StartTime": "2022-02-21 15:47:02", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "no-answer", - "CallType": "incomplete", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-21 15:47:40", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "0", - "CallerId": "09999999980", - "CauseCode": "RING_TIMEOUT", - "Cause": "1003", - } - ], - } + from .exotel_test_data import call_not_answered_data api_method = "handle_missed_call" - emulate_api_call(data, api_method) + emulate_api_call(call_not_answered_data, api_method) - frappe.reload_doctype("Call Log") - call_log = frappe.get_doc( - "Call Log", {"from": "09999999991", "to": "09999999999", "status": "No Answer"} - ) + call_log = frappe.get_doc("Call Log", call_not_answered_data.CallSid) - self.assertEqual(call_log.get("from"), "09999999991") - self.assertEqual(call_log.get("to"), "09999999999") + self.assertEqual(call_log.get("from"), call_not_answered_data.CallFrom) + self.assertEqual(call_log.get("to"), call_not_answered_data.DialWhomNumber) self.assertEqual(call_log.get("call_received_by"), "EMP-00001") self.assertEqual(call_log.get("status"), "No Answer") @@ -171,29 +59,14 @@ class TestExotel(unittest.TestCase): frappe.db.rollback() -def emulate_api_call(data, api_method, end_call_data=None, end_call_api_method=None): +def emulate_api_call(data, api_method): # Build URL + url = get_exotel_handler_endpoint(api_method) + res = requests.post(url=url, data=frappe.as_json(data)) + res.raise_for_status() + time.sleep(1) + +def get_exotel_handler_endpoint(method): + site = "localhost" if os.environ.get("CI") else frappe.local.site port = frappe.get_site_config().webserver_port or "8000" - - if os.environ.get("CI"): - host = "localhost" - else: - host = frappe.local.site - - url = "http://{site}:{port}/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}".format( - site=host, port=port, api_method=api_method - ) - - if end_call_data: - end_call_url = "http://{site}:{port}/api/method/erpnext.erpnext_integrations.exotel_integration.{end_call_api_method}".format( - site=host, port=port, end_call_api_method=end_call_api_method - ) - - requests.post(url=url, data=data) - time.sleep(3) - - if end_call_data: - requests.post(url=end_call_url, data=end_call_data) - time.sleep(3) - - return + return f"http://{site}:{port}/api/method/erpnext.erpnext_integrations.exotel_integration.{method}" \ No newline at end of file From 39abfae5feb7ccbcdc22d5af75fe6ebe97820ffe Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 4 Apr 2022 07:28:41 +0530 Subject: [PATCH 11/18] chore: Remove unused code - and simplify get_call_log --- erpnext/erpnext_integrations/exotel_integration.py | 13 +++---------- erpnext/telephony/doctype/call_log/call_log.py | 8 -------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py index 82bcb8d0db..522de9ead8 100644 --- a/erpnext/erpnext_integrations/exotel_integration.py +++ b/erpnext/erpnext_integrations/exotel_integration.py @@ -68,16 +68,9 @@ def update_call_log(call_payload, status="Ringing", call_log=None): def get_call_log(call_payload): - call_log = frappe.get_all( - "Call Log", - { - "id": call_payload.get("CallSid"), - }, - limit=1, - ) - - if call_log: - return frappe.get_doc("Call Log", call_log[0].name) + call_log_id = call_payload.get("CallSid") + if frappe.db.exists("Call Log", call_log_id): + return frappe.get_doc("Call Log", call_log_id) def create_call_log(call_payload): diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 7ef16d76fd..e7fe7abfe9 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -95,14 +95,6 @@ class CallLog(Document): frappe.publish_realtime("show_call_popup", self, user=email) -def get_employee_name(emp): - employee_name = "" - for name in ["first_name", "middle_name", "last_name"]: - if emp.get(name): - employee_name += (" " if employee_name else "") + emp.get(name) - return employee_name - - @frappe.whitelist() def add_call_summary_and_call_type(call_log, summary, call_type): doc = frappe.get_doc("Call Log", call_log) From cfee53eb558b0cc474cdfd61205c06047f1178d0 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 4 Apr 2022 07:38:15 +0530 Subject: [PATCH 12/18] fix: Handle exception where no employee is returned --- erpnext/telephony/doctype/call_log/call_log.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index e7fe7abfe9..2092ec284b 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -35,10 +35,10 @@ class CallLog(Document): # Add Employee Name if self.is_incoming_call(): # Taking the last 10 digits of the number - employee = get_employees_with_number(self.get("to")) - - self.call_received_by = employee[0].get("name") - self.employee_user_id = employee[0].get("user_id") + employees = get_employees_with_number(self.get("to")) + if employees: + self.call_received_by = employees[0].get("name") + self.employee_user_id = employees[0].get("user_id") def after_insert(self): self.trigger_call_popup() From f4b8573e648e0ee3ab26fd79f700102536788c41 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 4 Apr 2022 09:31:15 +0530 Subject: [PATCH 13/18] test: Fix erroneous code --- erpnext/tests/exotel_test_data.py | 226 ++++++++++++++++-------------- erpnext/tests/test_exotel.py | 12 +- 2 files changed, 124 insertions(+), 114 deletions(-) diff --git a/erpnext/tests/exotel_test_data.py b/erpnext/tests/exotel_test_data.py index e2ae7f776e..3ad2575c23 100644 --- a/erpnext/tests/exotel_test_data.py +++ b/erpnext/tests/exotel_test_data.py @@ -1,114 +1,122 @@ import frappe -call_initiation_data = frappe._dict({ - "CallSid": "23c162077629863c1a2d7f29263a162m", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "Created": "Wed, 23 Feb 2022 12:31:59", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-23 12:32:02", - "DialWhomNumber": "09999999999", - "Status": "busy", - "EventType": "Dial", - "AgentEmail": "test_employee_exotel@company.com", -}) +call_initiation_data = frappe._dict( + { + "CallSid": "23c162077629863c1a2d7f29263a162m", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "Created": "Wed, 23 Feb 2022 12:31:59", + "From": "09999999991", + "To": "09999999988", + "CurrentTime": "2022-02-23 12:32:02", + "DialWhomNumber": "09999999999", + "Status": "busy", + "EventType": "Dial", + "AgentEmail": "test_employee_exotel@company.com", + } +) -call_end_data = frappe._dict({ - "CallSid": "23c162077629863c1a2d7f29263a162m", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Wed, 23 Feb 2022 12:31:59", - "DialCallDuration": "17", - "RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/random.mp3", - "StartTime": "2022-02-23 12:31:58", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "completed", - "CallType": "completed", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25", - "CurrentTime": "2022-02-23 12:32:25", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "10", - "CallerId": "09999999980", - "CauseCode": "NORMAL_CLEARING", - "Cause": "16", - } - ], -}) +call_end_data = frappe._dict( + { + "CallSid": "23c162077629863c1a2d7f29263a162m", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "ForwardedFrom": "null", + "Created": "Wed, 23 Feb 2022 12:31:59", + "DialCallDuration": "17", + "RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/random.mp3", + "StartTime": "2022-02-23 12:31:58", + "EndTime": "1970-01-01 05:30:00", + "DialCallStatus": "completed", + "CallType": "completed", + "DialWhomNumber": "09999999999", + "ProcessStatus": "null", + "flow_id": "228040", + "tenant_id": "67291", + "From": "09999999991", + "To": "09999999988", + "RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25", + "CurrentTime": "2022-02-23 12:32:25", + "OutgoingPhoneNumber": "09999999988", + "Legs": [ + { + "Number": "09999999999", + "Type": "single", + "OnCallDuration": "10", + "CallerId": "09999999980", + "CauseCode": "NORMAL_CLEARING", + "Cause": "16", + } + ], + } +) -call_disconnected_data = frappe._dict({ - "CallSid": "d96421addce69e24bdc7ce5880d1162l", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Mon, 21 Feb 2022 15:58:12", - "DialCallDuration": "0", - "StartTime": "2022-02-21 15:58:12", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "canceled", - "CallType": "client-hangup", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-21 15:58:47", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "0", - "CallerId": "09999999980", - "CauseCode": "RING_TIMEOUT", - "Cause": "1003", - } - ], -}) +call_disconnected_data = frappe._dict( + { + "CallSid": "d96421addce69e24bdc7ce5880d1162l", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "ForwardedFrom": "null", + "Created": "Mon, 21 Feb 2022 15:58:12", + "DialCallDuration": "0", + "StartTime": "2022-02-21 15:58:12", + "EndTime": "1970-01-01 05:30:00", + "DialCallStatus": "canceled", + "CallType": "client-hangup", + "DialWhomNumber": "09999999999", + "ProcessStatus": "null", + "flow_id": "228040", + "tenant_id": "67291", + "From": "09999999991", + "To": "09999999988", + "CurrentTime": "2022-02-21 15:58:47", + "OutgoingPhoneNumber": "09999999988", + "Legs": [ + { + "Number": "09999999999", + "Type": "single", + "OnCallDuration": "0", + "CallerId": "09999999980", + "CauseCode": "RING_TIMEOUT", + "Cause": "1003", + } + ], + } +) -call_not_answered_data = frappe._dict({ - "CallSid": "fdb67a2b4b2d057b610a52ef43f81622", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Mon, 21 Feb 2022 15:47:02", - "DialCallDuration": "0", - "StartTime": "2022-02-21 15:47:02", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "no-answer", - "CallType": "incomplete", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-21 15:47:40", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "0", - "CallerId": "09999999980", - "CauseCode": "RING_TIMEOUT", - "Cause": "1003", - } - ], -}) \ No newline at end of file +call_not_answered_data = frappe._dict( + { + "CallSid": "fdb67a2b4b2d057b610a52ef43f81622", + "CallFrom": "09999999991", + "CallTo": "09999999980", + "Direction": "incoming", + "ForwardedFrom": "null", + "Created": "Mon, 21 Feb 2022 15:47:02", + "DialCallDuration": "0", + "StartTime": "2022-02-21 15:47:02", + "EndTime": "1970-01-01 05:30:00", + "DialCallStatus": "no-answer", + "CallType": "incomplete", + "DialWhomNumber": "09999999999", + "ProcessStatus": "null", + "flow_id": "228040", + "tenant_id": "67291", + "From": "09999999991", + "To": "09999999988", + "CurrentTime": "2022-02-21 15:47:40", + "OutgoingPhoneNumber": "09999999988", + "Legs": [ + { + "Number": "09999999999", + "Type": "single", + "OnCallDuration": "0", + "CallerId": "09999999980", + "CauseCode": "RING_TIMEOUT", + "Cause": "1003", + } + ], + } +) diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py index d2e317ac6c..a5dc7dd211 100644 --- a/erpnext/tests/test_exotel.py +++ b/erpnext/tests/test_exotel.py @@ -16,15 +16,14 @@ class TestExotel(unittest.TestCase): create_contact("Test Contact", "Mr", phones=phones) def test_for_successful_call(self): - from .exotel_test_data import call_initiation_data, call_end_data + from .exotel_test_data import call_end_data, call_initiation_data + api_method = "handle_incoming_call" end_call_api_method = "handle_end_call" - emulate_api_call(call_initiation_data, api_method) emulate_api_call(call_end_data, end_call_api_method) - self.assertEqual(call_log.get("from"), "09999999991") - self.assertEqual(call_log.get("to"), "09999999999") + call_log = frappe.get_doc("Call Log", call_initiation_data.CallSid) self.assertEqual(call_log.get("from"), call_initiation_data.CallFrom) @@ -34,6 +33,7 @@ class TestExotel(unittest.TestCase): def test_for_disconnected_call(self): from .exotel_test_data import call_disconnected_data + api_method = "handle_missed_call" emulate_api_call(call_disconnected_data, api_method) call_log = frappe.get_doc("Call Log", call_disconnected_data.CallSid) @@ -45,6 +45,7 @@ class TestExotel(unittest.TestCase): def test_for_call_not_answered(self): from .exotel_test_data import call_not_answered_data + api_method = "handle_missed_call" emulate_api_call(call_not_answered_data, api_method) @@ -66,7 +67,8 @@ def emulate_api_call(data, api_method): res.raise_for_status() time.sleep(1) + def get_exotel_handler_endpoint(method): site = "localhost" if os.environ.get("CI") else frappe.local.site port = frappe.get_site_config().webserver_port or "8000" - return f"http://{site}:{port}/api/method/erpnext.erpnext_integrations.exotel_integration.{method}" \ No newline at end of file + return f"http://{site}:{port}/api/method/erpnext.erpnext_integrations.exotel_integration.{method}" From 40d33b5fecb0a6418fedb595cfddef2387eb881a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 4 Apr 2022 11:15:29 +0530 Subject: [PATCH 14/18] test: Use FrappeAPITestCase to track coverage --- erpnext/tests/test_exotel.py | 63 +++++++++++++++++------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py index a5dc7dd211..68c70ea344 100644 --- a/erpnext/tests/test_exotel.py +++ b/erpnext/tests/test_exotel.py @@ -1,19 +1,20 @@ -import os -import time -import unittest - import frappe -import requests from frappe.contacts.doctype.contact.test_contact import create_contact +from frappe.tests.test_api import FrappeAPITestCase from erpnext.hr.doctype.employee.test_employee import make_employee -class TestExotel(unittest.TestCase): - def setUp(self): - make_employee("test_employee_exotel@company.com", cell_number="9999999999") +class TestExotel(FrappeAPITestCase): + @classmethod + def setUpClass(cls): + cls.CURRENT_DB_CONNECTION = frappe.db + cls.test_employee_name = make_employee( + user="test_employee_exotel@company.com", cell_number="9999999999" + ) phones = [{"phone": "+91 9999999991", "is_primary_phone": 0, "is_primary_mobile_no": 1}] - create_contact("Test Contact", "Mr", phones=phones) + create_contact(name="Test Contact", salutation="Mr", phones=phones) + frappe.db.commit() def test_for_successful_call(self): from .exotel_test_data import call_end_data, call_initiation_data @@ -21,54 +22,48 @@ class TestExotel(unittest.TestCase): api_method = "handle_incoming_call" end_call_api_method = "handle_end_call" - emulate_api_call(call_initiation_data, api_method) - emulate_api_call(call_end_data, end_call_api_method) - + self.emulate_api_call_from_exotel(api_method, call_initiation_data) + self.emulate_api_call_from_exotel(end_call_api_method, call_end_data) call_log = frappe.get_doc("Call Log", call_initiation_data.CallSid) self.assertEqual(call_log.get("from"), call_initiation_data.CallFrom) self.assertEqual(call_log.get("to"), call_initiation_data.DialWhomNumber) - self.assertEqual(call_log.get("call_received_by"), "EMP-00001") + self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) self.assertEqual(call_log.get("status"), "Completed") def test_for_disconnected_call(self): from .exotel_test_data import call_disconnected_data api_method = "handle_missed_call" - emulate_api_call(call_disconnected_data, api_method) + self.emulate_api_call_from_exotel(api_method, call_disconnected_data) call_log = frappe.get_doc("Call Log", call_disconnected_data.CallSid) self.assertEqual(call_log.get("from"), call_disconnected_data.CallFrom) self.assertEqual(call_log.get("to"), call_disconnected_data.DialWhomNumber) - self.assertEqual(call_log.get("call_received_by"), "EMP-00001") + self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) self.assertEqual(call_log.get("status"), "Canceled") def test_for_call_not_answered(self): from .exotel_test_data import call_not_answered_data api_method = "handle_missed_call" - emulate_api_call(call_not_answered_data, api_method) - + self.emulate_api_call_from_exotel(api_method, call_not_answered_data) call_log = frappe.get_doc("Call Log", call_not_answered_data.CallSid) - self.assertEqual(call_log.get("from"), call_not_answered_data.CallFrom) self.assertEqual(call_log.get("to"), call_not_answered_data.DialWhomNumber) - self.assertEqual(call_log.get("call_received_by"), "EMP-00001") + self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) self.assertEqual(call_log.get("status"), "No Answer") - def tearDown(self): - frappe.db.rollback() + def emulate_api_call_from_exotel(self, api_method, data): + self.post( + f"/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}", + data=frappe.as_json(data), + content_type="application/json", + as_tuple=True, + ) + # restart db connection to get latest data + frappe.connect() - -def emulate_api_call(data, api_method): - # Build URL - url = get_exotel_handler_endpoint(api_method) - res = requests.post(url=url, data=frappe.as_json(data)) - res.raise_for_status() - time.sleep(1) - - -def get_exotel_handler_endpoint(method): - site = "localhost" if os.environ.get("CI") else frappe.local.site - port = frappe.get_site_config().webserver_port or "8000" - return f"http://{site}:{port}/api/method/erpnext.erpnext_integrations.exotel_integration.{method}" + @classmethod + def tearDownClass(cls): + frappe.db = cls.CURRENT_DB_CONNECTION From 01909d2e8cb081197fe7c67d9fb049b3930fa6dd Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 4 Apr 2022 20:21:36 +0530 Subject: [PATCH 15/18] test: Fix used frappe._dict to avoid AttributeError down the line ref: https://github.com/frappe/erpnext/runs/5816574721?check_suite_focus=true#step:12:880 --- .../doctype/quality_procedure/test_quality_procedure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py index daf7a694a3..a26ce2383d 100644 --- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py @@ -19,7 +19,7 @@ class TestQualityProcedure(unittest.TestCase): ) ).insert() - frappe.form_dict = dict( + frappe.form_dict = frappe._dict( doctype="Quality Procedure", quality_procedure_name="Test Child 1", parent_quality_procedure=procedure.name, From 7ff5bc9e3d62ffd0e57600f6383eb865e26ee28f Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 8 Apr 2022 21:33:29 +0530 Subject: [PATCH 16/18] test: Clean up form_dict To avoid failures like https://github.com/frappe/erpnext/runs/5887687369?check_suite_focus=true#step:12:783 --- erpnext/tests/test_exotel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py index 68c70ea344..67bc5bdbb8 100644 --- a/erpnext/tests/test_exotel.py +++ b/erpnext/tests/test_exotel.py @@ -8,6 +8,7 @@ from erpnext.hr.doctype.employee.test_employee import make_employee class TestExotel(FrappeAPITestCase): @classmethod def setUpClass(cls): + frappe.form_dict = frappe._dict() cls.CURRENT_DB_CONNECTION = frappe.db cls.test_employee_name = make_employee( user="test_employee_exotel@company.com", cell_number="9999999999" @@ -37,7 +38,6 @@ class TestExotel(FrappeAPITestCase): api_method = "handle_missed_call" self.emulate_api_call_from_exotel(api_method, call_disconnected_data) call_log = frappe.get_doc("Call Log", call_disconnected_data.CallSid) - self.assertEqual(call_log.get("from"), call_disconnected_data.CallFrom) self.assertEqual(call_log.get("to"), call_disconnected_data.DialWhomNumber) self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) From cef28df8db6ab14e4d243c1ea302bb5f4e57b528 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 13 Apr 2022 19:10:19 +0530 Subject: [PATCH 17/18] test: Do not overwrite frappe.form_dict to retain proxy reference Set frappe.local.form_dict instead --- .../doctype/request_for_quotation/test_request_for_quotation.py | 1 - .../doctype/quality_procedure/test_quality_procedure.py | 2 +- erpnext/tests/test_exotel.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py index dcdba095fb..064b806e95 100644 --- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py @@ -65,7 +65,6 @@ class TestRequestforQuotation(FrappeTestCase): ) sq.submit() - frappe.form_dict = frappe.local("form_dict") frappe.form_dict.name = rfq.name self.assertEqual(check_supplier_has_docname_access(supplier_wt_appos[0].get("supplier")), True) diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py index a26ce2383d..04e8211214 100644 --- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py @@ -19,7 +19,7 @@ class TestQualityProcedure(unittest.TestCase): ) ).insert() - frappe.form_dict = frappe._dict( + frappe.local.form_dict = frappe._dict( doctype="Quality Procedure", quality_procedure_name="Test Child 1", parent_quality_procedure=procedure.name, diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py index 67bc5bdbb8..bb916a8092 100644 --- a/erpnext/tests/test_exotel.py +++ b/erpnext/tests/test_exotel.py @@ -8,7 +8,6 @@ from erpnext.hr.doctype.employee.test_employee import make_employee class TestExotel(FrappeAPITestCase): @classmethod def setUpClass(cls): - frappe.form_dict = frappe._dict() cls.CURRENT_DB_CONNECTION = frappe.db cls.test_employee_name = make_employee( user="test_employee_exotel@company.com", cell_number="9999999999" From ff41b8da4e3ae40b07847193c0ad40848feb9ced Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 13 Apr 2022 20:12:08 +0530 Subject: [PATCH 18/18] fix: Update received_by if "to" is changed --- erpnext/telephony/doctype/call_log/call_log.py | 16 ++++++++++------ erpnext/tests/test_exotel.py | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 2092ec284b..7725e71f19 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -34,11 +34,7 @@ class CallLog(Document): # Add Employee Name if self.is_incoming_call(): - # Taking the last 10 digits of the number - employees = get_employees_with_number(self.get("to")) - if employees: - self.call_received_by = employees[0].get("name") - self.employee_user_id = employees[0].get("user_id") + self.update_received_by() def after_insert(self): self.trigger_call_popup() @@ -57,6 +53,9 @@ class CallLog(Document): if not doc_before_save: return + if self.is_incoming_call() and self.has_value_changed("to"): + self.update_received_by() + if _is_call_missed(doc_before_save, self): frappe.publish_realtime("call_{id}_missed".format(id=self.id), self) self.trigger_call_popup() @@ -94,6 +93,11 @@ class CallLog(Document): for email in emails: frappe.publish_realtime("show_call_popup", self, user=email) + def update_received_by(self): + if employees := get_employees_with_number(self.get("to")): + self.call_received_by = employees[0].get("name") + self.employee_user_id = employees[0].get("user_id") + @frappe.whitelist() def add_call_summary_and_call_type(call_log, summary, call_type): @@ -114,7 +118,7 @@ def get_employees_with_number(number): employee_doc_name_and_emails = frappe.get_all( "Employee", - filters={"cell_number": ["like", "%{}%".format(number)], "user_id": ["!=", ""]}, + filters={"cell_number": ["like", f"%{number}%"], "user_id": ["!=", ""]}, fields=["name", "user_id"], ) diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py index bb916a8092..76bbb3e05a 100644 --- a/erpnext/tests/test_exotel.py +++ b/erpnext/tests/test_exotel.py @@ -12,6 +12,7 @@ class TestExotel(FrappeAPITestCase): cls.test_employee_name = make_employee( user="test_employee_exotel@company.com", cell_number="9999999999" ) + frappe.db.set_value("Exotel Settings", "Exotel Settings", "enabled", 1) phones = [{"phone": "+91 9999999991", "is_primary_phone": 0, "is_primary_mobile_no": 1}] create_contact(name="Test Contact", salutation="Mr", phones=phones) frappe.db.commit()