diff --git a/crm/api/doc.py b/crm/api/doc.py index fae49446..f8e20b85 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -1,4 +1,5 @@ import frappe +import json from frappe import _ from frappe.model.document import get_controller from frappe.model import no_value_fields @@ -156,6 +157,43 @@ def get_group_by_fields(doctype: str): return fields +@frappe.whitelist() +def get_quick_entry_fields(doctype: str): + sections = [] + if frappe.db.exists("CRM Fields Layout", {"dt": doctype, "type": "Quick Entry"}): + layout = frappe.get_doc("CRM Fields Layout", {"dt": doctype, "type": "Quick Entry"}) + else: + return [] + + if layout.layout: + sections = json.loads(layout.layout) + + allowed_fields = [] + for section in sections: + 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"): + field = next((f for f in fields if f.fieldname == field), None) + if field: + 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": ""}) + field = { + "label": _(field.label), + "name": field.fieldname, + "type": field.fieldtype, + "options": field.options, + "mandatory": field.reqd, + } + section["fields"][section.get("fields").index(field["name"])] = field + + return sections or [] + def get_fields_meta(DocField, doctype, allowed_fieldtypes, restricted_fields): parent = "parent" if DocField._table_name == "tabDocField" else "dt" return ( diff --git a/crm/fcrm/doctype/crm_fields_layout/__init__.py b/crm/fcrm/doctype/crm_fields_layout/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.js b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.js new file mode 100644 index 00000000..6a3abeb8 --- /dev/null +++ b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("CRM Fields Layout", { +// refresh(frm) { + +// }, +// }); diff --git a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json new file mode 100644 index 00000000..c780557a --- /dev/null +++ b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json @@ -0,0 +1,73 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:{dt}-{type}", + "creation": "2024-06-07 16:42:05.495324", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "dt", + "column_break_post", + "type", + "section_break_ttpm", + "layout" + ], + "fields": [ + { + "fieldname": "dt", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Document Type", + "options": "DocType", + "unique": 1 + }, + { + "fieldname": "type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Type", + "options": "Quick Entry\nSide Panel" + }, + { + "fieldname": "section_break_ttpm", + "fieldtype": "Section Break" + }, + { + "fieldname": "layout", + "fieldtype": "Code", + "label": "Layout", + "options": "JS" + }, + { + "fieldname": "column_break_post", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2024-06-07 17:01:20.250697", + "modified_by": "Administrator", + "module": "FCRM", + "name": "CRM Fields Layout", + "naming_rule": "Expression", + "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": "creation", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py new file mode 100644 index 00000000..36d8bb72 --- /dev/null +++ b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class CRMFieldsLayout(Document): + pass diff --git a/crm/fcrm/doctype/crm_fields_layout/test_crm_fields_layout.py b/crm/fcrm/doctype/crm_fields_layout/test_crm_fields_layout.py new file mode 100644 index 00000000..bdc71c0a --- /dev/null +++ b/crm/fcrm/doctype/crm_fields_layout/test_crm_fields_layout.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestCRMFieldsLayout(FrappeTestCase): + pass diff --git a/crm/install.py b/crm/install.py index 351dd8ce..923cbd4e 100644 --- a/crm/install.py +++ b/crm/install.py @@ -13,6 +13,7 @@ def after_install(): add_default_lead_statuses() add_default_deal_statuses() add_default_communication_statuses() + add_default_fields_layout() add_property_setter() add_email_template_custom_fields() frappe.db.commit() @@ -108,6 +109,36 @@ def add_default_communication_statuses(): doc.status = status doc.insert() +def add_default_fields_layout(): + layouts = { + "CRM Lead-Quick Entry": { + "doctype": "CRM Lead", + "layout": '[\n{\n"label": "Person",\n\t"fields": ["salutation", "first_name", "last_name", "email", "mobile_no", "gender"]\n},\n{\n"label": "Organization",\n\t"fields": ["organization", "website", "no_of_employees", "territory", "annual_revenue", "industry"]\n},\n{\n"label": "Other",\n"columns": 2,\n\t"fields": ["status", "lead_owner"]\n}\n]' + }, + "CRM Deal-Quick Entry": { + "doctype": "CRM Deal", + "layout": '[\n{\n"label": "Select Organization",\n\t"fields": ["organization"]\n},\n{\n"label": "Organization Details",\n\t"fields": [{"label": "Organization Name", "name": "organization_name", "type": "Data"}, "website", "no_of_employees", "territory", "annual_revenue", {"label": "Industry", "name": "industry", "type": "Link", "options": "CRM Industry"}]\n},\n{\n"label": "Select Contact",\n\t"fields": [{"label": "Contact", "name": "contact", "type": "Link", "options": "Contact"}]\n},\n{\n"label": "Contact Details",\n\t"fields": [{"label": "Salutation", "name": "salutation", "type": "Link", "options": "Salutation"}, {"label": "First Name", "name": "first_name", "type": "Data"}, {"label": "Last Name", "name": "last_name", "type": "Data"}, "email", "mobile_no", {"label": "Gender", "name": "gender", "type": "Link", "options": "Gender"}]\n},\n{\n"label": "Other",\n"columns": 2,\n\t"fields": ["status", "deal_owner"]\n}\n]' + }, + "Contact-Quick Entry": { + "doctype": "Contact", + "layout": '[\n{\n"label": "Salutation",\n"columns": 1,\n"fields": ["salutation"]\n},\n{\n"label": "Full Name",\n"columns": 2,\n"hideBorder": true,\n"fields": ["first_name", "last_name"]\n},\n{\n"label": "Email",\n"columns": 1,\n"hideBorder": true,\n"fields": ["email_id"]\n},\n{\n"label": "Mobile No. & Gender",\n"columns": 2,\n"hideBorder": true,\n"fields": ["mobile_no", "gender"]\n},\n{\n"label": "Organization",\n"columns": 1,\n"hideBorder": true,\n"fields": ["company_name"]\n},\n{\n"label": "Designation",\n"columns": 1,\n"hideBorder": true,\n"fields": ["designation"]\n}\n]' + }, + "Organization-Quick Entry": { + "doctype": "CRM Organization", + "layout": '[\n{\n"label": "Organization Name",\n"columns": 1,\n"fields": ["organization_name"]\n},\n{\n"label": "Website & Revenue",\n"columns": 2,\n"hideBorder": true,\n"fields": ["website", "annual_revenue"]\n},\n{\n"label": "Territory",\n"columns": 1,\n"hideBorder": true,\n"fields": ["territory"]\n},\n{\n"label": "No of Employees & Industry",\n"columns": 2,\n"hideBorder": true,\n"fields": ["no_of_employees", "industry"]\n}\n]' + }, + } + + for layout in layouts: + if frappe.db.exists("CRM Fields Layout", layout): + continue + + doc = frappe.new_doc("CRM Fields Layout") + doc.type = "Quick Entry" + doc.dt = layouts[layout]["doctype"] + doc.layout = layouts[layout]["layout"] + doc.insert() + def add_property_setter(): if not frappe.db.exists("Property Setter", {"name": "Contact-main-search_fields"}): doc = frappe.new_doc("Property Setter") diff --git a/crm/patches.txt b/crm/patches.txt index a16089c3..c0204ec5 100644 --- a/crm/patches.txt +++ b/crm/patches.txt @@ -5,4 +5,5 @@ crm.patches.v1_0.move_crm_note_data_to_fcrm_note [post_model_sync] # Patches added in this section will be executed after doctypes are migrated -crm.patches.v1_0.create_email_template_custom_fields \ No newline at end of file +crm.patches.v1_0.create_email_template_custom_fields +crm.patches.v1_0.create_default_fields_layout \ No newline at end of file diff --git a/crm/patches/v1_0/create_default_fields_layout.py b/crm/patches/v1_0/create_default_fields_layout.py new file mode 100644 index 00000000..383e7ac2 --- /dev/null +++ b/crm/patches/v1_0/create_default_fields_layout.py @@ -0,0 +1,5 @@ + +from crm.install import add_default_fields_layout + +def execute(): + add_default_fields_layout() \ No newline at end of file diff --git a/frontend/src/components/Fields.vue b/frontend/src/components/Fields.vue index 84a59179..b93c2357 100644 --- a/frontend/src/components/Fields.vue +++ b/frontend/src/components/Fields.vue @@ -2,7 +2,7 @@
@@ -20,34 +20,34 @@ *
-
+