From eb296d5781a38036512f8fbd5a4c3414c91dd85c Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 22 Jan 2025 19:56:53 +0530 Subject: [PATCH 1/4] chore: updated record call label --- crm/fcrm/doctype/crm_exotel_settings/crm_exotel_settings.json | 4 ++-- frappe-ui | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crm/fcrm/doctype/crm_exotel_settings/crm_exotel_settings.json b/crm/fcrm/doctype/crm_exotel_settings/crm_exotel_settings.json index 82a9a45f9..c153a6c54 100644 --- a/crm/fcrm/doctype/crm_exotel_settings/crm_exotel_settings.json +++ b/crm/fcrm/doctype/crm_exotel_settings/crm_exotel_settings.json @@ -72,7 +72,7 @@ "depends_on": "enabled", "fieldname": "record_call", "fieldtype": "Check", - "label": "Record Call" + "label": "Record Outgoing Calls" }, { "fieldname": "column_break_qwfn", @@ -96,7 +96,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2025-01-20 15:00:51.096985", + "modified": "2025-01-22 19:54:20.074393", "modified_by": "Administrator", "module": "FCRM", "name": "CRM Exotel Settings", diff --git a/frappe-ui b/frappe-ui index aea806331..e01b1ee30 160000 --- a/frappe-ui +++ b/frappe-ui @@ -1 +1 @@ -Subproject commit aea806331c179cc0cdb89b6a2f9de2130bd1061f +Subproject commit e01b1ee305a0207413cd67ed7345578440c503db From 4c797f65a3f0fe68e3288fa1256adcb4bb3ffb33 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 22 Jan 2025 20:14:29 +0530 Subject: [PATCH 2/4] fix: handle checkbox quick filter --- frontend/src/components/ViewControls.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/ViewControls.vue b/frontend/src/components/ViewControls.vue index 30d4f8496..d1678ad58 100644 --- a/frontend/src/components/ViewControls.vue +++ b/frontend/src/components/ViewControls.vue @@ -608,8 +608,10 @@ const quickFilterList = computed(() => { ) return filter['value'] = value[1]?.replace(/%/g, '') + } else if (typeof value == 'boolean') { + filter['value'] = value } else { - filter['value'] = value.replace(/%/g, '') + filter['value'] = value?.replace(/%/g, '') } } }) From 7e00969ef77b6d2bc6670f164ab31a8d69c9b194 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 23 Jan 2025 01:59:38 +0530 Subject: [PATCH 3/4] fix: reload notes on delete --- frontend/src/components/Activities/NoteArea.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Activities/NoteArea.vue b/frontend/src/components/Activities/NoteArea.vue index a0971c174..f04234d7e 100644 --- a/frontend/src/components/Activities/NoteArea.vue +++ b/frontend/src/components/Activities/NoteArea.vue @@ -68,6 +68,6 @@ async function deleteNote(name) { doctype: 'FCRM Note', name, }) - notes.reload() + notes.value?.reload() } From 7b56035123e243ff27bd86cf4394791271153777 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 23 Jan 2025 14:43:44 +0530 Subject: [PATCH 4/4] refactor: use fieldname instead of name and value in sortby, filter & groupby --- crm/api/doc.py | 71 ++++++++++++------------ crm/integrations/api.py | 3 + frontend/src/components/Filter.vue | 24 +++----- frontend/src/components/GroupBy.vue | 11 ++-- frontend/src/components/SortBy.vue | 12 ++-- frontend/src/components/ViewControls.vue | 2 +- frontend/src/pages/Deals.vue | 8 +-- frontend/src/pages/Leads.vue | 8 +-- 8 files changed, 66 insertions(+), 73 deletions(-) diff --git a/crm/api/doc.py b/crm/api/doc.py index 282ebe8d5..ad2cf99ce 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -19,21 +19,23 @@ def sort_options(doctype: str): { "label": _(field.label), "value": field.fieldname, + "fieldname": field.fieldname, } for field in fields if field.label and field.fieldname ] standard_fields = [ - {"label": "Name", "value": "name"}, - {"label": "Created On", "value": "creation"}, - {"label": "Last Modified", "value": "modified"}, - {"label": "Modified By", "value": "modified_by"}, - {"label": "Owner", "value": "owner"}, + {"label": "Name", "fieldname": "name"}, + {"label": "Created On", "fieldname": "creation"}, + {"label": "Last Modified", "fieldname": "modified"}, + {"label": "Modified By", "fieldname": "modified_by"}, + {"label": "Owner", "fieldname": "owner"}, ] for field in standard_fields: field["label"] = _(field["label"]) + field["value"] = field["fieldname"] fields.append(field) return fields @@ -100,6 +102,7 @@ def get_filterable_fields(doctype: str): for field in res: field["label"] = _(field.get("label")) + field["value"] = field.get("fieldname") return res @@ -129,23 +132,23 @@ def get_group_by_fields(doctype: str): fields = [ { "label": _(field.label), - "value": field.fieldname, + "fieldname": field.fieldname, } for field in fields if field.label and field.fieldname ] standard_fields = [ - {"label": "Name", "value": "name"}, - {"label": "Created On", "value": "creation"}, - {"label": "Last Modified", "value": "modified"}, - {"label": "Modified By", "value": "modified_by"}, - {"label": "Owner", "value": "owner"}, - {"label": "Liked By", "value": "_liked_by"}, - {"label": "Assigned To", "value": "_assign"}, - {"label": "Comments", "value": "_comments"}, - {"label": "Created On", "value": "creation"}, - {"label": "Modified On", "value": "modified"}, + {"label": "Name", "fieldname": "name"}, + {"label": "Created On", "fieldname": "creation"}, + {"label": "Last Modified", "fieldname": "modified"}, + {"label": "Modified By", "fieldname": "modified_by"}, + {"label": "Owner", "fieldname": "owner"}, + {"label": "Liked By", "fieldname": "_liked_by"}, + {"label": "Assigned To", "fieldname": "_assign"}, + {"label": "Comments", "fieldname": "_comments"}, + {"label": "Created On", "fieldname": "creation"}, + {"label": "Modified On", "fieldname": "modified"}, ] for field in standard_fields: @@ -197,7 +200,7 @@ def get_quick_filters(doctype: str): ) if doctype == "CRM Lead": - quick_filters = [filter for filter in quick_filters if filter.get("name") != "converted"] + quick_filters = [filter for filter in quick_filters if filter.get("fieldname") != "converted"] return quick_filters @@ -344,7 +347,7 @@ def get_data( for kc in kanban_columns: 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.get("name")) or kc.get("delete"): column_data = [] else: column_filters.update(filters.copy()) @@ -392,8 +395,8 @@ def get_data( fields = [ { "label": _(field.label), - "type": field.fieldtype, - "value": field.fieldname, + "fieldtype": field.fieldtype, + "fieldname": field.fieldname, "options": field.options, } for field in fields @@ -401,23 +404,23 @@ def get_data( ] std_fields = [ - {"label": "Name", "type": "Data", "value": "name"}, - {"label": "Created On", "type": "Datetime", "value": "creation"}, - {"label": "Last Modified", "type": "Datetime", "value": "modified"}, + {"label": "Name", "fieldtype": "Data", "fieldname": "name"}, + {"label": "Created On", "fieldtype": "Datetime", "fieldname": "creation"}, + {"label": "Last Modified", "fieldtype": "Datetime", "fieldname": "modified"}, { "label": "Modified By", - "type": "Link", - "value": "modified_by", + "fieldtype": "Link", + "fieldname": "modified_by", "options": "User", }, - {"label": "Assigned To", "type": "Text", "value": "_assign"}, - {"label": "Owner", "type": "Link", "value": "owner", "options": "User"}, - {"label": "Like", "type": "Data", "value": "_liked_by"}, + {"label": "Assigned To", "fieldtype": "Text", "fieldname": "_assign"}, + {"label": "Owner", "fieldtype": "Link", "fieldname": "owner", "options": "User"}, + {"label": "Like", "fieldtype": "Data", "fieldname": "_liked_by"}, ] for field in std_fields: - if field.get("value") not in rows: - rows.append(field.get("value")) + if field.get("fieldname") not in rows: + rows.append(field.get("fieldname")) if field not in fields: field["label"] = _(field["label"]) fields.append(field) @@ -451,12 +454,12 @@ def get_options(type, options): return options for field in fields: - if field.get("value") == group_by_field: + if field.get("fieldname") == group_by_field: group_by_field = { "label": field.get("label"), - "name": field.get("value"), - "type": field.get("type"), - "options": get_options(field.get("type"), field.get("options")), + "fieldname": field.get("fieldname"), + "fieldtype": field.get("fieldtype"), + "options": get_options(field.get("fieldtype"), field.get("options")), } return { diff --git a/crm/integrations/api.py b/crm/integrations/api.py index c1c4e1983..d83006781 100644 --- a/crm/integrations/api.py +++ b/crm/integrations/api.py @@ -116,6 +116,9 @@ def get_contact_by_phone_number(phone_number): def get_contact(phone_number, exact_match=False): + if not phone_number: + return {"mobile_no": phone_number} + cleaned_number = ( phone_number.strip() .replace(" ", "") diff --git a/frontend/src/components/Filter.vue b/frontend/src/components/Filter.vue index 0d8c031aa..437856b0d 100644 --- a/frontend/src/components/Filter.vue +++ b/frontend/src/components/Filter.vue @@ -197,19 +197,7 @@ const list = defineModel() const filterableFields = createResource({ url: 'crm.api.doc.get_filterable_fields', cache: ['filterableFields', props.doctype], - params: { - doctype: props.doctype, - }, - transform(fields) { - fields = fields.map((field) => { - return { - label: field.label, - value: field.fieldname, - ...field, - } - }) - return fields - }, + params: { doctype: props.doctype }, }) onMounted(() => { @@ -447,11 +435,11 @@ function setfilter(data) { filters.value.add({ field: { label: data.label, - fieldname: data.value, + fieldname: data.fieldname, fieldtype: data.fieldtype, options: data.options, }, - fieldname: data.value, + fieldname: data.fieldname, operator: getDefaultOperator(data.fieldtype), value: getDefaultValue(data), }) @@ -459,14 +447,16 @@ function setfilter(data) { } function updateFilter(data, index) { + if (!data.fieldname) return + filters.value.delete(Array.from(filters.value)[index]) filters.value.add({ - fieldname: data.value, + fieldname: data.fieldname, operator: getDefaultOperator(data.fieldtype), value: getDefaultValue(data), field: { label: data.label, - fieldname: data.value, + fieldname: data.fieldname, fieldtype: data.fieldtype, options: data.options, }, diff --git a/frontend/src/components/GroupBy.vue b/frontend/src/components/GroupBy.vue index a856d0cd5..a71ab9a28 100644 --- a/frontend/src/components/GroupBy.vue +++ b/frontend/src/components/GroupBy.vue @@ -45,15 +45,13 @@ const list = defineModel() const groupByValue = ref({ label: '', - value: '', + fieldname: '', }) const groupByOptions = createResource({ url: 'crm.api.doc.get_group_by_fields', cache: ['groupByOptions', props.doctype], - params: { - doctype: props.doctype, - }, + params: { doctype: props.doctype }, }) onMounted(() => { @@ -62,8 +60,9 @@ onMounted(() => { }) function setGroupBy(data) { + if (!data?.fieldname) return groupByValue.value = data - nextTick(() => emit('update', data.value)) + nextTick(() => emit('update', data.fieldname)) } const options = computed(() => { @@ -71,7 +70,7 @@ const options = computed(() => { if (!list.value?.data?.group_by_field) return groupByOptions.data groupByValue.value = list.value.data.group_by_field return groupByOptions.data.filter( - (option) => option !== groupByValue.value.value + (option) => option.fieldname !== groupByValue.value.fieldname, ) }) diff --git a/frontend/src/components/SortBy.vue b/frontend/src/components/SortBy.vue index acd3902b6..4e7e7fa40 100644 --- a/frontend/src/components/SortBy.vue +++ b/frontend/src/components/SortBy.vue @@ -195,9 +195,7 @@ const list = defineModel() const sortOptions = createResource({ url: 'crm.api.doc.sort_options', cache: ['sortOptions', props.doctype], - params: { - doctype: props.doctype, - }, + params: { doctype: props.doctype }, }) onMounted(() => { @@ -228,7 +226,7 @@ const options = computed(() => { const selectedOptions = [...sortValues.value].map((sort) => sort.fieldname) restartSort() return sortOptions.data.filter((option) => { - return !selectedOptions.includes(option.value) + return !selectedOptions.includes(option.fieldname) }) }) @@ -242,13 +240,13 @@ function getSortLabel() { if (!sortValues.value.size) return __('Sort') let values = Array.from(sortValues.value) let label = sortOptions.data?.find( - (option) => option.value === values[0].fieldname, + (option) => option.fieldname === values[0].fieldname, )?.label return label || values[0].fieldname } function setSort(data) { - sortValues.value.add({ fieldname: data.value, direction: 'asc' }) + sortValues.value.add({ fieldname: data.fieldname, direction: 'asc' }) restartSort() apply() } @@ -257,7 +255,7 @@ function updateSort(data, index) { let oldSort = Array.from(sortValues.value)[index] sortValues.value.delete(oldSort) sortValues.value.add({ - fieldname: data.value, + fieldname: data.fieldname, direction: oldSort.direction, }) apply() diff --git a/frontend/src/components/ViewControls.vue b/frontend/src/components/ViewControls.vue index d1678ad58..03ed56629 100644 --- a/frontend/src/components/ViewControls.vue +++ b/frontend/src/components/ViewControls.vue @@ -589,7 +589,7 @@ const viewsDropdownOptions = computed(() => { }) const quickFilterList = computed(() => { - let filters = [{ name: 'name', label: __('ID') }] + let filters = [{ fieldname: 'name', fieldtype: 'Data', label: __('ID') }] if (quickFilters.data) { filters.push(...quickFilters.data) } diff --git a/frontend/src/pages/Deals.vue b/frontend/src/pages/Deals.vue index afe921e37..578534b7a 100644 --- a/frontend/src/pages/Deals.vue +++ b/frontend/src/pages/Deals.vue @@ -328,7 +328,7 @@ function getRow(name, field) { const rows = computed(() => { if (!deals.value?.data?.data) return [] if (deals.value.data.view_type === 'group_by') { - if (!deals.value?.data.group_by_field?.name) return [] + if (!deals.value?.data.group_by_field?.fieldname) return [] return getGroupedByRows( deals.value?.data.data, deals.value?.data.group_by_field, @@ -348,9 +348,9 @@ function getGroupedByRows(listRows, groupByField, columns) { let filteredRows = [] if (!option) { - filteredRows = listRows.filter((row) => !row[groupByField.name]) + filteredRows = listRows.filter((row) => !row[groupByField.fieldname]) } else { - filteredRows = listRows.filter((row) => row[groupByField.name] == option) + filteredRows = listRows.filter((row) => row[groupByField.fieldname] == option) } let groupDetail = { @@ -359,7 +359,7 @@ function getGroupedByRows(listRows, groupByField, columns) { collapsed: false, rows: parseRows(filteredRows, columns), } - if (groupByField.name == 'status') { + if (groupByField.fieldname == 'status') { groupDetail.icon = () => h(IndicatorIcon, { class: getDealStatus(option)?.color, diff --git a/frontend/src/pages/Leads.vue b/frontend/src/pages/Leads.vue index d3508ef31..98afce8fa 100644 --- a/frontend/src/pages/Leads.vue +++ b/frontend/src/pages/Leads.vue @@ -348,7 +348,7 @@ function getRow(name, field) { const rows = computed(() => { if (!leads.value?.data?.data) return [] if (leads.value.data.view_type === 'group_by') { - if (!leads.value?.data.group_by_field?.name) return [] + if (!leads.value?.data.group_by_field?.fieldname) return [] return getGroupedByRows( leads.value?.data.data, leads.value?.data.group_by_field, @@ -368,9 +368,9 @@ function getGroupedByRows(listRows, groupByField, columns) { let filteredRows = [] if (!option) { - filteredRows = listRows.filter((row) => !row[groupByField.name]) + filteredRows = listRows.filter((row) => !row[groupByField.fieldname]) } else { - filteredRows = listRows.filter((row) => row[groupByField.name] == option) + filteredRows = listRows.filter((row) => row[groupByField.fieldname] == option) } let groupDetail = { @@ -379,7 +379,7 @@ function getGroupedByRows(listRows, groupByField, columns) { collapsed: false, rows: parseRows(filteredRows, columns), } - if (groupByField.name == 'status') { + if (groupByField.fieldname == 'status') { groupDetail.icon = () => h(IndicatorIcon, { class: getLeadStatus(option)?.color,