diff --git a/crm/api/doc.py b/crm/api/doc.py index 576127a1a..b7e3e4638 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -78,12 +78,7 @@ def get_filterable_fields(doctype: str): # append standard fields (getting error when using frappe.model.std_fields) standard_fields = [ {"fieldname": "name", "fieldtype": "Link", "label": "ID", "options": doctype}, - { - "fieldname": "owner", - "fieldtype": "Link", - "label": "Created By", - "options": "User" - }, + {"fieldname": "owner", "fieldtype": "Link", "label": "Created By", "options": "User"}, { "fieldname": "modified_by", "fieldtype": "Link", @@ -98,10 +93,7 @@ def get_filterable_fields(doctype: str): {"fieldname": "modified", "fieldtype": "Datetime", "label": "Last Updated On"}, ] for field in standard_fields: - if ( - field.get("fieldname") not in restricted_fields and - field.get("fieldtype") in allowed_fieldtypes - ): + if field.get("fieldname") not in restricted_fields and field.get("fieldtype") in allowed_fieldtypes: field["name"] = field.get("fieldname") res.append(field) @@ -128,7 +120,11 @@ def get_group_by_fields(doctype: str): ] fields = frappe.get_meta(doctype).fields - fields = [field for field in fields if field.fieldtype not in no_value_fields and field.fieldtype in allowed_fieldtypes] + fields = [ + field + for field in fields + if field.fieldtype not in no_value_fields and field.fieldtype in allowed_fieldtypes + ] fields = [ { "label": _(field.label), @@ -176,6 +172,7 @@ def get_doctype_fields_meta(DocField, doctype, allowed_fieldtypes, restricted_fi .run(as_dict=True) ) + @frappe.whitelist() def get_quick_filters(doctype: str): meta = frappe.get_meta(doctype) @@ -183,23 +180,25 @@ def get_quick_filters(doctype: str): quick_filters = [] for field in fields: - if field.fieldtype == "Select": field.options = field.options.split("\n") field.options = [{"label": option, "value": option} for option in field.options] field.options.insert(0, {"label": "", "value": ""}) - quick_filters.append({ - "label": _(field.label), - "name": field.fieldname, - "type": field.fieldtype, - "options": field.options, - }) + quick_filters.append( + { + "label": _(field.label), + "name": field.fieldname, + "type": field.fieldtype, + "options": field.options, + } + ) if doctype == "CRM Lead": quick_filters = [filter for filter in quick_filters if filter.get("name") != "converted"] return quick_filters + @frappe.whitelist() def get_data( doctype: str, @@ -223,9 +222,9 @@ def get_data( kanban_fields = frappe.parse_json(kanban_fields or "[]") kanban_columns = frappe.parse_json(kanban_columns or "[]") - custom_view_name = view.get('custom_view_name') if view else None - view_type = view.get('view_type') if view else None - group_by_field = view.get('group_by_field') if view else None + custom_view_name = view.get("custom_view_name") if view else None + view_type = view.get("view_type") if view else None + group_by_field = view.get("group_by_field") if view else None for key in filters: value = filters[key] @@ -268,7 +267,7 @@ def get_data( default_view_filters = { "dt": doctype, - "type": view_type or 'list', + "type": view_type or "list", "is_default": 1, "user": frappe.session.user, } @@ -295,13 +294,16 @@ def get_data( if group_by_field and group_by_field not in rows: rows.append(group_by_field) - data = frappe.get_list( - doctype, - fields=rows, - filters=filters, - order_by=order_by, - page_length=page_length, - ) or [] + data = ( + frappe.get_list( + doctype, + fields=rows, + filters=filters, + order_by=order_by, + page_length=page_length, + ) + or [] + ) if view_type == "kanban": if not rows: @@ -336,9 +338,9 @@ def get_data( rows.append(field) for kc in kanban_columns: - column_filters = { column_field: kc.get('name') } + column_filters = {column_field: kc.get("name")} order = kc.get("order") - if column_field in filters and filters.get(column_field) != kc.name or kc.get('delete'): + if column_field in filters and filters.get(column_field) != kc.name or kc.get("delete"): column_data = [] else: column_filters.update(filters.copy()) @@ -348,7 +350,9 @@ def get_data( page_length = kc.get("page_length") if order: - column_data = get_records_based_on_order(doctype, rows, column_filters, page_length, order) + column_data = get_records_based_on_order( + doctype, rows, column_filters, page_length, order + ) else: column_data = frappe.get_list( doctype, @@ -359,9 +363,11 @@ def get_data( ) new_filters = filters.copy() - new_filters.update({ column_field: kc.get('name') }) + new_filters.update({column_field: kc.get("name")}) - all_count = len(frappe.get_list(doctype, filters=convert_filter_to_tuple(doctype, new_filters))) + all_count = len( + frappe.get_list(doctype, filters=convert_filter_to_tuple(doctype, new_filters)) + ) kc["all_count"] = all_count kc["count"] = len(column_data) @@ -371,8 +377,8 @@ def get_data( if order: column_data = sorted( - column_data, key=lambda x: order.index(x.get("name")) - if x.get("name") in order else len(order) + column_data, + key=lambda x: order.index(x.get("name")) if x.get("name") in order else len(order), ) data.append({"column": kc, "fields": kanban_fields, "data": column_data}) @@ -406,8 +412,8 @@ def get_data( ] for field in std_fields: - if field.get('value') not in rows: - rows.append(field.get('value')) + if field.get("value") not in rows: + rows.append(field.get("value")) if field not in fields: field["label"] = _(field["label"]) fields.append(field) @@ -416,6 +422,7 @@ def get_data( is_default = frappe.db.get_value("CRM View Settings", custom_view_name, "load_default_columns") if group_by_field and view_type == "group_by": + def get_options(type, options): if type == "Select": return [option for option in options.split("\n")] @@ -428,7 +435,9 @@ def get_options(type, options): if order_by and group_by_field in order_by: order_by_fields = order_by.split(",") - order_by_fields = [(field.split(" ")[0], field.split(" ")[1]) for field in order_by_fields] + order_by_fields = [ + (field.split(" ")[0], field.split(" ")[1]) for field in order_by_fields + ] if (group_by_field, "asc") in order_by_fields: options.sort() elif (group_by_field, "desc") in order_by_fields: @@ -467,6 +476,7 @@ def get_options(type, options): "view_type": view_type, } + def convert_filter_to_tuple(doctype, filters): if isinstance(filters, dict): filters_items = filters.items() @@ -504,6 +514,7 @@ def get_records_based_on_order(doctype, rows, filters, page_length, order): return records + @frappe.whitelist() def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False): not_allowed_fieldtypes = [ @@ -521,12 +532,7 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False): standard_fields = [ {"fieldname": "name", "fieldtype": "Link", "label": "ID", "options": doctype}, - { - "fieldname": "owner", - "fieldtype": "Link", - "label": "Created By", - "options": "User" - }, + {"fieldname": "owner", "fieldtype": "Link", "label": "Created By", "options": "User"}, { "fieldname": "modified_by", "fieldtype": "Link", @@ -542,7 +548,7 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False): ] for field in standard_fields: - if not restricted_fieldtypes or field.get('fieldtype') not in restricted_fieldtypes: + if not restricted_fieldtypes or field.get("fieldtype") not in restricted_fieldtypes: fields.append(field) if as_array: @@ -550,10 +556,11 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False): fields_meta = {} for field in fields: - fields_meta[field.get('fieldname')] = field + fields_meta[field.get("fieldname")] = field return fields_meta + @frappe.whitelist() def get_sidebar_fields(doctype, name): if not frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": "Side Panel"}): @@ -562,7 +569,7 @@ def get_sidebar_fields(doctype, name): if not layout: return [] - + layout = json.loads(layout) not_allowed_fieldtypes = [ @@ -600,6 +607,7 @@ def get_sidebar_fields(doctype, name): return layout + def get_field_obj(field): obj = { "label": field.label, @@ -641,6 +649,7 @@ def get_type(field): return "read_only" return field.fieldtype.lower() + def get_assigned_users(doctype, name, default_assigned_to=None): assigned_users = frappe.get_all( "ToDo", @@ -671,32 +680,55 @@ def get_fields(doctype: str, allow_all_fieldtypes: bool = False): _fields = [] for field in fields: - if ( - field.fieldtype not in not_allowed_fieldtypes - and field.fieldname - ): - _fields.append({ - "label": field.label, - "type": field.fieldtype, - "value": field.fieldname, - "options": field.options, - "mandatory": field.reqd, - "read_only": field.read_only, - "hidden": field.hidden, - "depends_on": field.depends_on, - "mandatory_depends_on": field.mandatory_depends_on, - "read_only_depends_on": field.read_only_depends_on, - "link_filters": field.get("link_filters"), - "placeholder": field.get("placeholder"), - }) + if field.fieldtype not in not_allowed_fieldtypes and field.fieldname: + _fields.append( + { + "label": field.label, + "type": field.fieldtype, + "value": field.fieldname, + "options": field.options, + "mandatory": field.reqd, + "read_only": field.read_only, + "hidden": field.hidden, + "depends_on": field.depends_on, + "mandatory_depends_on": field.mandatory_depends_on, + "read_only_depends_on": field.read_only_depends_on, + "link_filters": field.get("link_filters"), + "placeholder": field.get("placeholder"), + } + ) return _fields def getCounts(d, doctype): - d["_email_count"] = frappe.db.count("Communication", filters={"reference_doctype": doctype, "reference_name": d.get("name"), "communication_type": "Communication"}) or 0 - d["_email_count"] = d["_email_count"] + frappe.db.count("Communication", filters={"reference_doctype": doctype, "reference_name": d.get("name"), "communication_type": "Automated Message"}) - d["_comment_count"] = frappe.db.count("Comment", filters={"reference_doctype": doctype, "reference_name": d.get("name"), "comment_type": "Comment"}) - d["_task_count"] = frappe.db.count("CRM Task", filters={"reference_doctype": doctype, "reference_docname": d.get("name")}) - d["_note_count"] = frappe.db.count("FCRM Note", filters={"reference_doctype": doctype, "reference_docname": d.get("name")}) - return d \ No newline at end of file + d["_email_count"] = ( + frappe.db.count( + "Communication", + filters={ + "reference_doctype": doctype, + "reference_name": d.get("name"), + "communication_type": "Communication", + }, + ) + or 0 + ) + d["_email_count"] = d["_email_count"] + frappe.db.count( + "Communication", + filters={ + "reference_doctype": doctype, + "reference_name": d.get("name"), + "communication_type": "Automated Message", + }, + ) + d["_comment_count"] = frappe.db.count( + "Comment", + filters={"reference_doctype": doctype, "reference_name": d.get("name"), "comment_type": "Comment"}, + ) + d["_task_count"] = frappe.db.count( + "CRM Task", filters={"reference_doctype": doctype, "reference_docname": d.get("name")} + ) + d["_note_count"] = frappe.db.count( + "FCRM Note", filters={"reference_doctype": doctype, "reference_docname": d.get("name")} + ) + return d diff --git a/crm/fcrm/doctype/crm_deal/api.py b/crm/fcrm/doctype/crm_deal/api.py index 76a764aed..7cc776bda 100644 --- a/crm/fcrm/doctype/crm_deal/api.py +++ b/crm/fcrm/doctype/crm_deal/api.py @@ -1,38 +1,19 @@ import frappe -from frappe import _ -from crm.api.doc import get_fields_meta, get_assigned_users +from crm.api.doc import get_assigned_users, get_fields_meta from crm.fcrm.doctype.crm_form_script.crm_form_script import get_form_script + @frappe.whitelist() def get_deal(name): - Deal = frappe.qb.DocType("CRM Deal") - - query = ( - frappe.qb.from_(Deal) - .select("*") - .where(Deal.name == name) - .limit(1) - ) - - deal = query.run(as_dict=True) - if not len(deal): - frappe.throw(_("Deal not found"), frappe.DoesNotExistError) - deal = deal.pop() + deal = frappe.get_doc("CRM Deal", name).as_dict() - - deal["contacts"] = frappe.get_all( - "CRM Contacts", - filters={"parenttype": "CRM Deal", "parent": deal.name}, - fields=["contact", "is_primary"], - ) - - deal["doctype"] = "CRM Deal" - deal["fields_meta"] = get_fields_meta("CRM Deal") - deal["_form_script"] = get_form_script('CRM Deal') + deal["fields_meta"] = get_fields_meta("CRM Deal") + deal["_form_script"] = get_form_script("CRM Deal") deal["_assign"] = get_assigned_users("CRM Deal", deal.name, deal.owner) return deal + @frappe.whitelist() def get_deal_contacts(name): contacts = frappe.get_all( @@ -44,16 +25,19 @@ def get_deal_contacts(name): for contact in contacts: is_primary = contact.is_primary contact = frappe.get_doc("Contact", contact.contact).as_dict() + def get_primary_email(contact): for email in contact.email_ids: if email.is_primary: return email.email_id return contact.email_ids[0].email_id if contact.email_ids else "" + def get_primary_mobile_no(contact): for phone in contact.phone_nos: if phone.is_primary: return phone.phone return contact.phone_nos[0].phone if contact.phone_nos else "" + _contact = { "name": contact.name, "image": contact.image, @@ -63,4 +47,4 @@ def get_primary_mobile_no(contact): "is_primary": is_primary, } deal_contacts.append(_contact) - return deal_contacts \ No newline at end of file + return deal_contacts 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 000127704..d31df954e 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" + "options": "Quick Entry\nSide Panel\nData Fields" }, { "fieldname": "section_break_ttpm", @@ -46,7 +46,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-06-13 15:10:01.612851", + "modified": "2024-12-05 13:29:37.021412", "modified_by": "Administrator", "module": "FCRM", "name": "CRM Fields Layout", diff --git a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py index 6926d0b2b..0d06ca436 100644 --- a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py +++ b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py @@ -2,6 +2,7 @@ # For license information, please see license.txt import json + import frappe from frappe import _ from frappe.model.document import Document @@ -10,46 +11,54 @@ class CRMFieldsLayout(Document): pass + @frappe.whitelist() def get_fields_layout(doctype: str, type: str): - sections = [] + tabs = [] if frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": type}): layout = frappe.get_doc("CRM Fields Layout", {"dt": doctype, "type": type}) else: return [] if layout.layout: - sections = json.loads(layout.layout) + tabs = json.loads(layout.layout) + + has_tabs = tabs[0].get("sections") if tabs and tabs[0] else False + + if not has_tabs: + tabs = [{"no_tabs": True, "sections": tabs}] allowed_fields = [] - for section in sections: - if not section.get("fields"): - continue - allowed_fields.extend(section.get("fields")) + for tab in tabs: + for section in tab.get("sections"): + if not section.get("fields"): + continue + allowed_fields.extend(section.get("fields")) fields = frappe.get_meta(doctype).fields fields = [field for field in fields if field.fieldname in allowed_fields] - for section in sections: - for field in section.get("fields") if section.get("fields") else []: - field = next((f for f in fields if f.fieldname == field), None) - if field: - if field.fieldtype == "Select" and field.options: - field.options = field.options.split("\n") - field.options = [{"label": _(option), "value": option} for option in field.options] - field.options.insert(0, {"label": "", "value": ""}) - field = { - "label": _(field.label), - "name": field.fieldname, - "type": field.fieldtype, - "options": field.options, - "mandatory": field.reqd, - "placeholder": field.get("placeholder"), - "filters": field.get("link_filters") - } - section["fields"][section.get("fields").index(field["name"])] = field - - return sections or [] + for tab in tabs: + for section in tab.get("sections"): + for field in section.get("fields") if section.get("fields") else []: + field = next((f for f in fields if f.fieldname == field), None) + if field: + if field.fieldtype == "Select" and field.options: + field.options = field.options.split("\n") + field.options = [{"label": _(option), "value": option} for option in field.options] + field.options.insert(0, {"label": "", "value": ""}) + field = { + "label": _(field.label), + "name": field.fieldname, + "type": field.fieldtype, + "options": field.options, + "mandatory": field.reqd, + "placeholder": field.get("placeholder"), + "filters": field.get("link_filters"), + } + section["fields"][section.get("fields").index(field["name"])] = field + + return tabs or [] @frappe.whitelist() @@ -59,11 +68,13 @@ def save_fields_layout(doctype: str, type: str, layout: str): else: doc = frappe.new_doc("CRM Fields Layout") - doc.update({ - "dt": doctype, - "type": type, - "layout": layout, - }) + doc.update( + { + "dt": doctype, + "type": type, + "layout": layout, + } + ) doc.save(ignore_permissions=True) return doc.layout diff --git a/crm/fcrm/doctype/crm_lead/api.py b/crm/fcrm/doctype/crm_lead/api.py index e1bb4a4f5..2ecaf112e 100644 --- a/crm/fcrm/doctype/crm_lead/api.py +++ b/crm/fcrm/doctype/crm_lead/api.py @@ -1,22 +1,14 @@ import frappe -from frappe import _ -from crm.api.doc import get_fields_meta, get_assigned_users +from crm.api.doc import get_assigned_users, get_fields_meta from crm.fcrm.doctype.crm_form_script.crm_form_script import get_form_script + @frappe.whitelist() def get_lead(name): - Lead = frappe.qb.DocType("CRM Lead") - - query = frappe.qb.from_(Lead).select("*").where(Lead.name == name).limit(1) - - lead = query.run(as_dict=True) - if not len(lead): - frappe.throw(_("Lead not found"), frappe.DoesNotExistError) - lead = lead.pop() + lead = frappe.get_doc("CRM Lead", name).as_dict() - lead["doctype"] = "CRM Lead" lead["fields_meta"] = get_fields_meta("CRM Lead") - lead["_form_script"] = get_form_script('CRM Lead') + lead["_form_script"] = get_form_script("CRM Lead") lead["_assign"] = get_assigned_users("CRM Lead", lead.name, lead.owner) return lead diff --git a/frontend/package.json b/frontend/package.json index b481b37d2..2782be374 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.90", + "frappe-ui": "^0.1.91", "gemoji": "^8.1.0", "lodash": "^4.17.21", "mime": "^4.0.1", diff --git a/frontend/src/components/Activities/Activities.vue b/frontend/src/components/Activities/Activities.vue index af7491194..baf28de0f 100644 --- a/frontend/src/components/Activities/Activities.vue +++ b/frontend/src/components/Activities/Activities.vue @@ -364,6 +364,9 @@ +
+ +
{ text = 'No Email Communications' } else if (title.value == 'Comments') { text = 'No Comments' + } else if (title.value == 'Data') { + text = 'No Data' } else if (title.value == 'Calls') { text = 'No Call Logs' } else if (title.value == 'Notes') { @@ -739,6 +746,8 @@ const emptyTextIcon = computed(() => { icon = Email2Icon } else if (title.value == 'Comments') { icon = CommentIcon + } else if (title.value == 'Data') { + icon = DetailsIcon } else if (title.value == 'Calls') { icon = PhoneIcon } else if (title.value == 'Notes') { diff --git a/frontend/src/components/Activities/ActivityHeader.vue b/frontend/src/components/Activities/ActivityHeader.vue index de7b812af..78ad93a72 100644 --- a/frontend/src/components/Activities/ActivityHeader.vue +++ b/frontend/src/components/Activities/ActivityHeader.vue @@ -1,5 +1,6 @@