diff --git a/crm/api/session.py b/crm/api/session.py
index 2e2b93758..746de854e 100644
--- a/crm/api/session.py
+++ b/crm/api/session.py
@@ -5,7 +5,16 @@
def get_users():
users = frappe.qb.get_query(
"User",
- fields=["name", "email", "enabled", "user_image", "first_name", "last_name", "full_name", "user_type"],
+ fields=[
+ "name",
+ "email",
+ "enabled",
+ "user_image",
+ "first_name",
+ "last_name",
+ "full_name",
+ "user_type",
+ ],
order_by="full_name asc",
distinct=True,
).run(as_dict=1)
@@ -14,11 +23,13 @@ def get_users():
if frappe.session.user == user.name:
user.session_user = True
- user.is_manager = (
- "Sales Manager" in frappe.get_roles(user.name) or user.name == "Administrator"
- )
+ user.is_manager = "Sales Manager" in frappe.get_roles(user.name) or user.name == "Administrator"
+
+ user.is_agent = frappe.db.exists("CRM Telephony Agent", {"user": user.name})
+
return users
+
@frappe.whitelist()
def get_contacts():
contacts = frappe.get_all(
@@ -37,7 +48,7 @@ def get_contacts():
"mobile_no",
"phone",
"company_name",
- "modified"
+ "modified",
],
order_by="first_name asc",
distinct=True,
@@ -58,18 +69,12 @@ def get_contacts():
return contacts
+
@frappe.whitelist()
def get_lead_contacts():
lead_contacts = frappe.get_all(
"CRM Lead",
- fields=[
- "name",
- "lead_name",
- "mobile_no",
- "phone",
- "image",
- "modified"
- ],
+ fields=["name", "lead_name", "mobile_no", "phone", "image", "modified"],
filters={"converted": 0},
order_by="lead_name asc",
distinct=True,
@@ -77,11 +82,12 @@ def get_lead_contacts():
return lead_contacts
+
@frappe.whitelist()
def get_organizations():
organizations = frappe.qb.get_query(
"CRM Organization",
- fields=['*'],
+ fields=["*"],
order_by="name asc",
distinct=True,
).run(as_dict=1)
diff --git a/crm/fcrm/doctype/crm_exotel_agent/__init__.py b/crm/fcrm/doctype/crm_telephony_agent/__init__.py
similarity index 100%
rename from crm/fcrm/doctype/crm_exotel_agent/__init__.py
rename to crm/fcrm/doctype/crm_telephony_agent/__init__.py
diff --git a/crm/fcrm/doctype/crm_exotel_agent/crm_exotel_agent.js b/crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.js
similarity index 77%
rename from crm/fcrm/doctype/crm_exotel_agent/crm_exotel_agent.js
rename to crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.js
index 78f3c9bbf..b1bea1732 100644
--- a/crm/fcrm/doctype/crm_exotel_agent/crm_exotel_agent.js
+++ b/crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.js
@@ -1,7 +1,7 @@
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
-// frappe.ui.form.on("CRM Exotel Agent", {
+// frappe.ui.form.on("CRM Telephony Agent", {
// refresh(frm) {
// },
diff --git a/crm/fcrm/doctype/crm_exotel_agent/crm_exotel_agent.json b/crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.json
similarity index 51%
rename from crm/fcrm/doctype/crm_exotel_agent/crm_exotel_agent.json
rename to crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.json
index c9baa7851..042632f69 100644
--- a/crm/fcrm/doctype/crm_exotel_agent/crm_exotel_agent.json
+++ b/crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.json
@@ -1,7 +1,7 @@
{
"actions": [],
"allow_rename": 1,
- "autoname": "field:mobile_no",
+ "autoname": "field:user",
"creation": "2025-01-11 16:12:46.602782",
"doctype": "DocType",
"engine": "InnoDB",
@@ -10,7 +10,15 @@
"user_name",
"column_break_hdec",
"mobile_no",
- "exotel_number"
+ "default_medium",
+ "section_break_ozjn",
+ "twilio",
+ "twilio_number",
+ "column_break_aydj",
+ "exotel",
+ "exotel_number",
+ "section_break_phlq",
+ "phone_nos"
],
"fields": [
{
@@ -20,7 +28,8 @@
"in_standard_filter": 1,
"label": "User",
"options": "User",
- "reqd": 1
+ "reqd": 1,
+ "unique": 1
},
{
"fieldname": "column_break_hdec",
@@ -32,8 +41,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Mobile No.",
- "reqd": 1,
- "unique": 1
+ "read_only": 1
},
{
"fetch_from": "user.full_name",
@@ -44,19 +52,62 @@
"label": "User Name"
},
{
+ "depends_on": "exotel",
"fieldname": "exotel_number",
"fieldtype": "Data",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Exotel Number"
+ "label": "Exotel Number",
+ "mandatory_depends_on": "exotel"
+ },
+ {
+ "fieldname": "section_break_phlq",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "section_break_ozjn",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_aydj",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "twilio",
+ "fieldname": "twilio_number",
+ "fieldtype": "Data",
+ "label": "Twilio Number",
+ "mandatory_depends_on": "twilio"
+ },
+ {
+ "fieldname": "phone_nos",
+ "fieldtype": "Table",
+ "label": "Phone Numbers",
+ "options": "CRM Telephony Phone"
+ },
+ {
+ "default": "0",
+ "fieldname": "twilio",
+ "fieldtype": "Check",
+ "label": "Twilio"
+ },
+ {
+ "default": "0",
+ "fieldname": "exotel",
+ "fieldtype": "Check",
+ "label": "Exotel"
+ },
+ {
+ "fieldname": "default_medium",
+ "fieldtype": "Select",
+ "label": "Default Medium",
+ "options": "\nTwilio\nExotel"
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2025-01-15 20:03:31.162162",
+ "modified": "2025-01-19 14:17:12.880185",
"modified_by": "Administrator",
"module": "FCRM",
- "name": "CRM Exotel Agent",
+ "name": "CRM Telephony Agent",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
diff --git a/crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.py b/crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.py
new file mode 100644
index 000000000..ff1f85e9b
--- /dev/null
+++ b/crm/fcrm/doctype/crm_telephony_agent/crm_telephony_agent.py
@@ -0,0 +1,34 @@
+# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+
+
+class CRMTelephonyAgent(Document):
+ def validate(self):
+ self.set_primary()
+
+ def set_primary(self):
+ # Used to set primary mobile no.
+ if len(self.phone_nos) == 0:
+ self.mobile_no = ""
+ return
+
+ is_primary = [phone.number for phone in self.phone_nos if phone.get("is_primary")]
+
+ if len(is_primary) > 1:
+ frappe.throw(
+ _("Only one {0} can be set as primary.").format(frappe.bold(frappe.unscrub("mobile_no")))
+ )
+
+ primary_number_exists = False
+ for d in self.phone_nos:
+ if d.get("is_primary") == 1:
+ primary_number_exists = True
+ self.mobile_no = d.number
+ break
+
+ if not primary_number_exists:
+ self.mobile_no = ""
diff --git a/crm/fcrm/doctype/crm_exotel_agent/test_crm_exotel_agent.py b/crm/fcrm/doctype/crm_telephony_agent/test_crm_telephony_agent.py
similarity index 77%
rename from crm/fcrm/doctype/crm_exotel_agent/test_crm_exotel_agent.py
rename to crm/fcrm/doctype/crm_telephony_agent/test_crm_telephony_agent.py
index 9fe610ce7..63e0f2fb7 100644
--- a/crm/fcrm/doctype/crm_exotel_agent/test_crm_exotel_agent.py
+++ b/crm/fcrm/doctype/crm_telephony_agent/test_crm_telephony_agent.py
@@ -4,7 +4,6 @@
# import frappe
from frappe.tests import IntegrationTestCase, UnitTestCase
-
# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
# Use these module variables to add/remove to/from that list
@@ -12,18 +11,18 @@
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
-class UnitTestCRMExotelAgent(UnitTestCase):
+class UnitTestCRMTelephonyAgent(UnitTestCase):
"""
- Unit tests for CRMExotelAgent.
+ Unit tests for CRMTelephonyAgent.
Use this class for testing individual functions and methods.
"""
pass
-class IntegrationTestCRMExotelAgent(IntegrationTestCase):
+class IntegrationTestCRMTelephonyAgent(IntegrationTestCase):
"""
- Integration tests for CRMExotelAgent.
+ Integration tests for CRMTelephonyAgent.
Use this class for testing interactions between multiple components.
"""
diff --git a/crm/fcrm/doctype/twilio_agents/__init__.py b/crm/fcrm/doctype/crm_telephony_phone/__init__.py
similarity index 100%
rename from crm/fcrm/doctype/twilio_agents/__init__.py
rename to crm/fcrm/doctype/crm_telephony_phone/__init__.py
diff --git a/crm/fcrm/doctype/crm_telephony_phone/crm_telephony_phone.json b/crm/fcrm/doctype/crm_telephony_phone/crm_telephony_phone.json
new file mode 100644
index 000000000..6450a96a8
--- /dev/null
+++ b/crm/fcrm/doctype/crm_telephony_phone/crm_telephony_phone.json
@@ -0,0 +1,40 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-01-19 13:57:01.702519",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "number",
+ "is_primary"
+ ],
+ "fields": [
+ {
+ "fieldname": "number",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Number",
+ "reqd": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "is_primary",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "label": "Is Primary"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-01-19 13:58:59.063775",
+ "modified_by": "Administrator",
+ "module": "FCRM",
+ "name": "CRM Telephony Phone",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/crm/fcrm/doctype/crm_exotel_agent/crm_exotel_agent.py b/crm/fcrm/doctype/crm_telephony_phone/crm_telephony_phone.py
similarity index 84%
rename from crm/fcrm/doctype/crm_exotel_agent/crm_exotel_agent.py
rename to crm/fcrm/doctype/crm_telephony_phone/crm_telephony_phone.py
index 05fa7ee74..5522b84db 100644
--- a/crm/fcrm/doctype/crm_exotel_agent/crm_exotel_agent.py
+++ b/crm/fcrm/doctype/crm_telephony_phone/crm_telephony_phone.py
@@ -5,5 +5,5 @@
from frappe.model.document import Document
-class CRMExotelAgent(Document):
+class CRMTelephonyPhone(Document):
pass
diff --git a/crm/fcrm/doctype/twilio_settings/__init__.py b/crm/fcrm/doctype/crm_twilio_settings/__init__.py
similarity index 100%
rename from crm/fcrm/doctype/twilio_settings/__init__.py
rename to crm/fcrm/doctype/crm_twilio_settings/__init__.py
diff --git a/crm/fcrm/doctype/twilio_settings/twilio_settings.js b/crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.js
similarity index 77%
rename from crm/fcrm/doctype/twilio_settings/twilio_settings.js
rename to crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.js
index 9837c18d4..1bfa5e446 100644
--- a/crm/fcrm/doctype/twilio_settings/twilio_settings.js
+++ b/crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.js
@@ -1,7 +1,7 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
-// frappe.ui.form.on("Twilio Settings", {
+// frappe.ui.form.on("CRM Twilio Settings", {
// refresh(frm) {
// },
diff --git a/crm/fcrm/doctype/twilio_settings/twilio_settings.json b/crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.json
similarity index 98%
rename from crm/fcrm/doctype/twilio_settings/twilio_settings.json
rename to crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.json
index 17e92cfbd..d898b631e 100644
--- a/crm/fcrm/doctype/twilio_settings/twilio_settings.json
+++ b/crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.json
@@ -108,7 +108,7 @@
"modified": "2025-01-15 19:35:13.406254",
"modified_by": "Administrator",
"module": "FCRM",
- "name": "Twilio Settings",
+ "name": "CRM Twilio Settings",
"owner": "Administrator",
"permissions": [
{
diff --git a/crm/fcrm/doctype/twilio_settings/twilio_settings.py b/crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.py
similarity index 76%
rename from crm/fcrm/doctype/twilio_settings/twilio_settings.py
rename to crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.py
index 1d3a20b63..f2737c6ce 100644
--- a/crm/fcrm/doctype/twilio_settings/twilio_settings.py
+++ b/crm/fcrm/doctype/crm_twilio_settings/crm_twilio_settings.py
@@ -2,13 +2,13 @@
# For license information, please see license.txt
import frappe
-from frappe.model.document import Document
from frappe import _
-
+from frappe.model.document import Document
from twilio.rest import Client
-class TwilioSettings(Document):
- friendly_resource_name = "Frappe CRM" # System creates TwiML app & API keys with this name.
+
+class CRMTwilioSettings(Document):
+ friendly_resource_name = "Frappe CRM" # System creates TwiML app & API keys with this name.
def validate(self):
self.validate_twilio_account()
@@ -33,28 +33,26 @@ def validate_twilio_account(self):
frappe.throw(_("Invalid Account SID or Auth Token."))
def set_api_credentials(self, twilio):
- """Generate Twilio API credentials if not exist and update them.
- """
+ """Generate Twilio API credentials if not exist and update them."""
if self.api_key and self.api_secret:
return
new_key = self.create_api_key(twilio)
self.api_key = new_key.sid
self.api_secret = new_key.secret
- frappe.db.set_value('Twilio Settings', 'Twilio Settings', {
- 'api_key': self.api_key,
- 'api_secret': self.api_secret
- })
+ frappe.db.set_value(
+ "CRM Twilio Settings",
+ "CRM Twilio Settings",
+ {"api_key": self.api_key, "api_secret": self.api_secret},
+ )
def set_application_credentials(self, twilio):
- """Generate TwiML app credentials if not exist and update them.
- """
+ """Generate TwiML app credentials if not exist and update them."""
credentials = self.get_application(twilio) or self.create_application(twilio)
self.twiml_sid = credentials.sid
- frappe.db.set_value('Twilio Settings', 'Twilio Settings', 'twiml_sid', self.twiml_sid)
+ frappe.db.set_value("CRM Twilio Settings", "CRM Twilio Settings", "twiml_sid", self.twiml_sid)
def create_api_key(self, twilio):
- """Create API keys in twilio account.
- """
+ """Create API keys in twilio account."""
try:
return twilio.new_keys.create(friendly_name=self.friendly_resource_name)
except Exception:
@@ -66,23 +64,21 @@ def get_twilio_voice_url(self):
return get_public_url(url_path)
def get_application(self, twilio, friendly_name=None):
- """Get TwiML App from twilio account if exists.
- """
+ """Get TwiML App from twilio account if exists."""
friendly_name = friendly_name or self.friendly_resource_name
applications = twilio.applications.list(friendly_name)
return applications and applications[0]
def create_application(self, twilio, friendly_name=None):
- """Create TwilML App in twilio account.
- """
+ """Create TwilML App in twilio account."""
friendly_name = friendly_name or self.friendly_resource_name
application = twilio.applications.create(
- voice_method='POST',
- voice_url=self.get_twilio_voice_url(),
- friendly_name=friendly_name
- )
+ voice_method="POST", voice_url=self.get_twilio_voice_url(), friendly_name=friendly_name
+ )
return application
-def get_public_url(path: str=None):
+
+def get_public_url(path: str | None = None):
from frappe.utils import get_url
- return get_url().split(":8", 1)[0] + path
\ No newline at end of file
+
+ return get_url().split(":8", 1)[0] + path
diff --git a/crm/fcrm/doctype/twilio_agents/test_twilio_agents.py b/crm/fcrm/doctype/crm_twilio_settings/test_crm_twilio_settings.py
similarity index 77%
rename from crm/fcrm/doctype/twilio_agents/test_twilio_agents.py
rename to crm/fcrm/doctype/crm_twilio_settings/test_crm_twilio_settings.py
index 29ecc305a..531076afc 100644
--- a/crm/fcrm/doctype/twilio_agents/test_twilio_agents.py
+++ b/crm/fcrm/doctype/crm_twilio_settings/test_crm_twilio_settings.py
@@ -5,5 +5,5 @@
from frappe.tests import UnitTestCase
-class TestTwilioAgents(UnitTestCase):
+class TestCRMTwilioSettings(UnitTestCase):
pass
diff --git a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json
index 0a902c992..f445541d1 100644
--- a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json
+++ b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json
@@ -12,9 +12,7 @@
"brand_logo",
"favicon",
"dropdown_items_tab",
- "dropdown_items",
- "calling_tab",
- "default_calling_medium"
+ "dropdown_items"
],
"fields": [
{
@@ -58,23 +56,12 @@
"fieldname": "favicon",
"fieldtype": "Attach",
"label": "Favicon"
- },
- {
- "fieldname": "calling_tab",
- "fieldtype": "Tab Break",
- "label": "Calling"
- },
- {
- "fieldname": "default_calling_medium",
- "fieldtype": "Select",
- "label": "Default calling medium",
- "options": "\nTwilio\nExotel"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2025-01-15 17:40:32.784762",
+ "modified": "2025-01-19 14:23:05.981355",
"modified_by": "Administrator",
"module": "FCRM",
"name": "FCRM Settings",
diff --git a/crm/fcrm/doctype/twilio_agents/twilio_agents.js b/crm/fcrm/doctype/twilio_agents/twilio_agents.js
deleted file mode 100644
index 8d8bf2944..000000000
--- a/crm/fcrm/doctype/twilio_agents/twilio_agents.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-// frappe.ui.form.on("Twilio Agents", {
-// refresh(frm) {
-
-// },
-// });
diff --git a/crm/fcrm/doctype/twilio_agents/twilio_agents.json b/crm/fcrm/doctype/twilio_agents/twilio_agents.json
deleted file mode 100644
index 652511304..000000000
--- a/crm/fcrm/doctype/twilio_agents/twilio_agents.json
+++ /dev/null
@@ -1,78 +0,0 @@
-{
- "actions": [],
- "allow_rename": 1,
- "autoname": "field:user",
- "creation": "2023-08-17 19:59:56.239729",
- "default_view": "List",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "user",
- "user_name",
- "call_receiving_device",
- "column_break_ljne",
- "twilio_number"
- ],
- "fields": [
- {
- "fieldname": "user",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "User",
- "options": "User",
- "unique": 1
- },
- {
- "fieldname": "column_break_ljne",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "twilio_number",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Twilio Number",
- "options": "Phone"
- },
- {
- "fetch_from": "user.full_name",
- "fieldname": "user_name",
- "fieldtype": "Data",
- "label": "User Name",
- "read_only": 1
- },
- {
- "default": "Computer",
- "fieldname": "call_receiving_device",
- "fieldtype": "Select",
- "label": "Device",
- "options": "Computer\nPhone"
- }
- ],
- "index_web_pages_for_search": 1,
- "links": [],
- "modified": "2024-01-19 21:57:18.626669",
- "modified_by": "Administrator",
- "module": "FCRM",
- "name": "Twilio Agents",
- "naming_rule": "By fieldname",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Sales Manager",
- "share": 1,
- "write": 1
- }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "states": [],
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/crm/fcrm/doctype/twilio_agents/twilio_agents.py b/crm/fcrm/doctype/twilio_agents/twilio_agents.py
deleted file mode 100644
index fb660dd87..000000000
--- a/crm/fcrm/doctype/twilio_agents/twilio_agents.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# 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 TwilioAgents(Document):
- pass
diff --git a/crm/fcrm/doctype/twilio_settings/test_twilio_settings.py b/crm/fcrm/doctype/twilio_settings/test_twilio_settings.py
deleted file mode 100644
index 21b038410..000000000
--- a/crm/fcrm/doctype/twilio_settings/test_twilio_settings.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-# import frappe
-from frappe.tests import UnitTestCase
-
-
-class TestTwilioSettings(UnitTestCase):
- pass
diff --git a/crm/integrations/api.py b/crm/integrations/api.py
index 145bf03a0..7bb15f898 100644
--- a/crm/integrations/api.py
+++ b/crm/integrations/api.py
@@ -7,20 +7,42 @@
@frappe.whitelist()
def is_call_integration_enabled():
- twilio_enabled = frappe.db.get_single_value("Twilio Settings", "enabled")
+ twilio_enabled = frappe.db.get_single_value("CRM Twilio Settings", "enabled")
exotel_enabled = frappe.db.get_single_value("CRM Exotel Settings", "enabled")
- default_calling_medium = frappe.db.get_single_value("FCRM Settings", "default_calling_medium")
return {
"twilio_enabled": twilio_enabled,
"exotel_enabled": exotel_enabled,
- "default_calling_medium": default_calling_medium,
+ "default_calling_medium": get_user_default_calling_medium(),
}
+def get_user_default_calling_medium():
+ if not frappe.db.exists("CRM Telephony Agent", frappe.session.user):
+ return None
+
+ default_medium = frappe.db.get_value("CRM Telephony Agent", frappe.session.user, "default_medium")
+
+ if not default_medium:
+ return None
+
+ return default_medium
+
+
@frappe.whitelist()
def set_default_calling_medium(medium):
- return frappe.db.set_value("FCRM Settings", "FCRM Settings", "default_calling_medium", medium)
+ if not frappe.db.exists("CRM Telephony Agent", frappe.session.user):
+ frappe.get_doc(
+ {
+ "doctype": "CRM Telephony Agent",
+ "agent": frappe.session.user,
+ "default_medium": medium,
+ }
+ ).insert(ignore_permissions=True)
+ else:
+ frappe.db.set_value("CRM Telephony Agent", frappe.session.user, "default_medium", medium)
+
+ return get_user_default_calling_medium()
@frappe.whitelist()
diff --git a/crm/integrations/exotel/handler.py b/crm/integrations/exotel/handler.py
index 1f01837c7..7d3301a8c 100644
--- a/crm/integrations/exotel/handler.py
+++ b/crm/integrations/exotel/handler.py
@@ -67,17 +67,22 @@ def make_a_call(to_number, from_number=None, caller_id=None):
endpoint = get_exotel_endpoint("Calls/connect.json?details=true")
if not from_number:
- from_number = frappe.get_value("CRM Exotel Agent", {"user": frappe.session.user}, "mobile_no")
+ from_number = frappe.get_value("CRM Telephony Agent", {"user": frappe.session.user}, "mobile_no")
if not caller_id:
- caller_id = frappe.get_value("CRM Exotel Agent", {"user": frappe.session.user}, "exotel_number")
+ caller_id = frappe.get_value("CRM Telephony Agent", {"user": frappe.session.user}, "exotel_number")
+
+ if not caller_id:
+ frappe.throw(
+ _("You do not have Exotel Number set in your Telephony Agent"), title=_("Exotel Number Missing")
+ )
if caller_id and caller_id not in get_all_exophones():
frappe.throw(_("Exotel Number {0} is not valid").format(caller_id), title=_("Invalid Exotel Number"))
if not from_number:
frappe.throw(
- _("You do not have mobile number set in your Exotel Agent"), title=_("Mobile Number Missing")
+ _("You do not have mobile number set in your Telephony Agent"), title=_("Mobile Number Missing")
)
record_call = frappe.db.get_single_value("CRM Exotel Settings", "record_call")
diff --git a/crm/integrations/twilio/api.py b/crm/integrations/twilio/api.py
index e78e0214b..0df049ae6 100644
--- a/crm/integrations/twilio/api.py
+++ b/crm/integrations/twilio/api.py
@@ -11,7 +11,7 @@
@frappe.whitelist()
def is_enabled():
- return frappe.db.get_single_value("Twilio Settings", "enabled")
+ return frappe.db.get_single_value("CRM Twilio Settings", "enabled")
@frappe.whitelist()
diff --git a/crm/integrations/twilio/twilio_handler.py b/crm/integrations/twilio/twilio_handler.py
index b24e0e9d3..2d6b7df1f 100644
--- a/crm/integrations/twilio/twilio_handler.py
+++ b/crm/integrations/twilio/twilio_handler.py
@@ -14,7 +14,7 @@ class Twilio:
def __init__(self, settings):
"""
- :param settings: `Twilio Settings` doctype
+ :param settings: `CRM Twilio Settings` doctype
"""
self.settings = settings
self.account_sid = settings.account_sid
@@ -26,7 +26,7 @@ def __init__(self, settings):
@classmethod
def connect(self):
"""Make a twilio connection."""
- settings = frappe.get_doc("Twilio Settings")
+ settings = frappe.get_doc("CRM Twilio Settings")
if not (settings and settings.enabled):
return
return Twilio(settings=settings)
@@ -114,11 +114,11 @@ def generate_twilio_client_response(self, client, ring_tone="at"):
@classmethod
def get_twilio_client(self):
- twilio_settings = frappe.get_doc("Twilio Settings")
+ twilio_settings = frappe.get_doc("CRM Twilio Settings")
if not twilio_settings.enabled:
frappe.throw(_("Please enable twilio settings before making a call."))
- auth_token = get_decrypted_password("Twilio Settings", "Twilio Settings", "auth_token")
+ auth_token = get_decrypted_password("CRM Twilio Settings", "CRM Twilio Settings", "auth_token")
client = TwilioClient(twilio_settings.account_sid, auth_token)
return client
diff --git a/crm/patches.txt b/crm/patches.txt
index f964d13fb..f8fbb4c6d 100644
--- a/crm/patches.txt
+++ b/crm/patches.txt
@@ -2,6 +2,7 @@
# Patches added in this section will be executed before doctypes are migrated
# Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations
crm.patches.v1_0.move_crm_note_data_to_fcrm_note
+crm.patches.v1_0.rename_twilio_settings_to_crm_twilio_settings
[post_model_sync]
# Patches added in this section will be executed after doctypes are migrated
@@ -9,4 +10,5 @@ crm.patches.v1_0.create_email_template_custom_fields
crm.patches.v1_0.create_default_fields_layout #10/12/2024
crm.patches.v1_0.create_default_sidebar_fields_layout
crm.patches.v1_0.update_deal_quick_entry_layout
-crm.patches.v1_0.update_layouts_to_new_format
\ No newline at end of file
+crm.patches.v1_0.update_layouts_to_new_format
+crm.patches.v1_0.move_twilio_agent_to_telephony_agent
\ No newline at end of file
diff --git a/crm/patches/v1_0/move_twilio_agent_to_telephony_agent.py b/crm/patches/v1_0/move_twilio_agent_to_telephony_agent.py
new file mode 100644
index 000000000..7049ab628
--- /dev/null
+++ b/crm/patches/v1_0/move_twilio_agent_to_telephony_agent.py
@@ -0,0 +1,27 @@
+import frappe
+
+
+def execute():
+ if not frappe.db.exists("DocType", "CRM Telephony Agent"):
+ frappe.reload_doctype("CRM Telephony Agent", force=True)
+
+ if frappe.db.exists("DocType", "Twilio Agents") and frappe.db.count("Twilio Agents") == 0:
+ return
+
+ agents = frappe.db.sql("SELECT * FROM `tabTwilio Agents`", as_dict=True)
+ if agents:
+ for agent in agents:
+ doc = frappe.get_doc(
+ {
+ "doctype": "CRM Telephony Agent",
+ "creation": agent.get("creation"),
+ "modified": agent.get("modified"),
+ "modified_by": agent.get("modified_by"),
+ "owner": agent.get("owner"),
+ "user": agent.get("user"),
+ "twilio_number": agent.get("twilio_number"),
+ "user_name": agent.get("user_name"),
+ "twilio": True,
+ }
+ )
+ doc.db_insert()
diff --git a/crm/patches/v1_0/rename_twilio_settings_to_crm_twilio_settings.py b/crm/patches/v1_0/rename_twilio_settings_to_crm_twilio_settings.py
new file mode 100644
index 000000000..349764843
--- /dev/null
+++ b/crm/patches/v1_0/rename_twilio_settings_to_crm_twilio_settings.py
@@ -0,0 +1,20 @@
+import frappe
+from frappe.model.rename_doc import rename_doc
+
+
+def execute():
+ if frappe.db.exists("DocType", "Twilio Settings"):
+ frappe.flags.ignore_route_conflict_validation = True
+ rename_doc("DocType", "Twilio Settings", "CRM Twilio Settings")
+ frappe.flags.ignore_route_conflict_validation = False
+
+ frappe.reload_doctype("CRM Twilio Settings", force=True)
+
+ if frappe.db.exists("__Auth", {"doctype": "Twilio Settings"}):
+ Auth = frappe.qb.DocType("__Auth")
+ result = frappe.qb.from_(Auth).select("*").where(Auth.doctype == "Twilio Settings").run(as_dict=True)
+
+ for row in result:
+ frappe.qb.into(Auth).insert(
+ "CRM Twilio Settings", "CRM Twilio Settings", row.fieldname, row.password, row.encrypted
+ ).run()
diff --git a/frontend/src/components/Settings/Settings.vue b/frontend/src/components/Settings/Settings.vue
index 2f1c981db..fde939388 100644
--- a/frontend/src/components/Settings/Settings.vue
+++ b/frontend/src/components/Settings/Settings.vue
@@ -67,7 +67,7 @@ import {
import { Dialog, Button, Avatar } from 'frappe-ui'
import { ref, markRaw, computed, watch, h } from 'vue'
-const { isManager, getUser } = usersStore()
+const { isManager, isAgent, getUser } = usersStore()
const user = computed(() => getUser() || {})
@@ -108,20 +108,22 @@ const tabs = computed(() => {
label: __('Telephony'),
icon: PhoneIcon,
component: markRaw(TelephonySettings),
+ condition: () => isManager() || isAgent(),
},
{
label: __('WhatsApp'),
icon: WhatsAppIcon,
component: markRaw(WhatsAppSettings),
- condition: () => isWhatsappInstalled.value,
+ condition: () => isWhatsappInstalled.value && isManager(),
},
{
label: __('ERPNext'),
icon: ERPNextIcon,
component: markRaw(ERPNextSettings),
+ condition: () => isManager(),
},
],
- condition: () => isManager(),
+ condition: () => isManager() || isAgent(),
},
]
diff --git a/frontend/src/components/Settings/TelephonySettings.vue b/frontend/src/components/Settings/TelephonySettings.vue
index 8134df82d..b7df8c351 100644
--- a/frontend/src/components/Settings/TelephonySettings.vue
+++ b/frontend/src/components/Settings/TelephonySettings.vue
@@ -19,17 +19,18 @@