Merge pull request #51 from shariquerik/organization-fields
feat: Minor features
This commit is contained in:
commit
34e3482090
@ -188,11 +188,16 @@ def get_doctype_fields(doctype):
|
|||||||
else:
|
else:
|
||||||
section_fields.append(get_field_obj(field))
|
section_fields.append(get_field_obj(field))
|
||||||
|
|
||||||
all_fields = []
|
section_fields = []
|
||||||
for section in sections:
|
for section in sections:
|
||||||
all_fields.append(sections[section])
|
section_fields.append(sections[section])
|
||||||
|
|
||||||
return all_fields
|
fields = [field for field in fields if field.fieldtype not in "Tab Break"]
|
||||||
|
fields_meta = {}
|
||||||
|
for field in fields:
|
||||||
|
fields_meta[field.fieldname] = field
|
||||||
|
|
||||||
|
return section_fields, fields_meta
|
||||||
|
|
||||||
|
|
||||||
def get_field_obj(field):
|
def get_field_obj(field):
|
||||||
@ -200,6 +205,9 @@ def get_field_obj(field):
|
|||||||
"label": field.label,
|
"label": field.label,
|
||||||
"type": get_type(field),
|
"type": get_type(field),
|
||||||
"name": field.fieldname,
|
"name": field.fieldname,
|
||||||
|
"hidden": field.hidden,
|
||||||
|
"reqd": field.reqd,
|
||||||
|
"read_only": field.read_only,
|
||||||
}
|
}
|
||||||
|
|
||||||
obj["placeholder"] = "Add " + field.label + "..."
|
obj["placeholder"] = "Add " + field.label + "..."
|
||||||
|
|||||||
@ -27,7 +27,7 @@ def get_deal(name):
|
|||||||
fields=["contact", "is_primary"],
|
fields=["contact", "is_primary"],
|
||||||
)
|
)
|
||||||
|
|
||||||
deal["doctype_fields"] = get_doctype_fields("CRM Deal")
|
deal["doctype_fields"], deal["all_fields"] = get_doctype_fields("CRM Deal")
|
||||||
deal["doctype"] = "CRM Deal"
|
deal["doctype"] = "CRM Deal"
|
||||||
deal["_form_script"] = get_form_script('CRM Deal')
|
deal["_form_script"] = get_form_script('CRM Deal')
|
||||||
return deal
|
return deal
|
||||||
|
|||||||
@ -52,19 +52,17 @@
|
|||||||
"label": "Probability"
|
"label": "Probability"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "organization.annual_revenue",
|
"fetch_from": ".annual_revenue",
|
||||||
"fieldname": "annual_revenue",
|
"fieldname": "annual_revenue",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Amount",
|
"label": "Amount"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "organization.website",
|
"fetch_from": ".website",
|
||||||
"fieldname": "website",
|
"fieldname": "website",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Website",
|
"label": "Website",
|
||||||
"options": "URL",
|
"options": "URL"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "close_date",
|
"fieldname": "close_date",
|
||||||
@ -212,11 +210,11 @@
|
|||||||
"options": "CRM Communication Status"
|
"options": "CRM Communication Status"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "organization.territory",
|
"fetch_from": ".territory",
|
||||||
"fieldname": "territory",
|
"fieldname": "territory",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Link",
|
||||||
"label": "Territory",
|
"label": "Territory",
|
||||||
"read_only": 1
|
"options": "CRM Territory"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "source",
|
"fieldname": "source",
|
||||||
@ -227,7 +225,7 @@
|
|||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-01-04 20:02:23.823826",
|
"modified": "2024-01-04 20:53:31.618792",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "CRM Deal",
|
"name": "CRM Deal",
|
||||||
|
|||||||
@ -17,6 +17,7 @@ class CRMDeal(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.set_primary_contact()
|
self.set_primary_contact()
|
||||||
self.set_primary_email_mobile_no()
|
self.set_primary_email_mobile_no()
|
||||||
|
self.update_organization()
|
||||||
if self.deal_owner and not self.is_new():
|
if self.deal_owner and not self.is_new():
|
||||||
self.assign_agent(self.deal_owner)
|
self.assign_agent(self.deal_owner)
|
||||||
|
|
||||||
@ -61,6 +62,20 @@ class CRMDeal(Document):
|
|||||||
self.email = ""
|
self.email = ""
|
||||||
self.mobile_no = ""
|
self.mobile_no = ""
|
||||||
|
|
||||||
|
def update_organization(self):
|
||||||
|
if self.organization:
|
||||||
|
if self.has_value_changed("organization"):
|
||||||
|
organization = frappe.get_cached_doc("CRM Organization", self.organization)
|
||||||
|
self.website = organization.website
|
||||||
|
self.territory = organization.territory
|
||||||
|
self.annual_revenue = organization.annual_revenue
|
||||||
|
if self.has_value_changed("website"):
|
||||||
|
frappe.db.set_value("CRM Organization", self.organization, "website", self.website)
|
||||||
|
if self.has_value_changed("territory"):
|
||||||
|
frappe.db.set_value("CRM Organization", self.organization, "territory", self.territory)
|
||||||
|
if self.has_value_changed("annual_revenue"):
|
||||||
|
frappe.db.set_value("CRM Organization", self.organization, "annual_revenue", self.annual_revenue)
|
||||||
|
|
||||||
def assign_agent(self, agent):
|
def assign_agent(self, agent):
|
||||||
if not agent:
|
if not agent:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -15,7 +15,7 @@ def get_lead(name):
|
|||||||
frappe.throw(_("Lead not found"), frappe.DoesNotExistError)
|
frappe.throw(_("Lead not found"), frappe.DoesNotExistError)
|
||||||
lead = lead.pop()
|
lead = lead.pop()
|
||||||
|
|
||||||
lead["doctype_fields"] = get_doctype_fields("CRM Lead")
|
lead["doctype_fields"], lead["all_fields"] = get_doctype_fields("CRM Lead")
|
||||||
lead["doctype"] = "CRM Lead"
|
lead["doctype"] = "CRM Lead"
|
||||||
lead["_form_script"] = get_form_script('CRM Lead')
|
lead["_form_script"] = get_form_script('CRM Lead')
|
||||||
return lead
|
return lead
|
||||||
|
|||||||
@ -101,12 +101,10 @@
|
|||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "organization.website",
|
|
||||||
"fieldname": "website",
|
"fieldname": "website",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Website",
|
"label": "Website",
|
||||||
"options": "URL",
|
"options": "URL"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "mobile_no",
|
"fieldname": "mobile_no",
|
||||||
@ -155,11 +153,10 @@
|
|||||||
"options": "CRM Lead Source"
|
"options": "CRM Lead Source"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "organization.industry",
|
|
||||||
"fieldname": "industry",
|
"fieldname": "industry",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Link",
|
||||||
"label": "Industry",
|
"label": "Industry",
|
||||||
"read_only": 1
|
"options": "CRM Industry"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
@ -187,9 +184,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "organization",
|
"fieldname": "organization",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Data",
|
||||||
"label": "Organization",
|
"label": "Organization"
|
||||||
"options": "CRM Organization"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@ -273,17 +269,16 @@
|
|||||||
"options": "CRM Communication Status"
|
"options": "CRM Communication Status"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "organization.territory",
|
|
||||||
"fieldname": "territory",
|
"fieldname": "territory",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Link",
|
||||||
"label": "Territory",
|
"label": "Territory",
|
||||||
"read_only": 1
|
"options": "CRM Territory"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-01-04 18:58:26.603817",
|
"modified": "2024-01-04 21:34:32.388456",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "FCRM",
|
"module": "FCRM",
|
||||||
"name": "CRM Lead",
|
"name": "CRM Lead",
|
||||||
|
|||||||
@ -111,6 +111,26 @@ class CRMLead(Document):
|
|||||||
|
|
||||||
return contact.name
|
return contact.name
|
||||||
|
|
||||||
|
def create_organization(self):
|
||||||
|
if not self.organization:
|
||||||
|
return
|
||||||
|
|
||||||
|
existing_organization = frappe.db.exists("CRM Organization", {"organization_name": self.organization})
|
||||||
|
if existing_organization:
|
||||||
|
return existing_organization
|
||||||
|
|
||||||
|
organization = frappe.new_doc("CRM Organization")
|
||||||
|
organization.update(
|
||||||
|
{
|
||||||
|
"organization_name": self.organization,
|
||||||
|
"website": self.website,
|
||||||
|
"territory": self.territory,
|
||||||
|
"annual_revenue": self.annual_revenue,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
organization.insert(ignore_permissions=True)
|
||||||
|
return organization.name
|
||||||
|
|
||||||
def contact_exists(self, throw=True):
|
def contact_exists(self, throw=True):
|
||||||
email_exist = frappe.db.exists("Contact Email", {"email_id": self.email})
|
email_exist = frappe.db.exists("Contact Email", {"email_id": self.email})
|
||||||
phone_exist = frappe.db.exists("Contact Phone", {"phone": self.phone})
|
phone_exist = frappe.db.exists("Contact Phone", {"phone": self.phone})
|
||||||
@ -136,12 +156,12 @@ class CRMLead(Document):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def create_deal(self, contact):
|
def create_deal(self, contact, organization):
|
||||||
deal = frappe.new_doc("CRM Deal")
|
deal = frappe.new_doc("CRM Deal")
|
||||||
deal.update(
|
deal.update(
|
||||||
{
|
{
|
||||||
"lead": self.name,
|
"lead": self.name,
|
||||||
"organization": self.organization,
|
"organization": organization,
|
||||||
"deal_owner": self.lead_owner,
|
"deal_owner": self.lead_owner,
|
||||||
"source": self.source,
|
"source": self.source,
|
||||||
"contacts": [{"contact": contact}],
|
"contacts": [{"contact": contact}],
|
||||||
@ -279,6 +299,7 @@ def convert_to_deal(lead):
|
|||||||
lead.communication_status = 'Replied'
|
lead.communication_status = 'Replied'
|
||||||
lead.save()
|
lead.save()
|
||||||
contact = lead.create_contact(False)
|
contact = lead.create_contact(False)
|
||||||
deal = lead.create_deal(contact)
|
organization = lead.create_organization()
|
||||||
|
deal = lead.create_deal(contact, organization)
|
||||||
return deal
|
return deal
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,31 @@
|
|||||||
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
# import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class CRMOrganization(Document):
|
class CRMOrganization(Document):
|
||||||
|
def on_update(self):
|
||||||
|
self.update_deal_organization_fields()
|
||||||
|
|
||||||
|
def update_deal_organization_fields(self):
|
||||||
|
if (
|
||||||
|
self.has_value_changed("website")
|
||||||
|
or self.has_value_changed("territory")
|
||||||
|
or self.has_value_changed("annual_revenue")
|
||||||
|
):
|
||||||
|
for deal in frappe.get_all(
|
||||||
|
"CRM Deal",
|
||||||
|
filters={"organization": self.name},
|
||||||
|
):
|
||||||
|
if self.has_value_changed("website"):
|
||||||
|
frappe.db.set_value("CRM Deal", deal.name, "website", self.website)
|
||||||
|
if self.has_value_changed("territory"):
|
||||||
|
frappe.db.set_value("CRM Deal", deal.name, "territory", self.territory)
|
||||||
|
if self.has_value_changed("annual_revenue"):
|
||||||
|
frappe.db.set_value("CRM Deal", deal.name, "annual_revenue", self.annual_revenue)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def sort_options():
|
def sort_options():
|
||||||
return [
|
return [
|
||||||
|
|||||||
@ -3,16 +3,25 @@
|
|||||||
<div
|
<div
|
||||||
v-for="field in fields"
|
v-for="field in fields"
|
||||||
:key="field.label"
|
:key="field.label"
|
||||||
|
:class="[field.hidden && 'hidden']"
|
||||||
class="flex items-center gap-2 px-3 leading-5 first:mt-3"
|
class="flex items-center gap-2 px-3 leading-5 first:mt-3"
|
||||||
>
|
>
|
||||||
<div class="w-[106px] shrink-0 text-sm text-gray-600">
|
<div class="w-[106px] shrink-0 text-sm text-gray-600">
|
||||||
{{ field.label }}
|
{{ field.label }}
|
||||||
|
<span class="text-red-500">{{ field.reqd ? ' *' : '' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="grid min-h-[28px] flex-1 items-center overflow-hidden text-base"
|
class="grid min-h-[28px] flex-1 items-center overflow-hidden text-base"
|
||||||
>
|
>
|
||||||
|
<Tooltip
|
||||||
|
v-if="field.read_only"
|
||||||
|
class="flex h-7 cursor-pointer items-center px-2 py-1 text-gray-600"
|
||||||
|
:text="field.tooltip"
|
||||||
|
>
|
||||||
|
{{ data[field.name] }}
|
||||||
|
</Tooltip>
|
||||||
<FormControl
|
<FormControl
|
||||||
v-if="
|
v-else-if="
|
||||||
[
|
[
|
||||||
'email',
|
'email',
|
||||||
'number',
|
'number',
|
||||||
@ -70,13 +79,6 @@
|
|||||||
@change="(data) => emit('update', field.name, data)"
|
@change="(data) => emit('update', field.name, data)"
|
||||||
:onCreate="field.create"
|
:onCreate="field.create"
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
|
||||||
v-else-if="field.type === 'read_only'"
|
|
||||||
class="flex h-7 cursor-pointer items-center px-2 py-1"
|
|
||||||
:text="field.tooltip"
|
|
||||||
>
|
|
||||||
{{ field.value }}
|
|
||||||
</Tooltip>
|
|
||||||
<FormControl
|
<FormControl
|
||||||
v-else
|
v-else
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
|||||||
@ -355,6 +355,8 @@ const organization = computed(() => {
|
|||||||
function updateDeal(fieldname, value, callback) {
|
function updateDeal(fieldname, value, callback) {
|
||||||
value = Array.isArray(fieldname) ? '' : value
|
value = Array.isArray(fieldname) ? '' : value
|
||||||
|
|
||||||
|
if (validateRequired(fieldname, value)) return
|
||||||
|
|
||||||
createResource({
|
createResource({
|
||||||
url: 'frappe.client.set_value',
|
url: 'frappe.client.set_value',
|
||||||
params: {
|
params: {
|
||||||
@ -386,6 +388,20 @@ function updateDeal(fieldname, value, callback) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateRequired(fieldname, value) {
|
||||||
|
let meta = deal.data.all_fields || {}
|
||||||
|
if (meta[fieldname]?.reqd && !value) {
|
||||||
|
createToast({
|
||||||
|
title: 'Error Updating Deal',
|
||||||
|
text: `${meta[fieldname].label} is a required field`,
|
||||||
|
icon: 'x',
|
||||||
|
iconClasses: 'text-red-600',
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const breadcrumbs = computed(() => {
|
const breadcrumbs = computed(() => {
|
||||||
let items = [{ label: 'Deals', route: { name: 'Deals' } }]
|
let items = [{ label: 'Deals', route: { name: 'Deals' } }]
|
||||||
items.push({
|
items.push({
|
||||||
@ -428,11 +444,13 @@ const detailSections = computed(() => {
|
|||||||
function getParsedFields(sections, contacts) {
|
function getParsedFields(sections, contacts) {
|
||||||
sections.forEach((section) => {
|
sections.forEach((section) => {
|
||||||
section.fields.forEach((field) => {
|
section.fields.forEach((field) => {
|
||||||
if (['website', 'annual_revenue'].includes(field.name)) {
|
if (
|
||||||
field.value = organization.value?.[field.name]
|
!deal.data.organization &&
|
||||||
field.tooltip =
|
['website', 'territory', 'annual_revenue'].includes(field.name)
|
||||||
'This field is read-only and is fetched from the organization'
|
) {
|
||||||
} else if (field.name == 'organization') {
|
field.hidden = true
|
||||||
|
}
|
||||||
|
if (field.name == 'organization') {
|
||||||
field.create = (value, close) => {
|
field.create = (value, close) => {
|
||||||
_organization.value.organization_name = value
|
_organization.value.organization_name = value
|
||||||
showOrganizationModal.value = true
|
showOrganizationModal.value = true
|
||||||
|
|||||||
@ -262,6 +262,8 @@ const organization = computed(() => {
|
|||||||
function updateLead(fieldname, value, callback) {
|
function updateLead(fieldname, value, callback) {
|
||||||
value = Array.isArray(fieldname) ? '' : value
|
value = Array.isArray(fieldname) ? '' : value
|
||||||
|
|
||||||
|
if (validateRequired(fieldname, value)) return
|
||||||
|
|
||||||
createResource({
|
createResource({
|
||||||
url: 'frappe.client.set_value',
|
url: 'frappe.client.set_value',
|
||||||
params: {
|
params: {
|
||||||
@ -292,6 +294,20 @@ function updateLead(fieldname, value, callback) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateRequired(fieldname, value) {
|
||||||
|
let meta = lead.data.all_fields || {}
|
||||||
|
if (meta[fieldname]?.reqd && !value) {
|
||||||
|
createToast({
|
||||||
|
title: 'Error Updating Lead',
|
||||||
|
text: `${meta[fieldname].label} is a required field`,
|
||||||
|
icon: 'x',
|
||||||
|
iconClasses: 'text-red-600',
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const breadcrumbs = computed(() => {
|
const breadcrumbs = computed(() => {
|
||||||
let items = [{ label: 'Leads', route: { name: 'Leads' } }]
|
let items = [{ label: 'Leads', route: { name: 'Leads' } }]
|
||||||
items.push({
|
items.push({
|
||||||
@ -335,34 +351,9 @@ function validateFile(file) {
|
|||||||
const detailSections = computed(() => {
|
const detailSections = computed(() => {
|
||||||
let data = lead.data
|
let data = lead.data
|
||||||
if (!data) return []
|
if (!data) return []
|
||||||
return getParsedFields(data.doctype_fields, data.contacts)
|
return data.doctype_fields
|
||||||
})
|
})
|
||||||
|
|
||||||
function getParsedFields(sections) {
|
|
||||||
sections.forEach((section) => {
|
|
||||||
section.fields.forEach((field) => {
|
|
||||||
if (['website', 'industry'].includes(field.name)) {
|
|
||||||
field.value = organization.value?.[field.name]
|
|
||||||
field.tooltip =
|
|
||||||
'This field is read-only and is fetched from the organization'
|
|
||||||
} else if (field.name == 'organization') {
|
|
||||||
field.create = (value, close) => {
|
|
||||||
_organization.value.organization_name = value
|
|
||||||
showOrganizationModal.value = true
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
field.link = (org) =>
|
|
||||||
router.push({
|
|
||||||
name: 'Organization',
|
|
||||||
params: { organizationId: org },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return sections
|
|
||||||
}
|
|
||||||
|
|
||||||
async function convertToDeal() {
|
async function convertToDeal() {
|
||||||
let deal = await call('crm.fcrm.doctype.crm_lead.crm_lead.convert_to_deal', {
|
let deal = await call('crm.fcrm.doctype.crm_lead.crm_lead.convert_to_deal', {
|
||||||
lead: lead.data.name,
|
lead: lead.data.name,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user