Skip to content

Commit 1014864

Browse files
authored
Merge pull request #898 from frappe/main-hotfix
2 parents d9dee3f + 62270a4 commit 1014864

File tree

14 files changed

+264
-51
lines changed

14 files changed

+264
-51
lines changed

crm/api/doc.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import frappe
44
from frappe import _
55
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
6+
from frappe.desk.form.assign_to import set_status
67
from frappe.model import no_value_fields
78
from frappe.model.document import get_controller
89
from frappe.utils import make_filter_tuple
@@ -658,6 +659,24 @@ def get_fields_meta(doctype, restricted_fieldtypes=None, as_array=False, only_re
658659
return fields_meta
659660

660661

662+
@frappe.whitelist()
663+
def remove_assignments(doctype, name, assignees, ignore_permissions=False):
664+
assignees = json.loads(assignees)
665+
666+
if not assignees:
667+
return
668+
669+
for assign_to in assignees:
670+
set_status(
671+
doctype,
672+
name,
673+
todo=None,
674+
assign_to=assign_to,
675+
status="Cancelled",
676+
ignore_permissions=ignore_permissions,
677+
)
678+
679+
@frappe.whitelist()
661680
def get_assigned_users(doctype, name, default_assigned_to=None):
662681
assigned_users = frappe.get_all(
663682
"ToDo",

crm/fcrm/doctype/crm_deal/api.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ def get_deal(name):
1313

1414
deal["fields_meta"] = get_fields_meta("CRM Deal")
1515
deal["_form_script"] = get_form_script("CRM Deal")
16-
deal["_assign"] = get_assigned_users("CRM Deal", deal.name)
1716
return deal
1817

1918

crm/fcrm/doctype/crm_lead/api.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,4 @@ def get_lead(name):
1313

1414
lead["fields_meta"] = get_fields_meta("CRM Lead")
1515
lead["_form_script"] = get_form_script("CRM Lead")
16-
lead["_assign"] = get_assigned_users("CRM Lead", lead.name)
1716
return lead

frontend/src/components/Activities/Activities.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,11 @@
365365
</div>
366366
</div>
367367
<div v-else-if="title == 'Data'" class="h-full flex flex-col px-3 sm:px-10">
368-
<DataFields :doctype="doctype" :docname="doc.data.name" />
368+
<DataFields
369+
:doctype="doctype"
370+
:docname="doc.data.name"
371+
@afterSave="(data) => emit('afterSave', data)"
372+
/>
369373
</div>
370374
<div
371375
v-else
@@ -514,6 +518,8 @@ const props = defineProps({
514518
},
515519
})
516520
521+
const emit = defineEmits(['afterSave'])
522+
517523
const route = useRoute()
518524
519525
const doc = defineModel()

frontend/src/components/Activities/DataFields.vue

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ const props = defineProps({
7676
required: true,
7777
},
7878
})
79+
80+
const emit = defineEmits(['afterSave'])
81+
7982
const { isManager } = usersStore()
8083
8184
const showDataFieldsModal = ref(false)
@@ -90,7 +93,21 @@ const tabs = createResource({
9093
})
9194
9295
function saveChanges() {
93-
document.save.submit()
96+
if (!document.isDirty) return
97+
98+
const updatedDoc = { ...document.doc }
99+
const oldDoc = { ...document.originalDoc }
100+
101+
const changes = Object.keys(updatedDoc).reduce((acc, key) => {
102+
if (JSON.stringify(updatedDoc[key]) !== JSON.stringify(oldDoc[key])) {
103+
acc[key] = updatedDoc[key]
104+
}
105+
return acc
106+
}, {})
107+
108+
document.save.submit(null, {
109+
onSuccess: () => emit('afterSave', changes),
110+
})
94111
}
95112
96113
watch(

frontend/src/components/Modals/AssignmentModal.vue

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,11 @@ function updateAssignees() {
145145
.map((assignee) => assignee.name)
146146
147147
if (removedAssignees.length) {
148-
for (let a of removedAssignees) {
149-
call('frappe.desk.form.assign_to.remove', {
150-
doctype: props.doctype,
151-
name: props.doc.name,
152-
assign_to: a,
153-
})
154-
}
148+
call('crm.api.doc.remove_assignments', {
149+
doctype: props.doctype,
150+
name: props.doc.name,
151+
assignees: JSON.stringify(removedAssignees),
152+
})
155153
}
156154
157155
if (addedAssignees.length) {

frontend/src/components/SidePanelLayout.vue

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,13 @@ const props = defineProps({
417417
},
418418
})
419419
420+
const emit = defineEmits(['afterFieldChange', 'reload'])
421+
420422
const { getFormattedPercent, getFormattedFloat, getFormattedCurrency } =
421423
getMeta(props.doctype)
422424
423425
const { isManager, getUser } = usersStore()
424426
425-
const emit = defineEmits(['reload'])
426-
427427
const showSidePanelModal = ref(false)
428428
429429
let document = { doc: {} }
@@ -493,7 +493,13 @@ async function fieldChange(value, df) {
493493
494494
await triggerOnChange(df.fieldname)
495495
496-
document.save.submit()
496+
document.save.submit(null, {
497+
onSuccess: () => {
498+
emit('afterFieldChange', {
499+
[df.fieldname]: value,
500+
})
501+
},
502+
})
497503
}
498504
499505
function parsedSection(section, editButtonAdded) {

frontend/src/data/document.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getScript } from '@/data/script'
2-
import { runSequentially } from '@/utils'
3-
import { createDocumentResource, toast } from 'frappe-ui'
2+
import { runSequentially, parseAssignees } from '@/utils'
3+
import { createDocumentResource, createResource, toast } from 'frappe-ui'
44
import { reactive } from 'vue'
55

66
const documentsCache = {}
@@ -35,6 +35,17 @@ export function useDocument(doctype, docname) {
3535
}
3636
}
3737

38+
const assignees = createResource({
39+
url: 'crm.api.doc.get_assigned_users',
40+
cache: `assignees:${doctype}:${docname}`,
41+
auto: true,
42+
params: {
43+
doctype: doctype,
44+
name: docname,
45+
},
46+
transform: (data) => parseAssignees(data),
47+
})
48+
3849
async function setupFormScript() {
3950
if (
4051
controllersCache[doctype] &&
@@ -64,6 +75,8 @@ export function useDocument(doctype, docname) {
6475
organizedControllers[controllerKey].push(controller)
6576
}
6677
controllersCache[doctype][docname || ''] = organizedControllers
78+
79+
triggerOnload()
6780
}
6881

6982
function getControllers(row = null) {
@@ -82,9 +95,16 @@ export function useDocument(doctype, docname) {
8295
return []
8396
}
8497

98+
async function triggerOnload() {
99+
const handler = async function () {
100+
await this.onload?.()
101+
}
102+
await trigger(handler)
103+
}
104+
85105
async function triggerOnRefresh() {
86106
const handler = async function () {
87-
await this.refresh()
107+
await this.refresh?.()
88108
}
89109
await trigger(handler)
90110
}
@@ -177,6 +197,9 @@ export function useDocument(doctype, docname) {
177197

178198
return {
179199
document: documentsCache[doctype][docname || ''],
200+
assignees,
201+
getControllers,
202+
triggerOnload,
180203
triggerOnChange,
181204
triggerOnRowAdd,
182205
triggerOnRowRemove,

frontend/src/data/script.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,13 @@ export function getScript(doctype, view = 'Form') {
116116
parentInstance = null,
117117
isChildDoctype = false,
118118
) {
119+
document.actions = document.actions || []
120+
document.statuses = document.statuses || []
121+
119122
let instance = new FormClass()
120123

124+
// Store the original document context to be used by properties like 'actions'
125+
instance._originalDocumentContext = document
121126
instance._isChildDoctype = isChildDoctype
122127

123128
for (const key in document) {
@@ -199,6 +204,76 @@ export function getScript(doctype, view = 'Form') {
199204
return createDocProxy(row, this)
200205
}
201206
}
207+
208+
if (!Object.prototype.hasOwnProperty.call(FormClass.prototype, 'actions')) {
209+
Object.defineProperty(FormClass.prototype, 'actions', {
210+
configurable: true,
211+
enumerable: true,
212+
get() {
213+
if (!this._originalDocumentContext) {
214+
console.warn(
215+
'CRM Script: _originalDocumentContext not found on instance for actions getter.',
216+
)
217+
return []
218+
}
219+
220+
return this._originalDocumentContext.actions
221+
},
222+
set(newValue) {
223+
if (!this._originalDocumentContext) {
224+
console.warn(
225+
'CRM Script: _originalDocumentContext not found on instance for actions setter.',
226+
)
227+
return
228+
}
229+
if (!Array.isArray(newValue)) {
230+
console.warn(
231+
'CRM Script: "actions" property must be an array. Value was not set.',
232+
newValue,
233+
)
234+
this._originalDocumentContext.actions = []
235+
return
236+
}
237+
this._originalDocumentContext.actions = newValue
238+
},
239+
})
240+
}
241+
242+
if (
243+
!Object.prototype.hasOwnProperty.call(FormClass.prototype, 'statuses')
244+
) {
245+
Object.defineProperty(FormClass.prototype, 'statuses', {
246+
configurable: true,
247+
enumerable: true,
248+
get() {
249+
if (!this._originalDocumentContext) {
250+
console.warn(
251+
'CRM Script: _originalDocumentContext not found on instance for statuses getter.',
252+
)
253+
return []
254+
}
255+
256+
return this._originalDocumentContext.statuses
257+
},
258+
set(newValue) {
259+
if (!this._originalDocumentContext) {
260+
console.warn(
261+
'CRM Script: _originalDocumentContext not found on instance for statuses setter.',
262+
)
263+
return
264+
}
265+
if (!Array.isArray(newValue)) {
266+
console.warn(
267+
'CRM Script: "statuses" property must be an array. Value was not set.',
268+
newValue,
269+
)
270+
this._originalDocumentContext.statuses = []
271+
return
272+
}
273+
this._originalDocumentContext.statuses = newValue
274+
},
275+
})
276+
}
202277
}
203278

204279
// utility function to setup a form controller

frontend/src/pages/Deal.vue

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,25 @@
1212
v-if="deal.data._customActions?.length"
1313
:actions="deal.data._customActions"
1414
/>
15+
<CustomActions
16+
v-if="document.actions?.length"
17+
:actions="document.actions"
18+
/>
1519
<AssignTo
16-
v-model="deal.data._assignedTo"
17-
:data="deal.data"
20+
v-model="assignees.data"
21+
:data="document.doc"
1822
doctype="CRM Deal"
1923
/>
2024
<Dropdown
21-
:options="statusOptions('deal', updateField, deal.data._customStatuses)"
25+
:options="
26+
statusOptions(
27+
'deal',
28+
updateField,
29+
document.statuses?.length
30+
? document.statuses
31+
: deal.data._customStatuses,
32+
)
33+
"
2234
>
2335
<template #default="{ open }">
2436
<Button :label="deal.data.status">
@@ -46,6 +58,7 @@
4658
v-model:reload="reload"
4759
v-model:tabIndex="tabIndex"
4860
v-model="deal"
61+
@afterSave="reloadAssignees"
4962
/>
5063
</template>
5164
</Tabs>
@@ -134,6 +147,7 @@
134147
doctype="CRM Deal"
135148
:docname="deal.data.name"
136149
@reload="sections.reload"
150+
@afterFieldChange="reloadAssignees"
137151
>
138152
<template #actions="{ section }">
139153
<div v-if="section.name == 'contacts_section'" class="pr-2">
@@ -332,17 +346,13 @@ import Section from '@/components/Section.vue'
332346
import SidePanelLayout from '@/components/SidePanelLayout.vue'
333347
import SLASection from '@/components/SLASection.vue'
334348
import CustomActions from '@/components/CustomActions.vue'
335-
import {
336-
openWebsite,
337-
setupAssignees,
338-
setupCustomizations,
339-
copyToClipboard,
340-
} from '@/utils'
349+
import { openWebsite, setupCustomizations, copyToClipboard } from '@/utils'
341350
import { getView } from '@/utils/view'
342351
import { getSettings } from '@/stores/settings'
343352
import { globalStore } from '@/stores/global'
344353
import { statusesStore } from '@/stores/statuses'
345354
import { getMeta } from '@/stores/meta'
355+
import { useDocument } from '@/data/document'
346356
import { whatsappEnabled, callEnabled } from '@/composables/settings'
347357
import {
348358
createResource,
@@ -396,7 +406,6 @@ const deal = createResource({
396406
organization.fetch()
397407
}
398408
399-
setupAssignees(deal)
400409
setupCustomizations(deal, {
401410
doc: data,
402411
$dialog,
@@ -721,4 +730,12 @@ const activities = ref(null)
721730
function openEmailBox() {
722731
activities.value.emailBox.show = true
723732
}
733+
734+
const { assignees, document } = useDocument('CRM Deal', props.dealId)
735+
736+
function reloadAssignees(data) {
737+
if (data?.hasOwnProperty('deal_owner')) {
738+
assignees.reload()
739+
}
740+
}
724741
</script>

0 commit comments

Comments
 (0)