Merge pull request #1013 from shariquerik/primary-mobile-no
This commit is contained in:
commit
40c5c92230
@ -14,24 +14,16 @@ def update_deals_email_mobile_no(doc):
|
||||
)
|
||||
|
||||
for linked_deal in linked_deals:
|
||||
deal = frappe.get_cached_doc("CRM Deal", linked_deal.parent)
|
||||
deal = frappe.db.get_values("CRM Deal", linked_deal.parent, ["email", "mobile_no"], as_dict=True)[0]
|
||||
if deal.email != doc.email_id or deal.mobile_no != doc.mobile_no:
|
||||
deal.email = doc.email_id
|
||||
deal.mobile_no = doc.mobile_no
|
||||
deal.save(ignore_permissions=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_contact(name):
|
||||
contact = frappe.get_doc("Contact", name)
|
||||
contact.check_permission("read")
|
||||
|
||||
contact = contact.as_dict()
|
||||
|
||||
if not len(contact):
|
||||
frappe.throw(_("Contact not found"), frappe.DoesNotExistError)
|
||||
|
||||
return contact
|
||||
frappe.db.set_value(
|
||||
"CRM Deal",
|
||||
linked_deal.parent,
|
||||
{
|
||||
"email": doc.email_id,
|
||||
"mobile_no": doc.mobile_no,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import frappe
|
||||
|
||||
from crm.api.doc import get_assigned_users, get_fields_meta
|
||||
from crm.api.doc import get_fields_meta
|
||||
from crm.fcrm.doctype.crm_form_script.crm_form_script import get_form_script
|
||||
|
||||
|
||||
@ -32,24 +32,12 @@ def get_deal_contacts(name):
|
||||
is_primary = contact.is_primary
|
||||
contact = frappe.get_doc("Contact", contact.contact).as_dict()
|
||||
|
||||
def get_primary_email(contact):
|
||||
for email in contact.email_ids:
|
||||
if email.is_primary:
|
||||
return email.email_id
|
||||
return contact.email_ids[0].email_id if contact.email_ids else ""
|
||||
|
||||
def get_primary_mobile_no(contact):
|
||||
for phone in contact.phone_nos:
|
||||
if phone.is_primary:
|
||||
return phone.phone
|
||||
return contact.phone_nos[0].phone if contact.phone_nos else ""
|
||||
|
||||
_contact = {
|
||||
"name": contact.name,
|
||||
"image": contact.image,
|
||||
"full_name": contact.full_name,
|
||||
"email": get_primary_email(contact),
|
||||
"mobile_no": get_primary_mobile_no(contact),
|
||||
"email": contact.email_id,
|
||||
"mobile_no": contact.mobile_no,
|
||||
"is_primary": is_primary,
|
||||
}
|
||||
deal_contacts.append(_contact)
|
||||
|
||||
@ -130,14 +130,16 @@
|
||||
{
|
||||
"fieldname": "email",
|
||||
"fieldtype": "Data",
|
||||
"label": "Email",
|
||||
"options": "Email"
|
||||
"label": "Primary Email",
|
||||
"options": "Email",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "mobile_no",
|
||||
"fieldtype": "Data",
|
||||
"label": "Mobile No",
|
||||
"options": "Phone"
|
||||
"label": "Primary Mobile No",
|
||||
"options": "Phone",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "Qualification",
|
||||
@ -250,8 +252,9 @@
|
||||
{
|
||||
"fieldname": "phone",
|
||||
"fieldtype": "Data",
|
||||
"label": "Phone",
|
||||
"options": "Phone"
|
||||
"label": "Primary Phone",
|
||||
"options": "Phone",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "log_tab",
|
||||
@ -411,7 +414,7 @@
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-07-02 11:07:50.192089",
|
||||
"modified": "2025-07-05 12:25:05.927806",
|
||||
"modified_by": "Administrator",
|
||||
"module": "FCRM",
|
||||
"name": "CRM Deal",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import frappe
|
||||
|
||||
from crm.api.doc import get_assigned_users, get_fields_meta
|
||||
from crm.api.doc import get_fields_meta
|
||||
from crm.fcrm.doctype.crm_form_script.crm_form_script import get_form_script
|
||||
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@
|
||||
/>
|
||||
<div v-else>
|
||||
<div
|
||||
class="p-1.5 px-7 text-base text-ink-gray-4"
|
||||
class="p-1.5 pl-3 pr-4 text-base text-ink-gray-4"
|
||||
>
|
||||
{{
|
||||
__('No {0} Available', [field.label])
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<LayoutHeader v-if="contact.data">
|
||||
<LayoutHeader v-if="contact.doc">
|
||||
<template #left-header>
|
||||
<Breadcrumbs :items="breadcrumbs">
|
||||
<template #prefix="{ item }">
|
||||
@ -8,9 +8,9 @@
|
||||
</Breadcrumbs>
|
||||
</template>
|
||||
</LayoutHeader>
|
||||
<div v-if="contact.data" ref="parentRef" class="flex h-full">
|
||||
<div v-if="contact.doc" ref="parentRef" class="flex h-full">
|
||||
<Resizer
|
||||
v-if="contact.data"
|
||||
v-if="contact.doc"
|
||||
:parent="$refs.parentRef"
|
||||
class="flex h-full flex-col overflow-hidden border-r"
|
||||
>
|
||||
@ -26,18 +26,18 @@
|
||||
<Avatar
|
||||
size="3xl"
|
||||
class="h-15.5 w-15.5"
|
||||
:label="contact.data.full_name"
|
||||
:image="contact.data.image"
|
||||
:label="contact.doc.full_name"
|
||||
:image="contact.doc.image"
|
||||
/>
|
||||
<component
|
||||
:is="contact.data.image ? Dropdown : 'div'"
|
||||
:is="contact.doc.image ? Dropdown : 'div'"
|
||||
v-bind="
|
||||
contact.data.image
|
||||
contact.doc.image
|
||||
? {
|
||||
options: [
|
||||
{
|
||||
icon: 'upload',
|
||||
label: contact.data.image
|
||||
label: contact.doc.image
|
||||
? __('Change image')
|
||||
: __('Upload image'),
|
||||
onClick: openFileSelector,
|
||||
@ -66,36 +66,34 @@
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 truncate text-ink-gray-9">
|
||||
<div class="truncate text-2xl font-medium">
|
||||
<span v-if="contact.data.salutation">
|
||||
{{ contact.data.salutation + '. ' }}
|
||||
<span v-if="contact.doc.salutation">
|
||||
{{ contact.doc.salutation + '. ' }}
|
||||
</span>
|
||||
<span>{{ contact.data.full_name }}</span>
|
||||
<span>{{ contact.doc.full_name }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="contact.data.company_name"
|
||||
v-if="contact.doc.company_name"
|
||||
class="flex items-center gap-1.5 text-base text-ink-gray-8"
|
||||
>
|
||||
<Avatar
|
||||
size="xs"
|
||||
:label="contact.data.company_name"
|
||||
:label="contact.doc.company_name"
|
||||
:image="
|
||||
getOrganization(contact.data.company_name)
|
||||
getOrganization(contact.doc.company_name)
|
||||
?.organization_logo
|
||||
"
|
||||
/>
|
||||
<span class="">{{ contact.data.company_name }}</span>
|
||||
<span class="">{{ contact.doc.company_name }}</span>
|
||||
</div>
|
||||
<ErrorMessage :message="__(error)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-1.5">
|
||||
<Button
|
||||
v-if="contact.data.actual_mobile_no"
|
||||
v-if="callEnabled && contact.doc.mobile_no"
|
||||
:label="__('Make Call')"
|
||||
size="sm"
|
||||
@click="
|
||||
callEnabled && makeCall(contact.data.actual_mobile_no)
|
||||
"
|
||||
@click="callEnabled && makeCall(contact.doc.mobile_no)"
|
||||
>
|
||||
<template #prefix>
|
||||
<PhoneIcon class="h-4 w-4" />
|
||||
@ -105,12 +103,9 @@
|
||||
:label="__('Delete')"
|
||||
theme="red"
|
||||
size="sm"
|
||||
icon-left="trash-2"
|
||||
@click="deleteContact()"
|
||||
>
|
||||
<template #prefix>
|
||||
<FeatherIcon name="trash-2" class="h-4 w-4" />
|
||||
</template>
|
||||
</Button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -123,7 +118,7 @@
|
||||
<SidePanelLayout
|
||||
:sections="sections.data"
|
||||
doctype="Contact"
|
||||
:docname="contact.data.name"
|
||||
:docname="contact.doc.name"
|
||||
@reload="sections.reload"
|
||||
/>
|
||||
</div>
|
||||
@ -176,7 +171,7 @@
|
||||
v-if="showDeleteLinkedDocModal"
|
||||
v-model="showDeleteLinkedDocModal"
|
||||
:doctype="'Contact'"
|
||||
:docname="contact.data.name"
|
||||
:docname="contact.doc.name"
|
||||
name="Contacts"
|
||||
/>
|
||||
</template>
|
||||
@ -192,14 +187,15 @@ import CameraIcon from '@/components/Icons/CameraIcon.vue'
|
||||
import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
||||
import DealsListView from '@/components/ListViews/DealsListView.vue'
|
||||
import { formatDate, timeAgo, validateIsImageFile } from '@/utils'
|
||||
import { showAddressModal, addressProps } from '@/composables/modals'
|
||||
import { getView } from '@/utils/view'
|
||||
import { useDocument } from '@/data/document'
|
||||
import { getSettings } from '@/stores/settings'
|
||||
import { getMeta } from '@/stores/meta'
|
||||
import { globalStore } from '@/stores/global.js'
|
||||
import { usersStore } from '@/stores/users.js'
|
||||
import { organizationsStore } from '@/stores/organizations.js'
|
||||
import { statusesStore } from '@/stores/statuses'
|
||||
import { showAddressModal, addressProps } from '@/composables/modals'
|
||||
import { callEnabled } from '@/composables/settings'
|
||||
import {
|
||||
Breadcrumbs,
|
||||
@ -213,10 +209,10 @@ import {
|
||||
toast,
|
||||
} from 'frappe-ui'
|
||||
import { ref, computed, h } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const { brand } = getSettings()
|
||||
const { $dialog, makeCall } = globalStore()
|
||||
const { makeCall } = globalStore()
|
||||
|
||||
const { getUser } = usersStore()
|
||||
const { getOrganization } = organizationsStore()
|
||||
@ -231,38 +227,11 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const _contact = ref({})
|
||||
|
||||
const errorTitle = ref('')
|
||||
const errorMessage = ref('')
|
||||
|
||||
const contact = createResource({
|
||||
url: 'crm.api.contact.get_contact',
|
||||
cache: ['contact', props.contactId],
|
||||
params: { name: props.contactId },
|
||||
auto: true,
|
||||
transform: (data) => {
|
||||
return {
|
||||
...data,
|
||||
actual_mobile_no: data.mobile_no,
|
||||
mobile_no: data.mobile_no,
|
||||
}
|
||||
},
|
||||
onSuccess: () => {
|
||||
errorTitle.value = ''
|
||||
errorMessage.value = ''
|
||||
},
|
||||
onError: (err) => {
|
||||
if (err.messages?.[0]) {
|
||||
errorTitle.value = __('Not permitted')
|
||||
errorMessage.value = __(err.messages?.[0])
|
||||
} else {
|
||||
router.push({ name: 'Contacts' })
|
||||
}
|
||||
},
|
||||
})
|
||||
const { document: contact } = useDocument('Contact', props.contactId)
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: __('Contacts'), route: { name: 'Contacts' } }]
|
||||
@ -291,7 +260,7 @@ const breadcrumbs = computed(() => {
|
||||
|
||||
const title = computed(() => {
|
||||
let t = doctypeMeta['Contact']?.title_field || 'name'
|
||||
return contact.data?.[t] || props.contactId
|
||||
return contact.doc?.[t] || props.contactId
|
||||
})
|
||||
|
||||
usePageMeta(() => {
|
||||
@ -306,14 +275,13 @@ async function deleteContact() {
|
||||
showDeleteLinkedDocModal.value = true
|
||||
}
|
||||
|
||||
async function changeContactImage(file) {
|
||||
await call('frappe.client.set_value', {
|
||||
doctype: 'Contact',
|
||||
name: props.contactId,
|
||||
fieldname: 'image',
|
||||
value: file?.file_url || '',
|
||||
function changeContactImage(file) {
|
||||
contact.doc.image = file?.file_url || ''
|
||||
contact.save.submit(null, {
|
||||
onSuccess: () => {
|
||||
toast.success(__('Contact image updated'))
|
||||
},
|
||||
})
|
||||
contact.reload()
|
||||
}
|
||||
|
||||
const tabIndex = ref(0)
|
||||
@ -358,22 +326,18 @@ function getParsedSections(_sections) {
|
||||
read_only: false,
|
||||
fieldtype: 'Dropdown',
|
||||
options:
|
||||
contact.data?.email_ids?.map((email) => {
|
||||
contact.doc?.email_ids?.map((email) => {
|
||||
return {
|
||||
name: email.name,
|
||||
value: email.email_id,
|
||||
selected: email.email_id === contact.data.email_id,
|
||||
selected: email.email_id === contact.doc.email_id,
|
||||
placeholder: 'john@doe.com',
|
||||
onClick: () => {
|
||||
_contact.value.email_id = email.email_id
|
||||
setAsPrimary('email', email.email_id)
|
||||
},
|
||||
onSave: (option, isNew) => {
|
||||
if (isNew) {
|
||||
createNew('email', option.value)
|
||||
if (contact.data.email_ids.length === 1) {
|
||||
_contact.value.email_id = option.value
|
||||
}
|
||||
} else {
|
||||
editOption(
|
||||
'Contact Email',
|
||||
@ -384,24 +348,15 @@ function getParsedSections(_sections) {
|
||||
}
|
||||
},
|
||||
onDelete: async (option, isNew) => {
|
||||
contact.data.email_ids = contact.data.email_ids.filter(
|
||||
contact.doc.email_ids = contact.doc.email_ids.filter(
|
||||
(email) => email.name !== option.name,
|
||||
)
|
||||
!isNew && (await deleteOption('Contact Email', option.name))
|
||||
if (_contact.value.email_id === option.value) {
|
||||
if (contact.data.email_ids.length === 0) {
|
||||
_contact.value.email_id = ''
|
||||
} else {
|
||||
_contact.value.email_id = contact.data.email_ids.find(
|
||||
(email) => email.is_primary,
|
||||
)?.email_id
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}) || [],
|
||||
create: () => {
|
||||
contact.data?.email_ids?.push({
|
||||
contact.doc?.email_ids?.push({
|
||||
name: 'new-1',
|
||||
value: '',
|
||||
selected: false,
|
||||
@ -415,22 +370,17 @@ function getParsedSections(_sections) {
|
||||
read_only: false,
|
||||
fieldtype: 'Dropdown',
|
||||
options:
|
||||
contact.data?.phone_nos?.map((phone) => {
|
||||
contact.doc?.phone_nos?.map((phone) => {
|
||||
return {
|
||||
name: phone.name,
|
||||
value: phone.phone,
|
||||
selected: phone.phone === contact.data.actual_mobile_no,
|
||||
selected: phone.phone === contact.doc.mobile_no,
|
||||
onClick: () => {
|
||||
_contact.value.actual_mobile_no = phone.phone
|
||||
_contact.value.mobile_no = phone.phone
|
||||
setAsPrimary('mobile_no', phone.phone)
|
||||
},
|
||||
onSave: (option, isNew) => {
|
||||
if (isNew) {
|
||||
createNew('phone', option.value)
|
||||
if (contact.data.phone_nos.length === 1) {
|
||||
_contact.value.actual_mobile_no = option.value
|
||||
}
|
||||
} else {
|
||||
editOption(
|
||||
'Contact Phone',
|
||||
@ -441,25 +391,15 @@ function getParsedSections(_sections) {
|
||||
}
|
||||
},
|
||||
onDelete: async (option, isNew) => {
|
||||
contact.data.phone_nos = contact.data.phone_nos.filter(
|
||||
contact.doc.phone_nos = contact.doc.phone_nos.filter(
|
||||
(phone) => phone.name !== option.name,
|
||||
)
|
||||
!isNew && (await deleteOption('Contact Phone', option.name))
|
||||
if (_contact.value.actual_mobile_no === option.value) {
|
||||
if (contact.data.phone_nos.length === 0) {
|
||||
_contact.value.actual_mobile_no = ''
|
||||
} else {
|
||||
_contact.value.actual_mobile_no =
|
||||
contact.data.phone_nos.find(
|
||||
(phone) => phone.is_primary_mobile_no,
|
||||
)?.phone
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}) || [],
|
||||
create: () => {
|
||||
contact.data?.phone_nos?.push({
|
||||
contact.doc?.phone_nos?.push({
|
||||
name: 'new-1',
|
||||
value: '',
|
||||
selected: false,
|
||||
@ -471,7 +411,6 @@ function getParsedSections(_sections) {
|
||||
return {
|
||||
...field,
|
||||
create: (value, close) => {
|
||||
_contact.value.address = value
|
||||
openAddressModal()
|
||||
close()
|
||||
},
|
||||
@ -489,7 +428,7 @@ function getParsedSections(_sections) {
|
||||
|
||||
async function setAsPrimary(field, value) {
|
||||
let d = await call('crm.api.contact.set_as_primary', {
|
||||
contact: contact.data.name,
|
||||
contact: contact.doc.name,
|
||||
field,
|
||||
value,
|
||||
})
|
||||
@ -502,7 +441,7 @@ async function setAsPrimary(field, value) {
|
||||
async function createNew(field, value) {
|
||||
if (!value) return
|
||||
let d = await call('crm.api.contact.create_new', {
|
||||
contact: contact.data.name,
|
||||
contact: contact.doc.name,
|
||||
field,
|
||||
value,
|
||||
})
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<LayoutHeader v-if="contact.data">
|
||||
<LayoutHeader v-if="contact.doc">
|
||||
<header
|
||||
class="relative flex h-10.5 items-center justify-between gap-2 py-2.5 pl-2"
|
||||
>
|
||||
@ -10,8 +10,11 @@
|
||||
</Breadcrumbs>
|
||||
</header>
|
||||
</LayoutHeader>
|
||||
<div v-if="contact.data" class="flex flex-col h-full overflow-hidden">
|
||||
<FileUploader @success="changeContactImage" :validateFile="validateIsImageFile">
|
||||
<div v-if="contact.doc" class="flex flex-col h-full overflow-hidden">
|
||||
<FileUploader
|
||||
@success="changeContactImage"
|
||||
:validateFile="validateIsImageFile"
|
||||
>
|
||||
<template #default="{ openFileSelector, error }">
|
||||
<div class="flex flex-col items-start justify-start gap-4 p-4">
|
||||
<div class="flex gap-4 items-center">
|
||||
@ -19,18 +22,18 @@
|
||||
<Avatar
|
||||
size="3xl"
|
||||
class="h-14.5 w-14.5"
|
||||
:label="contact.data.full_name"
|
||||
:image="contact.data.image"
|
||||
:label="contact.doc.full_name"
|
||||
:image="contact.doc.image"
|
||||
/>
|
||||
<component
|
||||
:is="contact.data.image ? Dropdown : 'div'"
|
||||
:is="contact.doc.image ? Dropdown : 'div'"
|
||||
v-bind="
|
||||
contact.data.image
|
||||
contact.doc.image
|
||||
? {
|
||||
options: [
|
||||
{
|
||||
icon: 'upload',
|
||||
label: contact.data.image
|
||||
label: contact.doc.image
|
||||
? __('Change image')
|
||||
: __('Upload image'),
|
||||
onClick: openFileSelector,
|
||||
@ -59,19 +62,17 @@
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 truncate">
|
||||
<div class="truncate text-lg font-medium text-ink-gray-9">
|
||||
<span v-if="contact.data.salutation">
|
||||
{{ contact.data.salutation + '. ' }}
|
||||
<span v-if="contact.doc.salutation">
|
||||
{{ contact.doc.salutation + '. ' }}
|
||||
</span>
|
||||
<span>{{ contact.data.full_name }}</span>
|
||||
<span>{{ contact.doc.full_name }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<Button
|
||||
v-if="contact.data.actual_mobile_no"
|
||||
v-if="callEnabled && contact.doc.mobile_no"
|
||||
:label="__('Make Call')"
|
||||
size="sm"
|
||||
@click="
|
||||
callEnabled && makeCall(contact.data.actual_mobile_no)
|
||||
"
|
||||
@click="callEnabled && makeCall(contact.doc.mobile_no)"
|
||||
>
|
||||
<template #prefix>
|
||||
<PhoneIcon class="h-4 w-4" />
|
||||
@ -81,19 +82,15 @@
|
||||
:label="__('Delete')"
|
||||
theme="red"
|
||||
size="sm"
|
||||
icon-left="trash-2"
|
||||
@click="deleteContact"
|
||||
>
|
||||
<template #prefix>
|
||||
<FeatherIcon name="trash-2" class="h-4 w-4" />
|
||||
</template>
|
||||
</Button>
|
||||
/>
|
||||
<Avatar
|
||||
v-if="contact.data.company_name"
|
||||
v-if="contact.doc.company_name"
|
||||
size="md"
|
||||
:label="contact.data.company_name"
|
||||
:label="contact.doc.company_name"
|
||||
:image="
|
||||
getOrganization(contact.data.company_name)
|
||||
?.organization_logo
|
||||
getOrganization(contact.doc.company_name)?.organization_logo
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
@ -132,7 +129,7 @@
|
||||
<SidePanelLayout
|
||||
:sections="sections.data"
|
||||
doctype="Contact"
|
||||
:docname="contact.data.name"
|
||||
:docname="contact.doc.name"
|
||||
@reload="sections.reload"
|
||||
/>
|
||||
</div>
|
||||
@ -169,13 +166,14 @@ import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
||||
import DealsListView from '@/components/ListViews/DealsListView.vue'
|
||||
import { formatDate, timeAgo, validateIsImageFile } from '@/utils'
|
||||
import { getView } from '@/utils/view'
|
||||
import { showAddressModal, addressProps } from '@/composables/modals'
|
||||
import { useDocument } from '@/data/document'
|
||||
import { getSettings } from '@/stores/settings'
|
||||
import { getMeta } from '@/stores/meta'
|
||||
import { globalStore } from '@/stores/global.js'
|
||||
import { usersStore } from '@/stores/users.js'
|
||||
import { organizationsStore } from '@/stores/organizations.js'
|
||||
import { statusesStore } from '@/stores/statuses'
|
||||
import { showAddressModal, addressProps } from '@/composables/modals'
|
||||
import { callEnabled } from '@/composables/settings'
|
||||
import {
|
||||
Breadcrumbs,
|
||||
@ -211,23 +209,7 @@ const props = defineProps({
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const _contact = ref({})
|
||||
|
||||
const contact = createResource({
|
||||
url: 'crm.api.contact.get_contact',
|
||||
cache: ['contact', props.contactId],
|
||||
params: {
|
||||
name: props.contactId,
|
||||
},
|
||||
auto: true,
|
||||
transform: (data) => {
|
||||
return {
|
||||
...data,
|
||||
actual_mobile_no: data.mobile_no,
|
||||
mobile_no: data.mobile_no,
|
||||
}
|
||||
},
|
||||
})
|
||||
const { document: contact } = useDocument('Contact', props.contactId)
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: __('Contacts'), route: { name: 'Contacts' } }]
|
||||
@ -256,7 +238,7 @@ const breadcrumbs = computed(() => {
|
||||
|
||||
const title = computed(() => {
|
||||
let t = doctypeMeta['Contact']?.title_field || 'name'
|
||||
return contact.data?.[t] || props.contactId
|
||||
return contact.doc?.[t] || props.contactId
|
||||
})
|
||||
|
||||
usePageMeta(() => {
|
||||
@ -266,14 +248,13 @@ usePageMeta(() => {
|
||||
}
|
||||
})
|
||||
|
||||
async function changeContactImage(file) {
|
||||
await call('frappe.client.set_value', {
|
||||
doctype: 'Contact',
|
||||
name: props.contactId,
|
||||
fieldname: 'image',
|
||||
value: file?.file_url || '',
|
||||
function changeContactImage(file) {
|
||||
contact.doc.image = file?.file_url || ''
|
||||
contact.save.submit(null, {
|
||||
onSuccess: () => {
|
||||
toast.success(__('Contact image updated'))
|
||||
},
|
||||
})
|
||||
contact.reload()
|
||||
}
|
||||
|
||||
async function deleteContact() {
|
||||
@ -343,22 +324,18 @@ function getParsedSections(_sections) {
|
||||
...field,
|
||||
type: 'dropdown',
|
||||
options:
|
||||
contact.data?.email_ids?.map((email) => {
|
||||
contact.doc?.email_ids?.map((email) => {
|
||||
return {
|
||||
name: email.name,
|
||||
value: email.email_id,
|
||||
selected: email.email_id === contact.data.email_id,
|
||||
selected: email.email_id === contact.doc.email_id,
|
||||
placeholder: 'john@doe.com',
|
||||
onClick: () => {
|
||||
_contact.value.email_id = email.email_id
|
||||
setAsPrimary('email', email.email_id)
|
||||
},
|
||||
onSave: (option, isNew) => {
|
||||
if (isNew) {
|
||||
createNew('email', option.value)
|
||||
if (contact.data.email_ids.length === 1) {
|
||||
_contact.value.email_id = option.value
|
||||
}
|
||||
} else {
|
||||
editOption(
|
||||
'Contact Email',
|
||||
@ -369,24 +346,15 @@ function getParsedSections(_sections) {
|
||||
}
|
||||
},
|
||||
onDelete: async (option, isNew) => {
|
||||
contact.data.email_ids = contact.data.email_ids.filter(
|
||||
contact.doc.email_ids = contact.doc.email_ids.filter(
|
||||
(email) => email.name !== option.name,
|
||||
)
|
||||
!isNew && (await deleteOption('Contact Email', option.name))
|
||||
if (_contact.value.email_id === option.value) {
|
||||
if (contact.data.email_ids.length === 0) {
|
||||
_contact.value.email_id = ''
|
||||
} else {
|
||||
_contact.value.email_id = contact.data.email_ids.find(
|
||||
(email) => email.is_primary,
|
||||
)?.email_id
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}) || [],
|
||||
create: () => {
|
||||
contact.data?.email_ids?.push({
|
||||
contact.doc?.email_ids?.push({
|
||||
name: 'new-1',
|
||||
value: '',
|
||||
selected: false,
|
||||
@ -400,22 +368,17 @@ function getParsedSections(_sections) {
|
||||
read_only: false,
|
||||
fieldtype: 'dropdown',
|
||||
options:
|
||||
contact.data?.phone_nos?.map((phone) => {
|
||||
contact.doc?.phone_nos?.map((phone) => {
|
||||
return {
|
||||
name: phone.name,
|
||||
value: phone.phone,
|
||||
selected: phone.phone === contact.data.actual_mobile_no,
|
||||
selected: phone.phone === contact.doc.mobile_no,
|
||||
onClick: () => {
|
||||
_contact.value.actual_mobile_no = phone.phone
|
||||
_contact.value.mobile_no = phone.phone
|
||||
setAsPrimary('mobile_no', phone.phone)
|
||||
},
|
||||
onSave: (option, isNew) => {
|
||||
if (isNew) {
|
||||
createNew('phone', option.value)
|
||||
if (contact.data.phone_nos.length === 1) {
|
||||
_contact.value.actual_mobile_no = option.value
|
||||
}
|
||||
} else {
|
||||
editOption(
|
||||
'Contact Phone',
|
||||
@ -426,25 +389,15 @@ function getParsedSections(_sections) {
|
||||
}
|
||||
},
|
||||
onDelete: async (option, isNew) => {
|
||||
contact.data.phone_nos = contact.data.phone_nos.filter(
|
||||
contact.doc.phone_nos = contact.doc.phone_nos.filter(
|
||||
(phone) => phone.name !== option.name,
|
||||
)
|
||||
!isNew && (await deleteOption('Contact Phone', option.name))
|
||||
if (_contact.value.actual_mobile_no === option.value) {
|
||||
if (contact.data.phone_nos.length === 0) {
|
||||
_contact.value.actual_mobile_no = ''
|
||||
} else {
|
||||
_contact.value.actual_mobile_no =
|
||||
contact.data.phone_nos.find(
|
||||
(phone) => phone.is_primary_mobile_no,
|
||||
)?.phone
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}) || [],
|
||||
create: () => {
|
||||
contact.data?.phone_nos?.push({
|
||||
contact.doc?.phone_nos?.push({
|
||||
name: 'new-1',
|
||||
value: '',
|
||||
selected: false,
|
||||
@ -456,7 +409,6 @@ function getParsedSections(_sections) {
|
||||
return {
|
||||
...field,
|
||||
create: (value, close) => {
|
||||
_contact.value.address = value
|
||||
openAddressModal()
|
||||
close()
|
||||
},
|
||||
@ -474,7 +426,7 @@ function getParsedSections(_sections) {
|
||||
|
||||
async function setAsPrimary(field, value) {
|
||||
let d = await call('crm.api.contact.set_as_primary', {
|
||||
contact: contact.data.name,
|
||||
contact: contact.doc.name,
|
||||
field,
|
||||
value,
|
||||
})
|
||||
@ -487,7 +439,7 @@ async function setAsPrimary(field, value) {
|
||||
async function createNew(field, value) {
|
||||
if (!value) return
|
||||
let d = await call('crm.api.contact.create_new', {
|
||||
contact: contact.data.name,
|
||||
contact: contact.doc.name,
|
||||
field,
|
||||
value,
|
||||
})
|
||||
|
||||
@ -158,6 +158,7 @@ import CameraIcon from '@/components/Icons/CameraIcon.vue'
|
||||
import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
||||
import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
|
||||
import { showAddressModal, addressProps } from '@/composables/modals'
|
||||
import { useDocument } from '@/data/document'
|
||||
import { getSettings } from '@/stores/settings'
|
||||
import { getMeta } from '@/stores/meta'
|
||||
import { globalStore } from '@/stores/global'
|
||||
@ -175,7 +176,6 @@ import {
|
||||
TabPanel,
|
||||
call,
|
||||
createListResource,
|
||||
createDocumentResource,
|
||||
usePageMeta,
|
||||
createResource,
|
||||
toast,
|
||||
@ -199,13 +199,10 @@ const { doctypeMeta } = getMeta('CRM Organization')
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const organization = createDocumentResource({
|
||||
doctype: 'CRM Organization',
|
||||
name: props.organizationId,
|
||||
cache: ['organization', props.organizationId],
|
||||
fields: ['*'],
|
||||
auto: true,
|
||||
})
|
||||
const { document: organization } = useDocument(
|
||||
'CRM Organization',
|
||||
props.organizationId,
|
||||
)
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }]
|
||||
@ -288,8 +285,6 @@ function openWebsite() {
|
||||
else window.open(organization.doc.website, '_blank')
|
||||
}
|
||||
|
||||
const _organization = ref({})
|
||||
|
||||
const sections = createResource({
|
||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_sidepanel_sections',
|
||||
cache: ['sidePanelSections', 'CRM Organization'],
|
||||
@ -306,7 +301,6 @@ function getParsedSections(_sections) {
|
||||
return {
|
||||
...field,
|
||||
create: (value, close) => {
|
||||
_organization.value.address = value
|
||||
openAddressModal()
|
||||
close()
|
||||
},
|
||||
|
||||
@ -186,9 +186,9 @@ import CameraIcon from '@/components/Icons/CameraIcon.vue'
|
||||
import DealsIcon from '@/components/Icons/DealsIcon.vue'
|
||||
import ContactsIcon from '@/components/Icons/ContactsIcon.vue'
|
||||
import { showAddressModal, addressProps } from '@/composables/modals'
|
||||
import { useDocument } from '@/data/document'
|
||||
import { getSettings } from '@/stores/settings'
|
||||
import { getMeta } from '@/stores/meta'
|
||||
import { globalStore } from '@/stores/global'
|
||||
import { usersStore } from '@/stores/users'
|
||||
import { statusesStore } from '@/stores/statuses'
|
||||
import { getView } from '@/utils/view'
|
||||
@ -202,13 +202,12 @@ import {
|
||||
Tabs,
|
||||
call,
|
||||
createListResource,
|
||||
createDocumentResource,
|
||||
usePageMeta,
|
||||
createResource,
|
||||
toast,
|
||||
} from 'frappe-ui'
|
||||
import { h, computed, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
import DeleteLinkedDocModal from '@/components/DeleteLinkedDocModal.vue'
|
||||
|
||||
const props = defineProps({
|
||||
@ -220,37 +219,20 @@ const props = defineProps({
|
||||
|
||||
const { brand } = getSettings()
|
||||
const { getUser } = usersStore()
|
||||
const { $dialog } = globalStore()
|
||||
const { getDealStatus } = statusesStore()
|
||||
const { doctypeMeta } = getMeta('CRM Organization')
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const errorTitle = ref('')
|
||||
const errorMessage = ref('')
|
||||
|
||||
const showDeleteLinkedDocModal = ref(false)
|
||||
|
||||
const organization = createDocumentResource({
|
||||
doctype: 'CRM Organization',
|
||||
name: props.organizationId,
|
||||
cache: ['organization', props.organizationId],
|
||||
fields: ['*'],
|
||||
auto: true,
|
||||
onSuccess: () => {
|
||||
errorTitle.value = ''
|
||||
errorMessage.value = ''
|
||||
},
|
||||
onError: (err) => {
|
||||
if (err.messages?.[0]) {
|
||||
errorTitle.value = __('Not permitted')
|
||||
errorMessage.value = __(err.messages?.[0])
|
||||
} else {
|
||||
router.push({ name: 'Organizations' })
|
||||
}
|
||||
},
|
||||
})
|
||||
const { document: organization } = useDocument(
|
||||
'CRM Organization',
|
||||
props.organizationId,
|
||||
)
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
let items = [{ label: __('Organizations'), route: { name: 'Organizations' } }]
|
||||
@ -319,8 +301,6 @@ function openWebsite() {
|
||||
else window.open(organization.doc.website, '_blank')
|
||||
}
|
||||
|
||||
const _organization = ref({})
|
||||
|
||||
const sections = createResource({
|
||||
url: 'crm.fcrm.doctype.crm_fields_layout.crm_fields_layout.get_sidepanel_sections',
|
||||
cache: ['sidePanelSections', 'CRM Organization'],
|
||||
@ -337,7 +317,6 @@ function getParsedSections(_sections) {
|
||||
return {
|
||||
...field,
|
||||
create: (value, close) => {
|
||||
_organization.value.address = value
|
||||
openAddressModal()
|
||||
close()
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user