Merge pull request #920 from shariquerik/forecasting
This commit is contained in:
commit
20405be86c
@ -11,11 +11,14 @@
|
|||||||
"naming_series",
|
"naming_series",
|
||||||
"organization",
|
"organization",
|
||||||
"next_step",
|
"next_step",
|
||||||
"probability",
|
|
||||||
"column_break_ijan",
|
"column_break_ijan",
|
||||||
"status",
|
"status",
|
||||||
"close_date",
|
|
||||||
"deal_owner",
|
"deal_owner",
|
||||||
|
"section_break_jgpm",
|
||||||
|
"probability",
|
||||||
|
"deal_value",
|
||||||
|
"column_break_kpxa",
|
||||||
|
"close_date",
|
||||||
"contacts_tab",
|
"contacts_tab",
|
||||||
"contacts",
|
"contacts",
|
||||||
"contact",
|
"contact",
|
||||||
@ -91,7 +94,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "close_date",
|
"fieldname": "close_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Close Date"
|
"label": "Expected Closure Date"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "next_step",
|
"fieldname": "next_step",
|
||||||
@ -374,12 +377,26 @@
|
|||||||
"label": "Net Total",
|
"label": "Net Total",
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_jgpm",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "deal_value",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Deal Value",
|
||||||
|
"options": "currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_kpxa",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-05-12 12:30:55.415282",
|
"modified": "2025-06-11 12:58:22.439045",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "CRM Deal",
|
"name": "CRM Deal",
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class CRMDeal(Document):
|
|||||||
self.assign_agent(self.deal_owner)
|
self.assign_agent(self.deal_owner)
|
||||||
if self.has_value_changed("status"):
|
if self.has_value_changed("status"):
|
||||||
add_status_change_log(self)
|
add_status_change_log(self)
|
||||||
|
self.update_close_date()
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
if self.deal_owner:
|
if self.deal_owner:
|
||||||
@ -133,6 +134,13 @@ class CRMDeal(Document):
|
|||||||
if sla:
|
if sla:
|
||||||
sla.apply(self)
|
sla.apply(self)
|
||||||
|
|
||||||
|
def update_close_date(self):
|
||||||
|
"""
|
||||||
|
Update the close date based on the "Won" status.
|
||||||
|
"""
|
||||||
|
if self.status == "Won" and not self.close_date:
|
||||||
|
self.close_date = frappe.utils.nowdate()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default_list_data():
|
def default_list_data():
|
||||||
columns = [
|
columns = [
|
||||||
|
|||||||
@ -8,7 +8,8 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"deal_status",
|
"deal_status",
|
||||||
"color",
|
"color",
|
||||||
"position"
|
"position",
|
||||||
|
"probability"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -32,11 +33,17 @@
|
|||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Position"
|
"label": "Position"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "probability",
|
||||||
|
"fieldtype": "Percent",
|
||||||
|
"label": "Probability"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"grid_page_length": 50,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-01-19 21:56:44.552134",
|
"modified": "2025-06-11 13:00:34.518808",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "CRM Deal Status",
|
"name": "CRM Deal Status",
|
||||||
@ -68,7 +75,8 @@
|
|||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"row_format": "Dynamic",
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": []
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"defaults_tab",
|
"defaults_tab",
|
||||||
"restore_defaults",
|
"restore_defaults",
|
||||||
|
"enable_forecasting",
|
||||||
"branding_tab",
|
"branding_tab",
|
||||||
"brand_name",
|
"brand_name",
|
||||||
"brand_logo",
|
"brand_logo",
|
||||||
@ -28,7 +29,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "defaults_tab",
|
"fieldname": "defaults_tab",
|
||||||
"fieldtype": "Tab Break",
|
"fieldtype": "Tab Break",
|
||||||
"label": "Defaults"
|
"label": "Settings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "branding_tab",
|
"fieldname": "branding_tab",
|
||||||
@ -56,12 +57,19 @@
|
|||||||
"fieldname": "favicon",
|
"fieldname": "favicon",
|
||||||
"fieldtype": "Attach",
|
"fieldtype": "Attach",
|
||||||
"label": "Favicon"
|
"label": "Favicon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "It will make deal's \"Expected Closure Date\" mandatory to get accurate forecasting insights",
|
||||||
|
"fieldname": "enable_forecasting",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Enable Forecasting"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-02-20 12:38:38.088477",
|
"modified": "2025-06-11 19:12:16.762499",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "FCRM Settings",
|
"name": "FCRM Settings",
|
||||||
@ -95,7 +103,8 @@
|
|||||||
"share": 1
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"row_format": "Dynamic",
|
||||||
"sort_field": "creation",
|
"sort_field": "creation",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": []
|
"states": []
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.custom.doctype.property_setter.property_setter import delete_property_setter, make_property_setter
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
from crm.install import after_install
|
from crm.install import after_install
|
||||||
@ -15,6 +16,7 @@ class FCRMSettings(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.do_not_allow_to_delete_if_standard()
|
self.do_not_allow_to_delete_if_standard()
|
||||||
|
self.setup_forecasting()
|
||||||
|
|
||||||
def do_not_allow_to_delete_if_standard(self):
|
def do_not_allow_to_delete_if_standard(self):
|
||||||
if not self.has_value_changed("dropdown_items"):
|
if not self.has_value_changed("dropdown_items"):
|
||||||
@ -29,6 +31,23 @@ class FCRMSettings(Document):
|
|||||||
return
|
return
|
||||||
frappe.throw(_("Cannot delete standard items {0}").format(", ".join(deleted_standard_items)))
|
frappe.throw(_("Cannot delete standard items {0}").format(", ".join(deleted_standard_items)))
|
||||||
|
|
||||||
|
def setup_forecasting(self):
|
||||||
|
if self.has_value_changed("enable_forecasting"):
|
||||||
|
if not self.enable_forecasting:
|
||||||
|
delete_property_setter(
|
||||||
|
"CRM Deal",
|
||||||
|
"reqd",
|
||||||
|
"close_date",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
make_property_setter(
|
||||||
|
"CRM Deal",
|
||||||
|
"close_date",
|
||||||
|
"reqd",
|
||||||
|
1 if self.enable_forecasting else 0,
|
||||||
|
"Check",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_standard_dropdown_items():
|
def get_standard_dropdown_items():
|
||||||
return [item.get("name1") for item in frappe.get_hooks("standard_dropdown_items")]
|
return [item.get("name1") for item in frappe.get_hooks("standard_dropdown_items")]
|
||||||
@ -57,3 +76,36 @@ def sync_table(key, hook):
|
|||||||
crm_settings.set(key, items)
|
crm_settings.set(key, items)
|
||||||
|
|
||||||
crm_settings.save()
|
crm_settings.save()
|
||||||
|
|
||||||
|
|
||||||
|
def create_forecasting_script():
|
||||||
|
if not frappe.db.exists("CRM Form Script", "Forecasting Script"):
|
||||||
|
script = get_forecasting_script()
|
||||||
|
frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "CRM Form Script",
|
||||||
|
"name": "Forecasting Script",
|
||||||
|
"dt": "CRM Deal",
|
||||||
|
"view": "Form",
|
||||||
|
"script": script,
|
||||||
|
"enabled": 1,
|
||||||
|
"is_standard": 1,
|
||||||
|
}
|
||||||
|
).insert()
|
||||||
|
|
||||||
|
|
||||||
|
def get_forecasting_script():
|
||||||
|
return """class CRMDeal {
|
||||||
|
async status() {
|
||||||
|
await this.doc.trigger('updateProbability')
|
||||||
|
}
|
||||||
|
async updateProbability() {
|
||||||
|
let status = await call("frappe.client.get_value", {
|
||||||
|
doctype: "CRM Deal Status",
|
||||||
|
fieldname: "probability",
|
||||||
|
filters: { name: this.doc.status },
|
||||||
|
})
|
||||||
|
|
||||||
|
this.doc.probability = status.probability
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|||||||
@ -359,5 +359,8 @@ def add_standard_dropdown_items():
|
|||||||
|
|
||||||
|
|
||||||
def add_default_scripts():
|
def add_default_scripts():
|
||||||
|
from crm.fcrm.doctype.fcrm_settings.fcrm_settings import create_forecasting_script
|
||||||
|
|
||||||
for doctype in ["CRM Lead", "CRM Deal"]:
|
for doctype in ["CRM Lead", "CRM Deal"]:
|
||||||
create_product_details_script(doctype)
|
create_product_details_script(doctype)
|
||||||
|
create_forecasting_script()
|
||||||
|
|||||||
@ -12,4 +12,4 @@ crm.patches.v1_0.create_default_sidebar_fields_layout
|
|||||||
crm.patches.v1_0.update_deal_quick_entry_layout
|
crm.patches.v1_0.update_deal_quick_entry_layout
|
||||||
crm.patches.v1_0.update_layouts_to_new_format
|
crm.patches.v1_0.update_layouts_to_new_format
|
||||||
crm.patches.v1_0.move_twilio_agent_to_telephony_agent
|
crm.patches.v1_0.move_twilio_agent_to_telephony_agent
|
||||||
crm.patches.v1_0.create_default_scripts
|
crm.patches.v1_0.create_default_scripts # 13-06-2025
|
||||||
@ -509,8 +509,7 @@ const deleteRows = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fieldChange(value, field, row) {
|
function fieldChange(value, field, row) {
|
||||||
row[field.fieldname] = value
|
triggerOnChange(field.fieldname, value, row)
|
||||||
triggerOnChange(field.fieldname, row)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultValue(defaultValue, fieldtype) {
|
function getDefaultValue(defaultValue, fieldtype) {
|
||||||
|
|||||||
@ -332,12 +332,10 @@ const getPlaceholder = (field) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fieldChange(value, df) {
|
function fieldChange(value, df) {
|
||||||
data.value[df.fieldname] = value
|
|
||||||
|
|
||||||
if (isGridRow) {
|
if (isGridRow) {
|
||||||
triggerOnChange(df.fieldname, data.value)
|
triggerOnChange(df.fieldname, value, data.value)
|
||||||
} else {
|
} else {
|
||||||
triggerOnChange(df.fieldname)
|
triggerOnChange(df.fieldname, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -94,7 +94,7 @@ const show = defineModel()
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const error = ref(null)
|
const error = ref(null)
|
||||||
|
|
||||||
const { document: deal } = useDocument('CRM Deal')
|
const { document: deal, triggerOnChange } = useDocument('CRM Deal')
|
||||||
|
|
||||||
const hasOrganizationSections = ref(true)
|
const hasOrganizationSections = ref(true)
|
||||||
const hasContactSections = ref(true)
|
const hasContactSections = ref(true)
|
||||||
@ -164,7 +164,7 @@ const tabs = createResource({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const dealStatuses = computed(() => {
|
const dealStatuses = computed(() => {
|
||||||
let statuses = statusOptions('deal')
|
let statuses = statusOptions('deal', null, [], triggerOnChange)
|
||||||
if (!deal.doc.status) {
|
if (!deal.doc.status) {
|
||||||
deal.doc.status = statuses[0].value
|
deal.doc.status = statuses[0].value
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,10 +70,10 @@ const router = useRouter()
|
|||||||
const error = ref(null)
|
const error = ref(null)
|
||||||
const isLeadCreating = ref(false)
|
const isLeadCreating = ref(false)
|
||||||
|
|
||||||
const { document: lead } = useDocument('CRM Lead')
|
const { document: lead, triggerOnChange } = useDocument('CRM Lead')
|
||||||
|
|
||||||
const leadStatuses = computed(() => {
|
const leadStatuses = computed(() => {
|
||||||
let statuses = statusOptions('lead')
|
let statuses = statusOptions('lead', null, [], triggerOnChange)
|
||||||
if (!lead.doc.status) {
|
if (!lead.doc.status) {
|
||||||
lead.doc.status = statuses?.[0]?.value
|
lead.doc.status = statuses?.[0]?.value
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,18 +44,21 @@
|
|||||||
>
|
>
|
||||||
<Tooltip :text="__(field.label)" :hoverDelay="1">
|
<Tooltip :text="__(field.label)" :hoverDelay="1">
|
||||||
<div
|
<div
|
||||||
class="w-[35%] min-w-20 shrink-0 truncate text-sm text-ink-gray-5"
|
class="w-[35%] min-w-20 shrink-0 flex items-center gap-0.5"
|
||||||
>
|
>
|
||||||
{{ __(field.label) }}
|
<div class="truncate text-sm text-ink-gray-5">
|
||||||
<span
|
{{ __(field.label) }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
field.reqd ||
|
field.reqd ||
|
||||||
(field.mandatory_depends_on &&
|
(field.mandatory_depends_on &&
|
||||||
field.mandatory_via_depends_on)
|
field.mandatory_via_depends_on)
|
||||||
"
|
"
|
||||||
class="text-ink-red-2"
|
class="text-ink-red-2"
|
||||||
>*</span
|
|
||||||
>
|
>
|
||||||
|
*
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div class="flex items-center justify-between w-[65%]">
|
<div class="flex items-center justify-between w-[65%]">
|
||||||
@ -489,9 +492,7 @@ function parsedField(field) {
|
|||||||
async function fieldChange(value, df) {
|
async function fieldChange(value, df) {
|
||||||
if (props.preview) return
|
if (props.preview) return
|
||||||
|
|
||||||
document.doc[df.fieldname] = value
|
await triggerOnChange(df.fieldname, value)
|
||||||
|
|
||||||
await triggerOnChange(df.fieldname)
|
|
||||||
|
|
||||||
document.save.submit(null, {
|
document.save.submit(null, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
|||||||
@ -117,20 +117,26 @@ export function useDocument(doctype, docname) {
|
|||||||
await trigger(handler)
|
await trigger(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function triggerOnChange(fieldname, row) {
|
async function triggerOnChange(fieldname, value, row) {
|
||||||
|
const oldValue = documentsCache[doctype][docname || ''].doc[fieldname]
|
||||||
|
documentsCache[doctype][docname || ''].doc[fieldname] = value
|
||||||
|
|
||||||
const handler = async function () {
|
const handler = async function () {
|
||||||
|
this.value = value
|
||||||
|
this.oldValue = oldValue
|
||||||
if (row) {
|
if (row) {
|
||||||
this.currentRowIdx = row.idx
|
this.currentRowIdx = row.idx
|
||||||
this.value = row[fieldname]
|
|
||||||
this.oldValue = getOldValue(fieldname, row)
|
|
||||||
} else {
|
|
||||||
this.value = documentsCache[doctype][docname || ''].doc[fieldname]
|
|
||||||
this.oldValue = getOldValue(fieldname)
|
|
||||||
}
|
}
|
||||||
await this[fieldname]?.()
|
await this[fieldname]?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
await trigger(handler, row)
|
try {
|
||||||
|
await trigger(handler, row)
|
||||||
|
} catch (error) {
|
||||||
|
documentsCache[doctype][docname || ''].doc[fieldname] = oldValue
|
||||||
|
console.error(handler)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function triggerOnRowAdd(row) {
|
async function triggerOnRowAdd(row) {
|
||||||
|
|||||||
@ -46,6 +46,11 @@ export function getScript(doctype, view = 'Form') {
|
|||||||
helpers.router = router
|
helpers.router = router
|
||||||
helpers.call = call
|
helpers.call = call
|
||||||
|
|
||||||
|
helpers.throwError = (message) => {
|
||||||
|
toast.error(message || __('An error occurred'))
|
||||||
|
throw new Error(message || __('An error occurred'))
|
||||||
|
}
|
||||||
|
|
||||||
helpers.crm = {
|
helpers.crm = {
|
||||||
makePhoneCall: makeCall,
|
makePhoneCall: makeCall,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,14 @@
|
|||||||
/>
|
/>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-if="document.doc"
|
v-if="document.doc"
|
||||||
:options="statusOptions('deal', document, deal.data._customStatuses)"
|
:options="
|
||||||
|
statusOptions(
|
||||||
|
'deal',
|
||||||
|
document,
|
||||||
|
deal.data._customStatuses,
|
||||||
|
triggerOnChange,
|
||||||
|
)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
<Button :label="document.doc.status">
|
<Button :label="document.doc.status">
|
||||||
@ -726,7 +733,10 @@ function openEmailBox() {
|
|||||||
activities.value.emailBox.show = true
|
activities.value.emailBox.show = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const { assignees, document } = useDocument('CRM Deal', props.dealId)
|
const { assignees, document, triggerOnChange } = useDocument(
|
||||||
|
'CRM Deal',
|
||||||
|
props.dealId,
|
||||||
|
)
|
||||||
|
|
||||||
function reloadAssignees(data) {
|
function reloadAssignees(data) {
|
||||||
if (data?.hasOwnProperty('deal_owner')) {
|
if (data?.hasOwnProperty('deal_owner')) {
|
||||||
|
|||||||
@ -23,7 +23,14 @@
|
|||||||
/>
|
/>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-if="document.doc"
|
v-if="document.doc"
|
||||||
:options="statusOptions('lead', document, lead.data._customStatuses)"
|
:options="
|
||||||
|
statusOptions(
|
||||||
|
'lead',
|
||||||
|
document,
|
||||||
|
lead.data._customStatuses,
|
||||||
|
triggerOnChange,
|
||||||
|
)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
<Button :label="document.doc.status">
|
<Button :label="document.doc.status">
|
||||||
@ -344,7 +351,12 @@ import SidePanelLayout from '@/components/SidePanelLayout.vue'
|
|||||||
import FieldLayout from '@/components/FieldLayout/FieldLayout.vue'
|
import FieldLayout from '@/components/FieldLayout/FieldLayout.vue'
|
||||||
import SLASection from '@/components/SLASection.vue'
|
import SLASection from '@/components/SLASection.vue'
|
||||||
import CustomActions from '@/components/CustomActions.vue'
|
import CustomActions from '@/components/CustomActions.vue'
|
||||||
import { openWebsite, setupCustomizations, copyToClipboard, validateIsImageFile } from '@/utils'
|
import {
|
||||||
|
openWebsite,
|
||||||
|
setupCustomizations,
|
||||||
|
copyToClipboard,
|
||||||
|
validateIsImageFile,
|
||||||
|
} from '@/utils'
|
||||||
import { showQuickEntryModal, quickEntryProps } from '@/composables/modals'
|
import { showQuickEntryModal, quickEntryProps } from '@/composables/modals'
|
||||||
import { getView } from '@/utils/view'
|
import { getView } from '@/utils/view'
|
||||||
import { getSettings } from '@/stores/settings'
|
import { getSettings } from '@/stores/settings'
|
||||||
@ -605,10 +617,8 @@ const existingOrganizationChecked = ref(false)
|
|||||||
const existingContact = ref('')
|
const existingContact = ref('')
|
||||||
const existingOrganization = ref('')
|
const existingOrganization = ref('')
|
||||||
|
|
||||||
const { triggerConvertToDeal, assignees, document } = useDocument(
|
const { triggerConvertToDeal, triggerOnChange, assignees, document } =
|
||||||
'CRM Lead',
|
useDocument('CRM Lead', props.leadId)
|
||||||
props.leadId,
|
|
||||||
)
|
|
||||||
|
|
||||||
async function convertToDeal() {
|
async function convertToDeal() {
|
||||||
if (existingContactChecked.value && !existingContact.value) {
|
if (existingContactChecked.value && !existingContact.value) {
|
||||||
|
|||||||
@ -11,7 +11,14 @@
|
|||||||
<div class="absolute right-0">
|
<div class="absolute right-0">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-if="document.doc"
|
v-if="document.doc"
|
||||||
:options="statusOptions('deal', document, deal.data._customStatuses)"
|
:options="
|
||||||
|
statusOptions(
|
||||||
|
'deal',
|
||||||
|
document,
|
||||||
|
deal.data._customStatuses,
|
||||||
|
triggerOnChange,
|
||||||
|
)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
<Button :label="document.doc.status">
|
<Button :label="document.doc.status">
|
||||||
@ -612,7 +619,10 @@ async function deleteDeal(name) {
|
|||||||
router.push({ name: 'Deals' })
|
router.push({ name: 'Deals' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { assignees, document } = useDocument('CRM Deal', props.dealId)
|
const { assignees, document, triggerOnChange } = useDocument(
|
||||||
|
'CRM Deal',
|
||||||
|
props.dealId,
|
||||||
|
)
|
||||||
|
|
||||||
function reloadAssignees(data) {
|
function reloadAssignees(data) {
|
||||||
if (data?.hasOwnProperty('deal_owner')) {
|
if (data?.hasOwnProperty('deal_owner')) {
|
||||||
|
|||||||
@ -11,7 +11,14 @@
|
|||||||
<div class="absolute right-0">
|
<div class="absolute right-0">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-if="document.doc"
|
v-if="document.doc"
|
||||||
:options="statusOptions('lead', document, lead.data._customStatuses)"
|
:options="
|
||||||
|
statusOptions(
|
||||||
|
'lead',
|
||||||
|
document,
|
||||||
|
lead.data._customStatuses,
|
||||||
|
triggerOnChange,
|
||||||
|
)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template #default="{ open }">
|
<template #default="{ open }">
|
||||||
<Button :label="document.doc.status">
|
<Button :label="document.doc.status">
|
||||||
@ -461,7 +468,10 @@ async function convertToDeal() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { assignees, document } = useDocument('CRM Lead', props.leadId)
|
const { assignees, document, triggerOnChange } = useDocument(
|
||||||
|
'CRM Lead',
|
||||||
|
props.leadId,
|
||||||
|
)
|
||||||
|
|
||||||
function reloadAssignees(data) {
|
function reloadAssignees(data) {
|
||||||
if (data?.hasOwnProperty('lead_owner')) {
|
if (data?.hasOwnProperty('lead_owner')) {
|
||||||
|
|||||||
@ -77,7 +77,12 @@ export const statusesStore = defineStore('crm-statuses', () => {
|
|||||||
return communicationStatuses[name]
|
return communicationStatuses[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusOptions(doctype, document, statuses = []) {
|
function statusOptions(
|
||||||
|
doctype,
|
||||||
|
document,
|
||||||
|
statuses = [],
|
||||||
|
triggerOnChange = null,
|
||||||
|
) {
|
||||||
let statusesByName =
|
let statusesByName =
|
||||||
doctype == 'deal' ? dealStatusesByName : leadStatusesByName
|
doctype == 'deal' ? dealStatusesByName : leadStatusesByName
|
||||||
|
|
||||||
@ -85,7 +90,7 @@ export const statusesStore = defineStore('crm-statuses', () => {
|
|||||||
statuses = document.statuses
|
statuses = document.statuses
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statuses.length) {
|
if (statuses?.length) {
|
||||||
statusesByName = statuses.reduce((acc, status) => {
|
statusesByName = statuses.reduce((acc, status) => {
|
||||||
acc[status] = statusesByName[status]
|
acc[status] = statusesByName[status]
|
||||||
return acc
|
return acc
|
||||||
@ -98,10 +103,10 @@ export const statusesStore = defineStore('crm-statuses', () => {
|
|||||||
label: statusesByName[status]?.name,
|
label: statusesByName[status]?.name,
|
||||||
value: statusesByName[status]?.name,
|
value: statusesByName[status]?.name,
|
||||||
icon: () => h(IndicatorIcon, { class: statusesByName[status]?.color }),
|
icon: () => h(IndicatorIcon, { class: statusesByName[status]?.color }),
|
||||||
onClick: () => {
|
onClick: async () => {
|
||||||
capture('status_changed', { doctype, status })
|
capture('status_changed', { doctype, status })
|
||||||
if (document) {
|
if (document) {
|
||||||
document.doc.status = statusesByName[status]?.name
|
await triggerOnChange?.('status', statusesByName[status]?.name)
|
||||||
document.save.submit()
|
document.save.submit()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user