From c3ab48ea7409653874980b1f4b559a1491e1976e Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 20 Feb 2025 18:31:26 +0530 Subject: [PATCH 01/10] fix: fix for dark mode --- frontend/src/pages/Lead.vue | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index 347935eb..e3e61b28 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -202,7 +202,7 @@ -
+
{{ __('Choose Existing') }}
@@ -210,7 +210,6 @@
-
+
{{ __('Choose Existing') }}
@@ -237,7 +236,6 @@ Date: Fri, 21 Feb 2025 14:38:01 +0530 Subject: [PATCH 02/10] fix: capture signup from demo account --- frontend/src/components/SignupBanner.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/components/SignupBanner.vue b/frontend/src/components/SignupBanner.vue index 7586f463..1faed676 100644 --- a/frontend/src/components/SignupBanner.vue +++ b/frontend/src/components/SignupBanner.vue @@ -21,6 +21,7 @@ From 235b1346a49e63e06a6441980c6e2ef8b5d3946c Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 21 Feb 2025 14:38:26 +0530 Subject: [PATCH 03/10] build(deps): bump frappeui to 0.1.110 --- frontend/package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 1ac29f78..88fa0c76 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.105", + "frappe-ui": "^0.1.110", "gemoji": "^8.1.0", "lodash": "^4.17.21", "mime": "^4.0.1", diff --git a/yarn.lock b/yarn.lock index 7b7baaa4..25bbca56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1136,10 +1136,10 @@ dependencies: mini-svg-data-uri "^1.2.3" -"@tailwindcss/typography@^0.5.0": - version "0.5.15" - resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.15.tgz#007ab9870c86082a1c76e5b3feda9392c7c8d648" - integrity sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA== +"@tailwindcss/typography@^0.5.16": + version "0.5.16" + resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.16.tgz#a926c8f44d5c439b2915e231cad80058850047c6" + integrity sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA== dependencies: lodash.castarray "^4.4.0" lodash.isplainobject "^4.0.6" @@ -2388,15 +2388,15 @@ fraction.js@^4.3.7: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -frappe-ui@^0.1.105: - version "0.1.105" - resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.105.tgz#3bdf3c458ba27f27ff2f2a28cf7eb6f9ed872367" - integrity sha512-9bZ/hj/HhQ9vp7DxE8aOKS8HqwETZrKT3IhSzjpYOk21efK8QwdbQ9sp0t4m3UII+HaUTSOTHnFzF7y9EhRZxg== +frappe-ui@^0.1.110: + version "0.1.110" + resolved "https://registry.yarnpkg.com/frappe-ui/-/frappe-ui-0.1.110.tgz#dbe02d294cb0aeb0a4c1b3000682093c309450ae" + integrity sha512-kFah6SoPauULXaeSbljNUq595/82VmY4k4+KA8zi4sXxpn4sXYi12qUl/1I8GOBhsCQQizmoh46DO7e/uU2M1A== dependencies: "@headlessui/vue" "^1.7.14" "@popperjs/core" "^2.11.2" "@tailwindcss/forms" "^0.5.3" - "@tailwindcss/typography" "^0.5.0" + "@tailwindcss/typography" "^0.5.16" "@tiptap/extension-color" "^2.0.3" "@tiptap/extension-highlight" "^2.0.3" "@tiptap/extension-image" "^2.0.3" From 3ed63ebc47b611244b92ecab46d8331bee2d52d7 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 21 Feb 2025 14:40:43 +0530 Subject: [PATCH 04/10] fix: catch error while converting lead to deal --- frontend/src/pages/Lead.vue | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index e3e61b28..7de430aa 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -599,10 +599,15 @@ async function convertToDeal(updated) { } else { let deal = await call( 'crm.fcrm.doctype.crm_lead.crm_lead.convert_to_deal', - { - lead: lead.data.name, - }, - ) + { lead: lead.data.name }, + ).catch((err) => { + createToast({ + title: __('Error converting to deal'), + text: __(err.messages?.[0]), + icon: 'x', + iconClasses: 'text-ink-red-4', + }) + }) if (deal) { capture('convert_lead_to_deal') if (updated) { From 55a4b9b3e3be1721ace3ef80086c0a2f50d805ab Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 21 Feb 2025 14:43:37 +0530 Subject: [PATCH 05/10] feat: show required deal fields in convert to deal modal --- frontend/src/pages/Lead.vue | 53 +++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index 7de430aa..3f02832e 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -245,6 +245,15 @@ {{ __("New contact will be created based on the person's details") }}
+ +
+ + { + let statuses = statusOptions('deal') + if (!deal.status) { + deal.status = statuses[0].value + } + return statuses +}) + +const dealTabs = createResource({ + url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout', + cache: ['RequiredFields', 'CRM Deal'], + params: { doctype: 'CRM Deal', type: 'Required Fields' }, + auto: true, + transform: (_tabs) => { + let hasFields = false + let parsedTabs = _tabs.forEach((tab) => { + tab.sections.forEach((section) => { + section.columns.forEach((column) => { + column.fields.forEach((field) => { + hasFields = true + if (field.fieldname == 'status') { + field.fieldtype = 'Select' + field.options = dealStatuses.value + field.prefix = getDealStatus(deal.status).color + } + + if (field.fieldtype === 'Table') { + deal[field.fieldname] = [] + } + }) + }) + }) + }) + return hasFields ? parsedTabs : [] + }, +}) From ec6a1f84eb7962cac52bfab411839595d2632e68 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 21 Feb 2025 14:44:33 +0530 Subject: [PATCH 06/10] fix: do not get default layout if type is Required Fields --- crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 271a5da9..e44e3e20 100644 --- a/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py +++ b/crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.py @@ -24,7 +24,7 @@ def get_fields_layout(doctype: str, type: str, parent_doctype: str | None = None if layout and layout.layout: tabs = json.loads(layout.layout) - if not tabs: + if not tabs and type != "Required Fields": tabs = get_default_layout(doctype) has_tabs = tabs[0].get("sections") if tabs and tabs[0] else False From 19bf3a0122530843fad3f039de7065aee5f71902 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 21 Feb 2025 14:44:54 +0530 Subject: [PATCH 07/10] fix: added Required Fields option in type --- crm/fcrm/doctype/crm_fields_layout/crm_fields_layout.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 43019c5c..c61189e2 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\nGrid Row" + "options": "Quick Entry\nSide Panel\nData Fields\nGrid Row\nRequired Fields" }, { "fieldname": "section_break_ttpm", @@ -46,7 +46,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2025-01-02 22:12:51.663011", + "modified": "2025-02-21 13:09:49.573515", "modified_by": "Administrator", "module": "FCRM", "name": "CRM Fields Layout", From 7294310e84488d73e69ca0ed1ec4f18c75306cb6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 21 Feb 2025 14:47:28 +0530 Subject: [PATCH 08/10] fix: added quick entry modal editor for required fields --- frontend/src/pages/Lead.vue | 49 +++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index 3f02832e..5361b41c 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -186,7 +186,6 @@ + + From 3c865c37a7296ef481b1c71f4edeaff38f8f055a Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 21 Feb 2025 14:49:39 +0530 Subject: [PATCH 09/10] fix: set type based on onlyRequired flag --- crm/api/doc.py | 5 ++++- frontend/src/components/FieldLayoutEditor.vue | 5 +++++ frontend/src/components/Modals/QuickEntryModal.vue | 13 ++++++++++--- frontend/src/pages/Lead.vue | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/crm/api/doc.py b/crm/api/doc.py index a8627584..8caf0d04 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -537,7 +537,7 @@ def get_records_based_on_order(doctype, rows, filters, page_length, order): @frappe.whitelist() -def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False): +def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False, only_required=False): not_allowed_fieldtypes = [ "Tab Break", "Section Break", @@ -572,6 +572,9 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False): if not restricted_fieldtypes or field.get("fieldtype") not in restricted_fieldtypes: fields.append(field) + if only_required: + fields = [field for field in fields if field.get("reqd")] + if as_array: return fields diff --git a/frontend/src/components/FieldLayoutEditor.vue b/frontend/src/components/FieldLayoutEditor.vue index 5dcff8e5..8804b920 100644 --- a/frontend/src/components/FieldLayoutEditor.vue +++ b/frontend/src/components/FieldLayoutEditor.vue @@ -226,6 +226,10 @@ import { ref, computed, watch } from 'vue' const props = defineProps({ tabs: Object, doctype: String, + onlyRequired: { + type: Boolean, + default: false, + }, }) const tabIndex = ref(0) @@ -249,6 +253,7 @@ const params = computed(() => { doctype: props.doctype, restricted_fieldtypes: restrictedFieldTypes, as_array: true, + only_required: props.onlyRequired, } }) diff --git a/frontend/src/components/Modals/QuickEntryModal.vue b/frontend/src/components/Modals/QuickEntryModal.vue index ec237520..7c3af8a8 100644 --- a/frontend/src/components/Modals/QuickEntryModal.vue +++ b/frontend/src/components/Modals/QuickEntryModal.vue @@ -35,6 +35,7 @@ v-if="!preview" :tabs="tabs.data" :doctype="_doctype" + :onlyRequired="onlyRequired" />
@@ -55,6 +56,10 @@ const props = defineProps({ type: String, default: 'CRM Lead', }, + onlyRequired: { + type: Boolean, + default: false, + }, }) const show = defineModel() @@ -64,12 +69,13 @@ const dirty = ref(false) const preview = ref(false) function getParams() { - return { doctype: _doctype.value, type: 'Quick Entry' } + let type = props.onlyRequired ? 'Required Fields' : 'Quick Entry' + return { doctype: _doctype.value, type } } const tabs = createResource({ url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_fields_layout', - cache: ['QuickEntryModal', _doctype.value], + cache: ['QuickEntryModal', _doctype.value, props.onlyRequired], params: getParams(), onSuccess(data) { tabs.originalData = JSON.parse(JSON.stringify(data)) @@ -106,11 +112,12 @@ function saveChanges() { }) }) loading.value = true + let type = props.onlyRequired ? 'Required Fields' : 'Quick Entry' call( 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.save_fields_layout', { doctype: _doctype.value, - type: 'Quick Entry', + type: type, layout: JSON.stringify(_tabs), }, ).then(() => { diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index 5361b41c..040b8834 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -285,6 +285,7 @@ v-if="showQuickEntryModal" v-model="showQuickEntryModal" doctype="CRM Deal" + :onlyRequired="true" /> Date: Fri, 21 Feb 2025 14:50:16 +0530 Subject: [PATCH 10/10] fix: pass required deal fields value while converting to deal --- crm/fcrm/doctype/crm_lead/crm_lead.py | 31 +++++++++++++++------------ frontend/src/pages/Lead.vue | 8 +++---- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/crm/fcrm/doctype/crm_lead/crm_lead.py b/crm/fcrm/doctype/crm_lead/crm_lead.py index bdf0885f..a7a41d34 100644 --- a/crm/fcrm/doctype/crm_lead/crm_lead.py +++ b/crm/fcrm/doctype/crm_lead/crm_lead.py @@ -197,8 +197,8 @@ class CRMLead(Document): return False - def create_deal(self, contact, organization): - deal = frappe.new_doc("CRM Deal") + def create_deal(self, contact, organization, deal=None): + new_deal = frappe.new_doc("CRM Deal") lead_deal_map = { "lead_owner": "deal_owner", @@ -245,13 +245,13 @@ class CRMLead(Document): if field.fieldname in lead_deal_map: fieldname = lead_deal_map[field.fieldname] - if hasattr(deal, fieldname): + if hasattr(new_deal, fieldname): if fieldname == "organization": - deal.update({fieldname: organization}) + new_deal.update({fieldname: organization}) else: - deal.update({fieldname: self.get(field.fieldname)}) + new_deal.update({fieldname: self.get(field.fieldname)}) - deal.update( + new_deal.update( { "lead": self.name, "contacts": [{"contact": contact}], @@ -259,7 +259,7 @@ class CRMLead(Document): ) if self.first_responded_on: - deal.update( + new_deal.update( { "sla_creation": self.sla_creation, "response_by": self.response_by, @@ -270,8 +270,11 @@ class CRMLead(Document): } ) - deal.insert(ignore_permissions=True) - return deal.name + if deal: + new_deal.update(deal) + + new_deal.insert(ignore_permissions=True) + return new_deal.name def set_sla(self): """ @@ -297,8 +300,8 @@ class CRMLead(Document): if sla: sla.apply(self) - def convert_to_deal(self): - return convert_to_deal(lead=self.name, doc=self) + def convert_to_deal(self, deal=None): + return convert_to_deal(lead=self.name, doc=self, deal=deal) @staticmethod def get_non_filterable_fields(): @@ -380,7 +383,7 @@ class CRMLead(Document): @frappe.whitelist() -def convert_to_deal(lead, doc=None): +def convert_to_deal(lead, doc=None, deal=None): if not (doc and doc.flags.get("ignore_permissions")) and not frappe.has_permission( "CRM Lead", "write", lead ): @@ -394,5 +397,5 @@ def convert_to_deal(lead, doc=None): lead.db_set("communication_status", "Replied") contact = lead.create_contact(False) organization = lead.create_organization() - deal = lead.create_deal(contact, organization) - return deal + _deal = lead.create_deal(contact, organization, deal) + return _deal diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue index 040b8834..1e8d728b 100644 --- a/frontend/src/pages/Lead.vue +++ b/frontend/src/pages/Lead.vue @@ -646,9 +646,9 @@ async function convertToDeal(updated) { ) showConvertToDealModal.value = false } else { - let deal = await call( + let _deal = await call( 'crm.fcrm.doctype.crm_lead.crm_lead.convert_to_deal', - { lead: lead.data.name }, + { lead: lead.data.name, deal }, ).catch((err) => { createToast({ title: __('Error converting to deal'), @@ -657,12 +657,12 @@ async function convertToDeal(updated) { iconClasses: 'text-ink-red-4', }) }) - if (deal) { + if (_deal) { capture('convert_lead_to_deal') if (updated) { await contacts.reload() } - router.push({ name: 'Deal', params: { dealId: deal } }) + router.push({ name: 'Deal', params: { dealId: _deal } }) } } }