diff --git a/crm/api/doc.py b/crm/api/doc.py
index 4bcbe3cc..f08c4ade 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
@@ -659,6 +660,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 @@
-
+ emit('afterSave', data)"
+ />
{
+ 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..8760ccbb 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] &&
@@ -177,6 +188,7 @@ export function useDocument(doctype, docname) {
return {
document: documentsCache[doctype][docname || ''],
+ assignees,
triggerOnChange,
triggerOnRowAdd,
triggerOnRowRemove,
diff --git a/frontend/src/pages/Deal.vue b/frontend/src/pages/Deal.vue
index 9d438146..d9b29299 100644
--- a/frontend/src/pages/Deal.vue
+++ b/frontend/src/pages/Deal.vue
@@ -13,8 +13,8 @@
:actions="deal.data._customActions"
/>
@@ -134,6 +135,7 @@
doctype="CRM Deal"
:docname="deal.data.name"
@reload="sections.reload"
+ @afterFieldChange="reloadAssignees"
>
@@ -332,17 +334,13 @@ import Section from '@/components/Section.vue'
import SidePanelLayout from '@/components/SidePanelLayout.vue'
import SLASection from '@/components/SLASection.vue'
import CustomActions from '@/components/CustomActions.vue'
-import {
- openWebsite,
- setupAssignees,
- setupCustomizations,
- copyToClipboard,
-} from '@/utils'
+import { openWebsite, setupCustomizations, copyToClipboard } from '@/utils'
import { getView } from '@/utils/view'
import { getSettings } from '@/stores/settings'
import { globalStore } from '@/stores/global'
import { statusesStore } from '@/stores/statuses'
import { getMeta } from '@/stores/meta'
+import { useDocument } from '@/data/document'
import { whatsappEnabled, callEnabled } from '@/composables/settings'
import {
createResource,
@@ -396,7 +394,6 @@ const deal = createResource({
organization.fetch()
}
- setupAssignees(deal)
setupCustomizations(deal, {
doc: data,
$dialog,
@@ -721,4 +718,12 @@ const activities = ref(null)
function openEmailBox() {
activities.value.emailBox.show = true
}
+
+const { assignees, document } = useDocument('CRM Deal', props.dealId)
+
+function reloadAssignees(data) {
+ if (data?.hasOwnProperty('deal_owner')) {
+ assignees.reload()
+ }
+}
diff --git a/frontend/src/pages/Lead.vue b/frontend/src/pages/Lead.vue
index f4755e0b..a13ec2a2 100644
--- a/frontend/src/pages/Lead.vue
+++ b/frontend/src/pages/Lead.vue
@@ -13,8 +13,8 @@
:actions="lead.data._customActions"
/>
@@ -186,6 +187,7 @@
doctype="CRM Lead"
:docname="lead.data.name"
@reload="sections.reload"
+ @afterFieldChange="reloadAssignees"
/>
@@ -335,12 +337,7 @@ import SidePanelLayout from '@/components/SidePanelLayout.vue'
import FieldLayout from '@/components/FieldLayout/FieldLayout.vue'
import SLASection from '@/components/SLASection.vue'
import CustomActions from '@/components/CustomActions.vue'
-import {
- openWebsite,
- setupAssignees,
- setupCustomizations,
- copyToClipboard,
-} from '@/utils'
+import { openWebsite, setupCustomizations, copyToClipboard } from '@/utils'
import { showQuickEntryModal, quickEntryProps } from '@/composables/modals'
import { getView } from '@/utils/view'
import { getSettings } from '@/stores/settings'
@@ -403,7 +400,6 @@ const lead = createResource({
onSuccess: (data) => {
errorTitle.value = ''
errorMessage.value = ''
- setupAssignees(lead)
setupCustomizations(lead, {
doc: data,
$dialog,
@@ -609,7 +605,10 @@ const existingOrganizationChecked = ref(false)
const existingContact = ref('')
const existingOrganization = ref('')
-const { triggerConvertToDeal } = useDocument('CRM Lead', props.leadId)
+const { triggerConvertToDeal, assignees, document } = useDocument(
+ 'CRM Lead',
+ props.leadId,
+)
async function convertToDeal() {
if (existingContactChecked.value && !existingContact.value) {
@@ -711,4 +710,10 @@ function openQuickEntryModal() {
}
showConvertToDealModal.value = false
}
+
+function reloadAssignees(data) {
+ if (data?.hasOwnProperty('lead_owner')) {
+ assignees.reload()
+ }
+}
diff --git a/frontend/src/pages/MobileDeal.vue b/frontend/src/pages/MobileDeal.vue
index 44dd6207..9a1b91f9 100644
--- a/frontend/src/pages/MobileDeal.vue
+++ b/frontend/src/pages/MobileDeal.vue
@@ -36,8 +36,8 @@
class="flex h-12 items-center justify-between gap-2 border-b px-3 py-2.5"
>
@@ -66,6 +66,7 @@
doctype="CRM Deal"
:docname="deal.data.name"
@reload="sections.reload"
+ @afterFieldChange="reloadAssignees"
>
@@ -258,12 +259,13 @@ import Link from '@/components/Controls/Link.vue'
import SidePanelLayout from '@/components/SidePanelLayout.vue'
import SLASection from '@/components/SLASection.vue'
import CustomActions from '@/components/CustomActions.vue'
-import { setupAssignees, setupCustomizations } from '@/utils'
+import { setupCustomizations } from '@/utils'
import { getView } from '@/utils/view'
import { getSettings } from '@/stores/settings'
import { globalStore } from '@/stores/global'
import { statusesStore } from '@/stores/statuses'
import { getMeta } from '@/stores/meta'
+import { useDocument } from '@/data/document'
import {
whatsappEnabled,
callEnabled,
@@ -311,7 +313,6 @@ const deal = createResource({
organization.fetch()
}
- setupAssignees(deal)
setupCustomizations(deal, {
doc: data,
$dialog,
@@ -605,4 +606,12 @@ async function deleteDeal(name) {
})
router.push({ name: 'Deals' })
}
+
+const { assignees, document } = useDocument('CRM Deal', props.dealId)
+
+function reloadAssignees(data) {
+ if (data?.hasOwnProperty('deal_owner')) {
+ assignees.reload()
+ }
+}
diff --git a/frontend/src/pages/MobileLead.vue b/frontend/src/pages/MobileLead.vue
index a103560c..3d12c970 100644
--- a/frontend/src/pages/MobileLead.vue
+++ b/frontend/src/pages/MobileLead.vue
@@ -36,8 +36,8 @@
class="flex h-12 items-center justify-between gap-2 border-b px-3 py-2.5"
>
@@ -71,6 +71,7 @@
doctype="CRM Lead"
:docname="lead.data.name"
@reload="sections.reload"
+ @afterFieldChange="reloadAssignees"
/>
@@ -173,12 +174,13 @@ import Link from '@/components/Controls/Link.vue'
import SidePanelLayout from '@/components/SidePanelLayout.vue'
import SLASection from '@/components/SLASection.vue'
import CustomActions from '@/components/CustomActions.vue'
-import { setupAssignees, setupCustomizations } from '@/utils'
+import { setupCustomizations } from '@/utils'
import { getView } from '@/utils/view'
import { getSettings } from '@/stores/settings'
import { globalStore } from '@/stores/global'
import { statusesStore } from '@/stores/statuses'
import { getMeta } from '@/stores/meta'
+import { useDocument } from '@/data/document'
import {
whatsappEnabled,
callEnabled,
@@ -220,7 +222,6 @@ const lead = createResource({
params: { name: props.leadId },
cache: ['lead', props.leadId],
onSuccess: (data) => {
- setupAssignees(lead)
setupCustomizations(lead, {
doc: data,
$dialog,
@@ -454,4 +455,12 @@ async function convertToDeal() {
router.push({ name: 'Deal', params: { dealId: deal } })
}
}
+
+const { assignees, document } = useDocument('CRM Lead', props.leadId)
+
+function reloadAssignees(data) {
+ if (data?.hasOwnProperty('lead_owner')) {
+ assignees.reload()
+ }
+}
diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.js
index 924161dd..82911a00 100644
--- a/frontend/src/utils/index.js
+++ b/frontend/src/utils/index.js
@@ -211,10 +211,9 @@ export function validateEmail(email) {
return regExp.test(email)
}
-export function setupAssignees(doc) {
+export function parseAssignees(assignees) {
let { getUser } = usersStore()
- let assignees = doc.data?._assign || []
- doc.data._assignedTo = assignees.map((user) => ({
+ return assignees.map((user) => ({
name: user,
image: getUser(user).user_image,
label: getUser(user).full_name,