diff --git a/crm/api/doc.py b/crm/api/doc.py index 9cc5c653..16e6b2ef 100644 --- a/crm/api/doc.py +++ b/crm/api/doc.py @@ -3,6 +3,7 @@ import json import frappe from frappe import _ from frappe.custom.doctype.property_setter.property_setter import make_property_setter +from frappe.desk.form.assign_to import set_status from frappe.model import no_value_fields from frappe.model.document import get_controller from frappe.utils import make_filter_tuple @@ -658,6 +659,24 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False, only_re return fields_meta +@frappe.whitelist() +def remove_assignments(doctype, name, assignees, ignore_permissions=False): + assignees = json.loads(assignees) + + if not assignees: + return + + for assign_to in assignees: + set_status( + doctype, + name, + todo=None, + assign_to=assign_to, + status="Cancelled", + ignore_permissions=ignore_permissions, + ) + +@frappe.whitelist() def get_assigned_users(doctype, name, default_assigned_to=None): assigned_users = frappe.get_all( "ToDo", diff --git a/crm/fcrm/doctype/crm_deal/api.py b/crm/fcrm/doctype/crm_deal/api.py index 5eaf2899..dd5d76df 100644 --- a/crm/fcrm/doctype/crm_deal/api.py +++ b/crm/fcrm/doctype/crm_deal/api.py @@ -13,7 +13,6 @@ def get_deal(name): 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) return deal diff --git a/crm/fcrm/doctype/crm_lead/api.py b/crm/fcrm/doctype/crm_lead/api.py index 77e4fc58..e826e888 100644 --- a/crm/fcrm/doctype/crm_lead/api.py +++ b/crm/fcrm/doctype/crm_lead/api.py @@ -13,5 +13,4 @@ def get_lead(name): lead["fields_meta"] = get_fields_meta("CRM Lead") lead["_form_script"] = get_form_script("CRM Lead") - lead["_assign"] = get_assigned_users("CRM Lead", lead.name) return lead diff --git a/frontend/src/components/Activities/Activities.vue b/frontend/src/components/Activities/Activities.vue index 3842a6dc..29733580 100644 --- a/frontend/src/components/Activities/Activities.vue +++ b/frontend/src/components/Activities/Activities.vue @@ -365,7 +365,11 @@
- +
{ + if (JSON.stringify(updatedDoc[key]) !== JSON.stringify(oldDoc[key])) { + acc[key] = updatedDoc[key] + } + return acc + }, {}) + + document.save.submit(null, { + onSuccess: () => emit('afterSave', changes), + }) } watch( diff --git a/frontend/src/components/Modals/AssignmentModal.vue b/frontend/src/components/Modals/AssignmentModal.vue index e0b99254..05cea454 100644 --- a/frontend/src/components/Modals/AssignmentModal.vue +++ b/frontend/src/components/Modals/AssignmentModal.vue @@ -145,13 +145,11 @@ function updateAssignees() { .map((assignee) => assignee.name) if (removedAssignees.length) { - for (let a of removedAssignees) { - call('frappe.desk.form.assign_to.remove', { - doctype: props.doctype, - name: props.doc.name, - assign_to: a, - }) - } + call('crm.api.doc.remove_assignments', { + doctype: props.doctype, + name: props.doc.name, + assignees: JSON.stringify(removedAssignees), + }) } if (addedAssignees.length) { diff --git a/frontend/src/components/SidePanelLayout.vue b/frontend/src/components/SidePanelLayout.vue index 9b794af9..3fe8fd1a 100644 --- a/frontend/src/components/SidePanelLayout.vue +++ b/frontend/src/components/SidePanelLayout.vue @@ -417,13 +417,13 @@ const props = defineProps({ }, }) +const emit = defineEmits(['afterFieldChange', 'reload']) + const { getFormattedPercent, getFormattedFloat, getFormattedCurrency } = getMeta(props.doctype) const { isManager, getUser } = usersStore() -const emit = defineEmits(['reload']) - const showSidePanelModal = ref(false) let document = { doc: {} } @@ -493,7 +493,13 @@ async function fieldChange(value, df) { await triggerOnChange(df.fieldname) - document.save.submit() + document.save.submit(null, { + onSuccess: () => { + emit('afterFieldChange', { + [df.fieldname]: value, + }) + }, + }) } function parsedSection(section, editButtonAdded) { diff --git a/frontend/src/data/document.js b/frontend/src/data/document.js index b8a7d911..a38e8ed6 100644 --- a/frontend/src/data/document.js +++ b/frontend/src/data/document.js @@ -1,6 +1,6 @@ import { getScript } from '@/data/script' -import { runSequentially } from '@/utils' -import { createDocumentResource, toast } from 'frappe-ui' +import { runSequentially, parseAssignees } from '@/utils' +import { createDocumentResource, createResource, toast } from 'frappe-ui' import { reactive } from 'vue' const documentsCache = {} @@ -35,6 +35,17 @@ export function useDocument(doctype, docname) { } } + const assignees = createResource({ + url: 'crm.api.doc.get_assigned_users', + cache: `assignees:${doctype}:${docname}`, + auto: true, + params: { + doctype: doctype, + name: docname, + }, + transform: (data) => parseAssignees(data), + }) + async function setupFormScript() { if ( controllersCache[doctype] && @@ -64,6 +75,8 @@ export function useDocument(doctype, docname) { organizedControllers[controllerKey].push(controller) } controllersCache[doctype][docname || ''] = organizedControllers + + triggerOnload() } function getControllers(row = null) { @@ -82,9 +95,16 @@ export function useDocument(doctype, docname) { return [] } + async function triggerOnload() { + const handler = async function () { + await this.onload?.() + } + await trigger(handler) + } + async function triggerOnRefresh() { const handler = async function () { - await this.refresh() + await this.refresh?.() } await trigger(handler) } @@ -177,6 +197,9 @@ export function useDocument(doctype, docname) { return { document: documentsCache[doctype][docname || ''], + assignees, + getControllers, + triggerOnload, triggerOnChange, triggerOnRowAdd, triggerOnRowRemove, diff --git a/frontend/src/data/script.js b/frontend/src/data/script.js index 9532fc11..b809f744 100644 --- a/frontend/src/data/script.js +++ b/frontend/src/data/script.js @@ -116,8 +116,13 @@ export function getScript(doctype, view = 'Form') { parentInstance = null, isChildDoctype = false, ) { + document.actions = document.actions || [] + document.statuses = document.statuses || [] + let instance = new FormClass() + // Store the original document context to be used by properties like 'actions' + instance._originalDocumentContext = document instance._isChildDoctype = isChildDoctype for (const key in document) { @@ -199,6 +204,76 @@ export function getScript(doctype, view = 'Form') { return createDocProxy(row, this) } } + + if (!Object.prototype.hasOwnProperty.call(FormClass.prototype, 'actions')) { + Object.defineProperty(FormClass.prototype, 'actions', { + configurable: true, + enumerable: true, + get() { + if (!this._originalDocumentContext) { + console.warn( + 'CRM Script: _originalDocumentContext not found on instance for actions getter.', + ) + return [] + } + + return this._originalDocumentContext.actions + }, + set(newValue) { + if (!this._originalDocumentContext) { + console.warn( + 'CRM Script: _originalDocumentContext not found on instance for actions setter.', + ) + return + } + if (!Array.isArray(newValue)) { + console.warn( + 'CRM Script: "actions" property must be an array. Value was not set.', + newValue, + ) + this._originalDocumentContext.actions = [] + return + } + this._originalDocumentContext.actions = newValue + }, + }) + } + + if ( + !Object.prototype.hasOwnProperty.call(FormClass.prototype, 'statuses') + ) { + Object.defineProperty(FormClass.prototype, 'statuses', { + configurable: true, + enumerable: true, + get() { + if (!this._originalDocumentContext) { + console.warn( + 'CRM Script: _originalDocumentContext not found on instance for statuses getter.', + ) + return [] + } + + return this._originalDocumentContext.statuses + }, + set(newValue) { + if (!this._originalDocumentContext) { + console.warn( + 'CRM Script: _originalDocumentContext not found on instance for statuses setter.', + ) + return + } + if (!Array.isArray(newValue)) { + console.warn( + 'CRM Script: "statuses" property must be an array. Value was not set.', + newValue, + ) + this._originalDocumentContext.statuses = [] + return + } + this._originalDocumentContext.statuses = newValue + }, + }) + } } // utility function to setup a form controller diff --git a/frontend/src/pages/Deal.vue b/frontend/src/pages/Deal.vue index 9d438146..9265db2e 100644 --- a/frontend/src/pages/Deal.vue +++ b/frontend/src/pages/Deal.vue @@ -12,13 +12,25 @@ v-if="deal.data._customActions?.length" :actions="deal.data._customActions" /> + @@ -134,6 +147,7 @@ doctype="CRM Deal" :docname="deal.data.name" @reload="sections.reload" + @afterFieldChange="reloadAssignees" >