diff --git a/crm/api/doc.py b/crm/api/doc.py index 8fc1f705..199c99b1 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -240,7 +240,8 @@ def get_list_data( "views": get_views(doctype), "total_count": len(frappe.get_list(doctype, filters=filters)), "row_count": len(data), - "form_script": get_form_script(doctype) + "form_script": get_form_script(doctype), + "list_script": get_form_script(doctype, "List"), } diff --git a/crm/api/notifications.py b/crm/api/notifications.py index bdbe9118..3dde662c 100644 --- a/crm/api/notifications.py +++ b/crm/api/notifications.py @@ -4,9 +4,6 @@ from frappe.query_builder import Order @frappe.whitelist() def get_notifications(): - if frappe.session.user == "Guest": - frappe.throw("Authentication failed", exc=frappe.AuthenticationError) - Notification = frappe.qb.DocType("CRM Notification") query = ( frappe.qb.from_(Notification) @@ -46,9 +43,6 @@ def get_notifications(): @frappe.whitelist() def mark_as_read(user=None, comment=None): - if frappe.session.user == "Guest": - frappe.throw("Authentication failed", exc=frappe.AuthenticationError) - user = user or frappe.session.user filters = {"to_user": user, "read": False} if comment: diff --git a/crm/api/session.py b/crm/api/session.py index c6218317..3902ff7f 100644 --- a/crm/api/session.py +++ b/crm/api/session.py @@ -1,11 +1,8 @@ import frappe -@frappe.whitelist(allow_guest=True) +@frappe.whitelist() def get_users(): - if frappe.session.user == "Guest": - frappe.throw("Authentication failed", exc=frappe.AuthenticationError) - users = frappe.qb.get_query( "User", fields=["name", "email", "enabled", "user_image", "full_name", "user_type"], @@ -24,9 +21,6 @@ def get_users(): @frappe.whitelist() def get_contacts(): - if frappe.session.user == "Guest": - frappe.throw("Authentication failed", exc=frappe.AuthenticationError) - contacts = frappe.get_all( "Contact", fields=[ @@ -66,9 +60,6 @@ def get_contacts(): @frappe.whitelist() def get_lead_contacts(): - if frappe.session.user == "Guest": - frappe.throw("Authentication failed", exc=frappe.AuthenticationError) - lead_contacts = frappe.get_all( "CRM Lead", fields=[ @@ -88,9 +79,6 @@ def get_lead_contacts(): @frappe.whitelist() def get_organizations(): - if frappe.session.user == "Guest": - frappe.throw("Authentication failed", exc=frappe.AuthenticationError) - organizations = frappe.qb.get_query( "CRM Organization", fields=['*'], diff --git a/crm/api/views.py b/crm/api/views.py index 6a95adbf..f70246e1 100644 --- a/crm/api/views.py +++ b/crm/api/views.py @@ -4,9 +4,6 @@ from pypika import Criterion @frappe.whitelist() def get_views(doctype): - if frappe.session.user == "Guest": - frappe.throw("Authentication failed", exc=frappe.AuthenticationError) - View = frappe.qb.DocType("CRM View Settings") query = ( frappe.qb.from_(View) diff --git a/crm/fcrm/doctype/crm_call_log/crm_call_log.py b/crm/fcrm/doctype/crm_call_log/crm_call_log.py index 6c646e4c..0147b9ac 100644 --- a/crm/fcrm/doctype/crm_call_log/crm_call_log.py +++ b/crm/fcrm/doctype/crm_call_log/crm_call_log.py @@ -93,7 +93,7 @@ def get_call_log(name): if c: return [c.full_name, c.image] return [None, None] - + def get_lead_contact(number): l = frappe.db.get_value("CRM Lead", {"mobile_no": number, "converted": 0}, ["lead_name", "image"], as_dict=True) if l: diff --git a/crm/fcrm/doctype/crm_form_script/crm_form_script.js b/crm/fcrm/doctype/crm_form_script/crm_form_script.js index 7f6f150e..9df643d5 100644 --- a/crm/fcrm/doctype/crm_form_script/crm_form_script.js +++ b/crm/fcrm/doctype/crm_form_script/crm_form_script.js @@ -1,8 +1,38 @@ // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -// frappe.ui.form.on("CRM Form Script", { -// refresh(frm) { +frappe.ui.form.on("CRM Form Script", { + refresh(frm) { + frm.set_query("dt", { + filters: { + istable: 0, + }, + }); + }, + view(frm) { + let has_form_boilerplate = frm.doc.script.includes( + "function setupForm(" + ); + let has_list_boilerplate = frm.doc.script.includes( + "function setupList(" + ); -// }, -// }); + if (frm.doc.view == "Form" && !has_form_boilerplate) { + frm.doc.script = ` +function setupForm({ doc }) { + return { + actions: [], + } +}`.trim(); + } + if (frm.doc.view == "List" && !has_list_boilerplate) { + frm.doc.script = ` +function setupList({ list }) { + return { + actions: [], + bulk_actions: [], + } +}`.trim(); + } + }, +}); diff --git a/crm/fcrm/doctype/crm_form_script/crm_form_script.json b/crm/fcrm/doctype/crm_form_script/crm_form_script.json index b883e804..9a026098 100644 --- a/crm/fcrm/doctype/crm_form_script/crm_form_script.json +++ b/crm/fcrm/doctype/crm_form_script/crm_form_script.json @@ -7,6 +7,7 @@ "engine": "InnoDB", "field_order": [ "dt", + "view", "column_break_gboh", "enabled", "section_break_xeox", @@ -36,16 +37,26 @@ "label": "Enabled" }, { - "default": "function setupForm({ doc }) {\n return {\n actions: []\n }\n}", + "default": "function setupForm({ doc }) {\n return {\n actions: [],\n }\n}", + "documentation_url": "https://docs.frappe.io/crm/custom-actions", "fieldname": "script", "fieldtype": "Code", "label": "Script", "options": "JS" + }, + { + "default": "Form", + "fieldname": "view", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Apply To", + "options": "Form\nList", + "set_only_once": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-01-19 21:52:46.078626", + "modified": "2024-04-10 18:27:17.617602", "modified_by": "Administrator", "module": "FCRM", "name": "CRM Form Script", diff --git a/crm/fcrm/doctype/crm_form_script/crm_form_script.py b/crm/fcrm/doctype/crm_form_script/crm_form_script.py index 32649d62..ff93ff4f 100644 --- a/crm/fcrm/doctype/crm_form_script/crm_form_script.py +++ b/crm/fcrm/doctype/crm_form_script/crm_form_script.py @@ -14,6 +14,7 @@ class CRMFormScript(Document): if self.dt and self.enabled: filters = { "dt": self.dt, + "view": self.view, "enabled": 1, } if self.name: @@ -27,13 +28,14 @@ class CRMFormScript(Document): frappe.DuplicateEntryError, ) -def get_form_script(dt): - """Returns the script for the given doctype""" +def get_form_script(dt, view="Form"): + """Returns the form script for the given doctype""" FormScript = frappe.qb.DocType("CRM Form Script") query = ( frappe.qb.from_(FormScript) .select("script") .where(FormScript.dt == dt) + .where(FormScript.view == view) .where(FormScript.enabled == 1) .limit(1) ) diff --git a/frontend/src/components/Filter.vue b/frontend/src/components/Filter.vue index be0dc765..76dbeabe 100644 --- a/frontend/src/components/Filter.vue +++ b/frontend/src/components/Filter.vue @@ -327,7 +327,7 @@ function getValSelect(f) { if (fieldtype == 'Dynamic Link') { return h(FormControl, { type: 'text' }) } - return h(Link, { class: 'form-control', doctype: options }) + return h(Link, { class: 'form-control', doctype: options, value: f.value }) } else if (typeNumber.includes(fieldtype)) { return h(FormControl, { type: 'number' }) } else if (typeDate.includes(fieldtype) && operator == 'between') { @@ -437,8 +437,6 @@ function updateOperator(event, filter) { function isSameTypeOperator(oldOperator, newOperator) { let textOperators = [ - 'like', - 'not like', 'equals', 'not equals', 'in', diff --git a/frontend/src/components/ListViews/CallLogsListView.vue b/frontend/src/components/ListViews/CallLogsListView.vue index 5d2cbe80..45c34b74 100644 --- a/frontend/src/components/ListViews/CallLogsListView.vue +++ b/frontend/src/components/ListViews/CallLogsListView.vue @@ -103,7 +103,7 @@ import { Dropdown, call, } from 'frappe-ui' -import { setupBulkActions, createToast } from '@/utils' +import { setupListActions, createToast } from '@/utils' import { globalStore } from '@/stores/global' import { onMounted, ref, watch } from 'vue' import { useRouter } from 'vue-router' @@ -181,6 +181,7 @@ function deleteValues(selections, unselectAll) { } const customBulkActions = ref([]) +const customListActions = ref([]) function bulkActions(selections, unselectAll) { let actions = [ @@ -209,7 +210,18 @@ function bulkActions(selections, unselectAll) { onMounted(() => { if (!list.value?.data) return - setupBulkActions(list.value.data) + setupListActions(list.value.data, { + list: list.value, + call, + createToast, + $dialog, + router, + }) customBulkActions.value = list.value?.data?.bulkActions || [] + customListActions.value = list.value?.data?.listActions || [] +}) + +defineExpose({ + customListActions, }) diff --git a/frontend/src/components/ListViews/ContactsListView.vue b/frontend/src/components/ListViews/ContactsListView.vue index 5a9781d7..64fc550b 100644 --- a/frontend/src/components/ListViews/ContactsListView.vue +++ b/frontend/src/components/ListViews/ContactsListView.vue @@ -103,7 +103,7 @@ import PhoneIcon from '@/components/Icons/PhoneIcon.vue' import EditValueModal from '@/components/Modals/EditValueModal.vue' import { globalStore } from '@/stores/global' -import { createToast } from '@/utils' +import { setupListActions, createToast } from '@/utils' import { Avatar, ListView, @@ -117,7 +117,8 @@ import { Dropdown, call, } from 'frappe-ui' -import { ref, watch } from 'vue' +import { ref, watch, onMounted } from 'vue' +import { useRouter } from 'vue-router' const props = defineProps({ rows: { @@ -150,6 +151,8 @@ const emit = defineEmits([ const pageLengthCount = defineModel() const list = defineModel('list') +const router = useRouter() + const { $dialog } = globalStore() watch(pageLengthCount, (val, old_value) => { @@ -199,6 +202,8 @@ function deleteValues(selections, unselectAll) { }) } +const customListActions = ref([]) + function bulkActions(selections, unselectAll) { let actions = [ { @@ -212,4 +217,21 @@ function bulkActions(selections, unselectAll) { ] return actions } + +onMounted(() => { + if (!list.value?.data) return + setupListActions(list.value.data, { + list: list.value, + call, + createToast, + $dialog, + router, + }) + // customBulkActions.value = list.value?.data?.bulkActions || [] + customListActions.value = list.value?.data?.listActions || [] +}) + +defineExpose({ + customListActions, +}) diff --git a/frontend/src/components/ListViews/DealsListView.vue b/frontend/src/components/ListViews/DealsListView.vue index e4988649..57a93e57 100644 --- a/frontend/src/components/ListViews/DealsListView.vue +++ b/frontend/src/components/ListViews/DealsListView.vue @@ -146,7 +146,7 @@ import { call, Tooltip, } from 'frappe-ui' -import { setupBulkActions, createToast } from '@/utils' +import { setupListActions, createToast } from '@/utils' import { globalStore } from '@/stores/global' import { onMounted, ref, watch } from 'vue' import { useRouter } from 'vue-router' @@ -234,6 +234,7 @@ function deleteValues(selections, unselectAll) { } const customBulkActions = ref([]) +const customListActions = ref([]) function bulkActions(selections, unselectAll) { let actions = [ @@ -266,7 +267,18 @@ function bulkActions(selections, unselectAll) { onMounted(() => { if (!list.value?.data) return - setupBulkActions(list.value.data) + setupListActions(list.value.data, { + list: list.value, + call, + createToast, + $dialog, + router, + }) customBulkActions.value = list.value?.data?.bulkActions || [] + customListActions.value = list.value?.data?.listActions || [] +}) + +defineExpose({ + customListActions, }) diff --git a/frontend/src/components/ListViews/EmailTemplatesListView.vue b/frontend/src/components/ListViews/EmailTemplatesListView.vue index 34afca15..b260d0e3 100644 --- a/frontend/src/components/ListViews/EmailTemplatesListView.vue +++ b/frontend/src/components/ListViews/EmailTemplatesListView.vue @@ -85,7 +85,7 @@ diff --git a/frontend/src/components/ListViews/LeadsListView.vue b/frontend/src/components/ListViews/LeadsListView.vue index 1483b574..a78a62ec 100644 --- a/frontend/src/components/ListViews/LeadsListView.vue +++ b/frontend/src/components/ListViews/LeadsListView.vue @@ -155,7 +155,7 @@ import { call, Tooltip, } from 'frappe-ui' -import { setupBulkActions, createToast } from '@/utils' +import { setupListActions, createToast } from '@/utils' import { globalStore } from '@/stores/global' import { onMounted, ref, watch } from 'vue' import { useRouter } from 'vue-router' @@ -243,6 +243,7 @@ function deleteValues(selections, unselectAll) { } const customBulkActions = ref([]) +const customListActions = ref([]) function bulkActions(selections, unselectAll) { let actions = [ @@ -275,7 +276,18 @@ function bulkActions(selections, unselectAll) { onMounted(() => { if (!list.value?.data) return - setupBulkActions(list.value.data) + setupListActions(list.value.data, { + list: list.value, + call, + createToast, + $dialog, + router, + }) customBulkActions.value = list.value?.data?.bulkActions || [] + customListActions.value = list.value?.data?.listActions || [] +}) + +defineExpose({ + customListActions, }) diff --git a/frontend/src/components/ListViews/OrganizationsListView.vue b/frontend/src/components/ListViews/OrganizationsListView.vue index cd8d677d..059f661b 100644 --- a/frontend/src/components/ListViews/OrganizationsListView.vue +++ b/frontend/src/components/ListViews/OrganizationsListView.vue @@ -88,7 +88,7 @@ diff --git a/frontend/src/components/ListViews/TasksListView.vue b/frontend/src/components/ListViews/TasksListView.vue index ab6b21a1..33898cf3 100644 --- a/frontend/src/components/ListViews/TasksListView.vue +++ b/frontend/src/components/ListViews/TasksListView.vue @@ -106,7 +106,7 @@ import CalendarIcon from '@/components/Icons/CalendarIcon.vue' import EditValueModal from '@/components/Modals/EditValueModal.vue' import { dateFormat } from '@/utils' import { globalStore } from '@/stores/global' -import { createToast } from '@/utils' +import { setupListActions, createToast } from '@/utils' import { Avatar, ListView, @@ -120,7 +120,8 @@ import { call, Tooltip, } from 'frappe-ui' -import { ref, watch } from 'vue' +import { ref, watch, onMounted } from 'vue' +import { useRouter } from 'vue-router' const props = defineProps({ rows: { @@ -154,6 +155,8 @@ const emit = defineEmits([ const pageLengthCount = defineModel() const list = defineModel('list') +const router = useRouter() + const { $dialog } = globalStore() watch(pageLengthCount, (val, old_value) => { @@ -203,6 +206,8 @@ function deleteValues(selections, unselectAll) { }) } +const customListActions = ref([]) + function bulkActions(selections, unselectAll) { let actions = [ { @@ -216,4 +221,21 @@ function bulkActions(selections, unselectAll) { ] return actions } + +onMounted(() => { + if (!list.value?.data) return + setupListActions(list.value.data, { + list: list.value, + call, + createToast, + $dialog, + router, + }) + // customBulkActions.value = list.value?.data?.bulkActions || [] + customListActions.value = list.value?.data?.listActions || [] +}) + +defineExpose({ + customListActions, +}) diff --git a/frontend/src/pages/CallLogs.vue b/frontend/src/pages/CallLogs.vue index 1c6b4ace..7d200827 100644 --- a/frontend/src/pages/CallLogs.vue +++ b/frontend/src/pages/CallLogs.vue @@ -3,6 +3,12 @@ +