1
0
forked from test/crm

feat: set contact as primary from UI

This commit is contained in:
Shariq Ansari 2023-11-13 12:21:50 +05:30
parent 36f4ddee20
commit d408d066c7
4 changed files with 99 additions and 52 deletions

View File

@ -22,7 +22,7 @@ def get_deal(name):
deal["contacts"] = frappe.get_all( deal["contacts"] = frappe.get_all(
"CRM Contacts", "CRM Contacts",
filters={"parenttype": "CRM Deal", "parent": deal.name}, filters={"parenttype": "CRM Deal", "parent": deal.name},
fields=["contact"], fields=["contact", "is_primary"],
) )
return deal return deal

View File

@ -76,3 +76,13 @@ def remove_contact(deal, contact):
deal.contacts = [d for d in deal.contacts if d.contact != contact] deal.contacts = [d for d in deal.contacts if d.contact != contact]
deal.save() deal.save()
return True return True
@frappe.whitelist()
def set_primary_contact(deal, contact):
if not frappe.has_permission("CRM Deal", "write", deal):
frappe.throw(_("Not allowed to set primary contact for Deal"), frappe.PermissionError)
deal = frappe.get_cached_doc("CRM Deal", deal)
deal.set_primary_contact(contact)
deal.save()
return True

View File

@ -0,0 +1,16 @@
<template>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M14 8C14 11.3137 11.3137 14 8 14C4.68629 14 2 11.3137 2 8C2 4.68629 4.68629 2 8 2C11.3137 2 14 4.68629 14 8ZM15 8C15 11.866 11.866 15 8 15C4.13401 15 1 11.866 1 8C1 4.13401 4.13401 1 8 1C11.866 1 15 4.13401 15 8ZM11.2909 5.98482C11.4666 5.77175 11.4363 5.45663 11.2232 5.28096C11.0101 5.1053 10.695 5.13561 10.5193 5.34868L7.07001 9.53239L5.72845 7.79857C5.55946 7.58018 5.24543 7.54012 5.02703 7.70911C4.80863 7.8781 4.76858 8.19214 4.93756 8.41053L6.66217 10.6394C6.7552 10.7596 6.89788 10.831 7.04988 10.8334C7.20188 10.8357 7.3467 10.7688 7.4434 10.6515L11.2909 5.98482Z"
fill="currentColor"
/>
</svg>
</template>

View File

@ -209,37 +209,6 @@
</div> </div>
</template> </template>
</Autocomplete> </Autocomplete>
<Dropdown
v-else-if="field.type === 'dropdown'"
:options="
statusDropdownOptions(deal.data, 'deal', updateField)
"
class="w-full flex-1"
>
<template #default="{ open }">
<Button
:label="deal.data[field.name]"
class="w-full justify-between"
>
<template #prefix>
<IndicatorIcon
:class="
dealStatuses[deal.data[field.name]].color
"
/>
</template>
<template #default>{{
deal.data[field.name]
}}</template>
<template #suffix>
<FeatherIcon
:name="open ? 'chevron-up' : 'chevron-down'"
class="h-4 text-gray-600"
/>
</template>
</Button>
</template>
</Dropdown>
<FormControl <FormControl
v-else-if="field.type === 'date'" v-else-if="field.type === 'date'"
type="date" type="date"
@ -307,28 +276,41 @@
size="md" size="md"
/> />
{{ getContactByName(contact.name).full_name }} {{ getContactByName(contact.name).full_name }}
</div> <Badge
<div class="flex gap-3"> v-if="contact.is_primary"
<FeatherIcon class="ml-2"
name="trash-2" variant="outline"
class="h-4 w-4" label="Primary"
@click="removeContact(contact.name)" theme="green"
/> />
<ExternalLinkIcon </div>
class="h-4 w-4" <div class="flex items-center">
<Dropdown :options="contactOptions(contact)">
<Button variant="ghost">
<FeatherIcon
name="more-horizontal"
class="h-4 text-gray-600"
/>
</Button>
</Dropdown>
<Button
variant="ghost"
@click=" @click="
router.push({ router.push({
name: 'Contact', name: 'Contact',
params: { contactId: contact.name }, params: { contactId: contact.name },
}) })
" "
/> >
<FeatherIcon <ExternalLinkIcon class="h-4 w-4" />
name="chevron-right" </Button>
class="h-4 w-4 text-gray-900 transition-all duration-300 ease-in-out" <Button variant="ghost" @click="cToggle()">
:class="{ 'rotate-90': cOpened }" <FeatherIcon
@click="cToggle()" name="chevron-right"
/> class="h-4 w-4 text-gray-900 transition-all duration-300 ease-in-out"
:class="{ 'rotate-90': cOpened }"
/>
</Button>
</div> </div>
</div> </div>
<transition <transition
@ -403,6 +385,7 @@ import NoteIcon from '@/components/Icons/NoteIcon.vue'
import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue' import IndicatorIcon from '@/components/Icons/IndicatorIcon.vue'
import LinkIcon from '@/components/Icons/LinkIcon.vue' import LinkIcon from '@/components/Icons/LinkIcon.vue'
import ExternalLinkIcon from '@/components/Icons/ExternalLinkIcon.vue' import ExternalLinkIcon from '@/components/Icons/ExternalLinkIcon.vue'
import SuccessIcon from '@/components/Icons/SuccessIcon.vue'
import LayoutHeader from '@/components/LayoutHeader.vue' import LayoutHeader from '@/components/LayoutHeader.vue'
import Toggler from '@/components/Toggler.vue' import Toggler from '@/components/Toggler.vue'
import Activities from '@/components/Activities.vue' import Activities from '@/components/Activities.vue'
@ -430,8 +413,9 @@ import {
Tabs, Tabs,
Breadcrumbs, Breadcrumbs,
call, call,
Badge,
} from 'frappe-ui' } from 'frappe-ui'
import { ref, computed } from 'vue' import { ref, computed, h } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const { getUser } = usersStore() const { getUser } = usersStore()
@ -588,6 +572,7 @@ const detailSections = computed(() => {
contacts: deal.data.contacts.map((contact) => { contacts: deal.data.contacts.map((contact) => {
return { return {
name: contact.contact, name: contact.contact,
is_primary: contact.is_primary,
opened: false, opened: false,
} }
}), }),
@ -598,10 +583,30 @@ const detailSections = computed(() => {
const showContactModal = ref(false) const showContactModal = ref(false)
const _contact = ref({}) const _contact = ref({})
async function addContact(value) { function contactOptions(contact) {
let options = [
{
label: 'Delete',
icon: 'trash-2',
onClick: () => removeContact(contact.name),
},
]
if (!contact.is_primary) {
options.push({
label: 'Set as primary contact',
icon: h(SuccessIcon, { class: 'h-4 w-4' }),
onClick: () => setPrimaryContact(contact.name),
})
}
return options
}
async function addContact(contact) {
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.add_contact', { let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.add_contact', {
deal: props.dealId, deal: props.dealId,
contact: value, contact,
}) })
if (d) { if (d) {
await contacts.reload() await contacts.reload()
@ -614,10 +619,10 @@ async function addContact(value) {
} }
} }
async function removeContact(value) { async function removeContact(contact) {
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.remove_contact', { let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.remove_contact', {
deal: props.dealId, deal: props.dealId,
contact: value, contact,
}) })
if (d) { if (d) {
deal.reload() deal.reload()
@ -630,6 +635,22 @@ async function removeContact(value) {
} }
} }
async function setPrimaryContact(contact) {
let d = await call('crm.fcrm.doctype.crm_deal.crm_deal.set_primary_contact', {
deal: props.dealId,
contact,
})
if (d) {
await contacts.reload()
deal.reload()
createToast({
title: 'Primary contact set',
icon: 'check',
iconClasses: 'text-green-600',
})
}
}
const organization = computed(() => { const organization = computed(() => {
return getOrganization(deal.data.organization) return getOrganization(deal.data.organization)
}) })