diff --git a/crm/api/doc.py b/crm/api/doc.py
index b7e3e463..64d14f6f 100644
--- a/crm/api/doc.py
+++ b/crm/api/doc.py
@@ -1,10 +1,11 @@
-import frappe
import json
+
+import frappe
from frappe import _
-from frappe.model.document import get_controller
from frappe.model import no_value_fields
-from pypika import Criterion
+from frappe.model.document import get_controller
from frappe.utils import make_filter_tuple
+from pypika import Criterion
from crm.api.views import get_views
from crm.fcrm.doctype.crm_form_script.crm_form_script import get_form_script
@@ -557,6 +558,9 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False):
fields_meta = {}
for field in fields:
fields_meta[field.get("fieldname")] = field
+ if field.get("fieldtype") == "Table":
+ _fields = frappe.get_meta(field.get("options")).fields
+ fields_meta[field.get("fieldname")] = {"df": field, "fields": _fields}
return fields_meta
@@ -672,7 +676,7 @@ def get_assigned_users(doctype, name, default_assigned_to=None):
@frappe.whitelist()
def get_fields(doctype: str, allow_all_fieldtypes: bool = False):
- not_allowed_fieldtypes = list(frappe.model.no_value_fields) + ["Read Only"]
+ not_allowed_fieldtypes = [*list(frappe.model.no_value_fields), "Read Only"]
if allow_all_fieldtypes:
not_allowed_fieldtypes = []
fields = frappe.get_meta(doctype).fields
diff --git a/crm/fcrm/doctype/crm_deal/crm_deal.py b/crm/fcrm/doctype/crm_deal/crm_deal.py
index 04bf74fd..beb63cbd 100644
--- a/crm/fcrm/doctype/crm_deal/crm_deal.py
+++ b/crm/fcrm/doctype/crm_deal/crm_deal.py
@@ -1,6 +1,5 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-import json
import frappe
from frappe import _
@@ -8,7 +7,9 @@ from frappe.desk.form.assign_to import add as assign
from frappe.model.document import Document
from crm.fcrm.doctype.crm_service_level_agreement.utils import get_sla
-from crm.fcrm.doctype.crm_status_change_log.crm_status_change_log import add_status_change_log
+from crm.fcrm.doctype.crm_status_change_log.crm_status_change_log import (
+ add_status_change_log,
+)
class CRMDeal(Document):
@@ -94,19 +95,26 @@ class CRMDeal(Document):
shared_with = [d.user for d in docshares] + [agent]
for user in shared_with:
- if user == agent and not frappe.db.exists("DocShare", {"user": agent, "share_name": self.name, "share_doctype": self.doctype}):
+ if user == agent and not frappe.db.exists(
+ "DocShare",
+ {"user": agent, "share_name": self.name, "share_doctype": self.doctype},
+ ):
frappe.share.add_docshare(
- self.doctype, self.name, agent, write=1, flags={"ignore_share_permission": True}
+ self.doctype,
+ self.name,
+ agent,
+ write=1,
+ flags={"ignore_share_permission": True},
)
elif user != agent:
frappe.share.remove(self.doctype, self.name, user)
-
def set_sla(self):
"""
Find an SLA to apply to the deal.
"""
- if self.sla: return
+ if self.sla:
+ return
sla = get_sla(self)
if not sla:
@@ -129,48 +137,48 @@ class CRMDeal(Document):
def default_list_data():
columns = [
{
- 'label': 'Organization',
- 'type': 'Link',
- 'key': 'organization',
- 'options': 'CRM Organization',
- 'width': '11rem',
+ "label": "Organization",
+ "type": "Link",
+ "key": "organization",
+ "options": "CRM Organization",
+ "width": "11rem",
},
{
- 'label': 'Amount',
- 'type': 'Currency',
- 'key': 'annual_revenue',
- 'align': 'right',
- 'width': '9rem',
+ "label": "Annual Revenue",
+ "type": "Currency",
+ "key": "annual_revenue",
+ "align": "right",
+ "width": "9rem",
},
{
- 'label': 'Status',
- 'type': 'Select',
- 'key': 'status',
- 'width': '10rem',
+ "label": "Status",
+ "type": "Select",
+ "key": "status",
+ "width": "10rem",
},
{
- 'label': 'Email',
- 'type': 'Data',
- 'key': 'email',
- 'width': '12rem',
+ "label": "Email",
+ "type": "Data",
+ "key": "email",
+ "width": "12rem",
},
{
- 'label': 'Mobile No',
- 'type': 'Data',
- 'key': 'mobile_no',
- 'width': '11rem',
+ "label": "Mobile No",
+ "type": "Data",
+ "key": "mobile_no",
+ "width": "11rem",
},
{
- 'label': 'Assigned To',
- 'type': 'Text',
- 'key': '_assign',
- 'width': '10rem',
+ "label": "Assigned To",
+ "type": "Text",
+ "key": "_assign",
+ "width": "10rem",
},
{
- 'label': 'Last Modified',
- 'type': 'Datetime',
- 'key': 'modified',
- 'width': '8rem',
+ "label": "Last Modified",
+ "type": "Datetime",
+ "key": "modified",
+ "width": "8rem",
},
]
rows = [
@@ -189,16 +197,17 @@ class CRMDeal(Document):
"modified",
"_assign",
]
- return {'columns': columns, 'rows': rows}
+ return {"columns": columns, "rows": rows}
@staticmethod
def default_kanban_settings():
return {
"column_field": "status",
"title_field": "organization",
- "kanban_fields": '["annual_revenue", "email", "mobile_no", "_assign", "modified"]'
+ "kanban_fields": '["annual_revenue", "email", "mobile_no", "_assign", "modified"]',
}
+
@frappe.whitelist()
def add_contact(deal, contact):
if not frappe.has_permission("CRM Deal", "write", deal):
@@ -209,6 +218,7 @@ def add_contact(deal, contact):
deal.save()
return True
+
@frappe.whitelist()
def remove_contact(deal, contact):
if not frappe.has_permission("CRM Deal", "write", deal):
@@ -219,6 +229,7 @@ def remove_contact(deal, contact):
deal.save()
return True
+
@frappe.whitelist()
def set_primary_contact(deal, contact):
if not frappe.has_permission("CRM Deal", "write", deal):
@@ -229,11 +240,14 @@ def set_primary_contact(deal, contact):
deal.save()
return True
+
def create_organization(doc):
if not doc.get("organization_name"):
return
- existing_organization = frappe.db.exists("CRM Organization", {"organization_name": doc.get("organization_name")})
+ existing_organization = frappe.db.exists(
+ "CRM Organization", {"organization_name": doc.get("organization_name")}
+ )
if existing_organization:
return existing_organization
@@ -250,6 +264,7 @@ def create_organization(doc):
organization.insert(ignore_permissions=True)
return organization.name
+
def contact_exists(doc):
email_exist = frappe.db.exists("Contact Email", {"email_id": doc.get("email")})
mobile_exist = frappe.db.exists("Contact Phone", {"phone": doc.get("mobile_no")})
@@ -262,6 +277,7 @@ def contact_exists(doc):
return False
+
def create_contact(doc):
existing_contact = contact_exists(doc)
if existing_contact:
@@ -288,18 +304,23 @@ def create_contact(doc):
return contact.name
+
@frappe.whitelist()
def create_deal(args):
deal = frappe.new_doc("CRM Deal")
contact = args.get("contact")
- if not contact and (args.get("first_name") or args.get("last_name") or args.get("email") or args.get("mobile_no")):
+ if not contact and (
+ args.get("first_name") or args.get("last_name") or args.get("email") or args.get("mobile_no")
+ ):
contact = create_contact(args)
- deal.update({
- "organization": args.get("organization") or create_organization(args),
- "contacts": [{"contact": contact, "is_primary": 1}] if contact else [],
- })
+ deal.update(
+ {
+ "organization": args.get("organization") or create_organization(args),
+ "contacts": [{"contact": contact, "is_primary": 1}] if contact else [],
+ }
+ )
args.pop("organization", None)
diff --git a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json
index d31df954..7094e58d 100644
--- a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json
+++ b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json
@@ -27,7 +27,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Type",
- "options": "Quick Entry\nSide Panel\nData Fields"
+ "options": "Quick Entry\nSide Panel\nData Fields\nGrid Row"
},
{
"fieldname": "section_break_ttpm",
@@ -46,7 +46,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2024-12-05 13:29:37.021412",
+ "modified": "2024-12-29 12:58:54.280569",
"modified_by": "Administrator",
"module": "FCRM",
"name": "CRM Fields Layout",
diff --git a/crm/fcrm/doctype/crm_lead/crm_lead.py b/crm/fcrm/doctype/crm_lead/crm_lead.py
index 632080a9..e81cc566 100644
--- a/crm/fcrm/doctype/crm_lead/crm_lead.py
+++ b/crm/fcrm/doctype/crm_lead/crm_lead.py
@@ -1,15 +1,16 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
-import json
import frappe
from frappe import _
from frappe.desk.form.assign_to import add as assign
from frappe.model.document import Document
-
from frappe.utils import has_gravatar, validate_email_address
+
from crm.fcrm.doctype.crm_service_level_agreement.utils import get_sla
-from crm.fcrm.doctype.crm_status_change_log.crm_status_change_log import add_status_change_log
+from crm.fcrm.doctype.crm_status_change_log.crm_status_change_log import (
+ add_status_change_log,
+)
class CRMLead(Document):
@@ -37,7 +38,15 @@ class CRMLead(Document):
def set_full_name(self):
if self.first_name:
self.lead_name = " ".join(
- filter(None, [self.salutation, self.first_name, self.middle_name, self.last_name])
+ filter(
+ None,
+ [
+ self.salutation,
+ self.first_name,
+ self.middle_name,
+ self.last_name,
+ ],
+ )
)
def set_lead_name(self):
@@ -92,9 +101,16 @@ class CRMLead(Document):
shared_with = [d.user for d in docshares] + [agent]
for user in shared_with:
- if user == agent and not frappe.db.exists("DocShare", {"user": agent, "share_name": self.name, "share_doctype": self.doctype}):
+ if user == agent and not frappe.db.exists(
+ "DocShare",
+ {"user": agent, "share_name": self.name, "share_doctype": self.doctype},
+ ):
frappe.share.add_docshare(
- self.doctype, self.name, agent, write=1, flags={"ignore_share_permission": True}
+ self.doctype,
+ self.name,
+ agent,
+ write=1,
+ flags={"ignore_share_permission": True},
)
elif user != agent:
frappe.share.remove(self.doctype, self.name, user)
@@ -188,8 +204,36 @@ class CRMLead(Document):
"lead_owner": "deal_owner",
}
- restricted_fieldtypes = ["Tab Break", "Section Break", "Column Break", "HTML", "Button", "Attach", "Table"]
- restricted_map_fields = ["name", "naming_series", "creation", "owner", "modified", "modified_by", "idx", "docstatus", "status", "email", "mobile_no", "phone", "sla", "sla_status", "response_by", "first_response_time", "first_responded_on", "communication_status", "sla_creation"]
+ restricted_fieldtypes = [
+ "Tab Break",
+ "Section Break",
+ "Column Break",
+ "HTML",
+ "Button",
+ "Attach",
+ "Table",
+ ]
+ restricted_map_fields = [
+ "name",
+ "naming_series",
+ "creation",
+ "owner",
+ "modified",
+ "modified_by",
+ "idx",
+ "docstatus",
+ "status",
+ "email",
+ "mobile_no",
+ "phone",
+ "sla",
+ "sla_status",
+ "response_by",
+ "first_response_time",
+ "first_responded_on",
+ "communication_status",
+ "sla_creation",
+ ]
for field in self.meta.fields:
if field.fieldtype in restricted_fieldtypes:
@@ -222,7 +266,7 @@ class CRMLead(Document):
"sla_status": self.sla_status,
"communication_status": self.communication_status,
"first_response_time": self.first_response_time,
- "first_responded_on": self.first_responded_on
+ "first_responded_on": self.first_responded_on,
}
)
@@ -233,7 +277,8 @@ class CRMLead(Document):
"""
Find an SLA to apply to the lead.
"""
- if self.sla: return
+ if self.sla:
+ return
sla = get_sla(self)
if not sla:
@@ -263,47 +308,47 @@ class CRMLead(Document):
def default_list_data():
columns = [
{
- 'label': 'Name',
- 'type': 'Data',
- 'key': 'lead_name',
- 'width': '12rem',
+ "label": "Name",
+ "type": "Data",
+ "key": "lead_name",
+ "width": "12rem",
},
{
- 'label': 'Organization',
- 'type': 'Link',
- 'key': 'organization',
- 'options': 'CRM Organization',
- 'width': '10rem',
+ "label": "Organization",
+ "type": "Link",
+ "key": "organization",
+ "options": "CRM Organization",
+ "width": "10rem",
},
{
- 'label': 'Status',
- 'type': 'Select',
- 'key': 'status',
- 'width': '8rem',
+ "label": "Status",
+ "type": "Select",
+ "key": "status",
+ "width": "8rem",
},
{
- 'label': 'Email',
- 'type': 'Data',
- 'key': 'email',
- 'width': '12rem',
+ "label": "Email",
+ "type": "Data",
+ "key": "email",
+ "width": "12rem",
},
{
- 'label': 'Mobile No',
- 'type': 'Data',
- 'key': 'mobile_no',
- 'width': '11rem',
+ "label": "Mobile No",
+ "type": "Data",
+ "key": "mobile_no",
+ "width": "11rem",
},
{
- 'label': 'Assigned To',
- 'type': 'Text',
- 'key': '_assign',
- 'width': '10rem',
+ "label": "Assigned To",
+ "type": "Text",
+ "key": "_assign",
+ "width": "10rem",
},
{
- 'label': 'Last Modified',
- 'type': 'Datetime',
- 'key': 'modified',
- 'width': '8rem',
+ "label": "Last Modified",
+ "type": "Datetime",
+ "key": "modified",
+ "width": "8rem",
},
]
rows = [
@@ -323,20 +368,22 @@ class CRMLead(Document):
"_assign",
"image",
]
- return {'columns': columns, 'rows': rows}
+ return {"columns": columns, "rows": rows}
@staticmethod
def default_kanban_settings():
return {
"column_field": "status",
"title_field": "lead_name",
- "kanban_fields": '["organization", "email", "mobile_no", "_assign", "modified"]'
+ "kanban_fields": '["organization", "email", "mobile_no", "_assign", "modified"]',
}
@frappe.whitelist()
def convert_to_deal(lead, doc=None):
- if not (doc and doc.flags.get("ignore_permissions")) and not frappe.has_permission("CRM Lead", "write", lead):
+ if not (doc and doc.flags.get("ignore_permissions")) and not frappe.has_permission(
+ "CRM Lead", "write", lead
+ ):
frappe.throw(_("Not allowed to convert Lead to Deal"), frappe.PermissionError)
lead = frappe.get_cached_doc("CRM Lead", lead)
diff --git a/frappe-ui b/frappe-ui
index 5a4f3c8d..46086c52 160000
--- a/frappe-ui
+++ b/frappe-ui
@@ -1 +1 @@
-Subproject commit 5a4f3c8d4f12efba37b9a83a51a59b53fa758be0
+Subproject commit 46086c524bc218d989c68ca54cd13a37e693fab9
diff --git a/frontend/package.json b/frontend/package.json
index 2782be37..5e247fa7 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -14,7 +14,7 @@
"@vueuse/core": "^10.3.0",
"@vueuse/integrations": "^10.3.0",
"feather-icons": "^4.28.0",
- "frappe-ui": "^0.1.91",
+ "frappe-ui": "^0.1.94",
"gemoji": "^8.1.0",
"lodash": "^4.17.21",
"mime": "^4.0.1",
diff --git a/frontend/src/components/Activities/DataFields.vue b/frontend/src/components/Activities/DataFields.vue
index cee88cf0..7d3d7380 100644
--- a/frontend/src/components/Activities/DataFields.vue
+++ b/frontend/src/components/Activities/DataFields.vue
@@ -31,7 +31,7 @@