Merge pull request #18715 from Alchez/v12-lead-address-contact

feat: create address and contact after lead creation (develop)
This commit is contained in:
Deepesh Garg 2019-12-24 16:46:05 +05:30 committed by GitHub
commit e0b4d33039
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 287 additions and 104 deletions

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.provide("erpnext");
@ -7,57 +7,54 @@ cur_frm.email_field = "email_id";
erpnext.LeadController = frappe.ui.form.Controller.extend({
setup: function () {
this.frm.make_methods = {
'Customer': this.make_customer,
'Quotation': this.make_quotation,
'Opportunity': this.create_opportunity
}
this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.customer_query" }
}
'Opportunity': this.make_opportunity
};
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
},
onload: function () {
if (cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
cur_frm.fields_dict.lead_owner.get_query = function (doc, cdt, cdn) {
return { query: "frappe.core.doctype.user.user.user_query" }
}
}
this.frm.set_query("customer", function (doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.customer_query" }
});
if (cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
cur_frm.fields_dict.contact_by.get_query = function (doc, cdt, cdn) {
return { query: "frappe.core.doctype.user.user.user_query" }
}
}
this.frm.set_query("lead_owner", function (doc, cdt, cdn) {
return { query: "frappe.core.doctype.user.user.user_query" }
});
this.frm.set_query("contact_by", function (doc, cdt, cdn) {
return { query: "frappe.core.doctype.user.user.user_query" }
});
},
refresh: function () {
var doc = this.frm.doc;
let doc = this.frm.doc;
erpnext.toggle_naming_series();
frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' }
if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
this.frm.add_custom_button(__("Customer"), this.create_customer, __('Create'));
this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __('Create'));
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __('Create'));
if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) {
this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create"));
this.frm.add_custom_button(__("Opportunity"), this.make_opportunity, __("Create"));
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create"));
}
if (!this.frm.doc.__islocal) {
frappe.contacts.render_address_and_contact(cur_frm);
if (!this.frm.is_new()) {
frappe.contacts.render_address_and_contact(this.frm);
} else {
frappe.contacts.clear_address_and_contact(cur_frm);
frappe.contacts.clear_address_and_contact(this.frm);
}
},
create_customer: function () {
make_customer: function () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_customer",
frm: cur_frm
})
},
create_opportunity: function () {
make_opportunity: function () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
frm: cur_frm
@ -77,7 +74,7 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
},
company_name: function () {
if (this.frm.doc.organization_lead == 1) {
if (this.frm.doc.organization_lead && !this.frm.doc.lead_name) {
this.frm.set_value("lead_name", this.frm.doc.company_name);
}
},
@ -85,7 +82,7 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
contact_date: function () {
if (this.frm.doc.contact_date) {
let d = moment(this.frm.doc.contact_date);
d.add(1, "hours");
d.add(1, "day");
this.frm.set_value("ends_on", d.format(frappe.defaultDatetimeFormat));
}
}

View File

@ -1,4 +1,5 @@
{
"actions": [],
"allow_events_in_timeline": 1,
"allow_import": 1,
"autoname": "naming_series:",
@ -16,6 +17,8 @@
"col_break123",
"lead_owner",
"status",
"salutation",
"designation",
"gender",
"source",
"customer",
@ -28,17 +31,22 @@
"ends_on",
"notes_section",
"notes",
"contact_info",
"address_desc",
"address_info",
"address_html",
"address_title",
"address_line1",
"address_line2",
"city",
"county",
"column_break2",
"contact_html",
"state",
"country",
"pincode",
"contact_section",
"phone",
"salutation",
"mobile_no",
"fax",
"website",
"territory",
"more_info",
"type",
"market_segment",
@ -46,8 +54,11 @@
"request_type",
"column_break3",
"company",
"website",
"territory",
"unsubscribed",
"blog_subscriber"
"blog_subscriber",
"title"
],
"fields": [
{
@ -73,7 +84,6 @@
"set_only_once": 1
},
{
"depends_on": "eval:!doc.organization_lead",
"fieldname": "lead_name",
"fieldtype": "Data",
"in_global_search": 1,
@ -130,7 +140,13 @@
"search_index": 1
},
{
"depends_on": "eval:!doc.organization_lead",
"depends_on": "eval: doc.__islocal",
"fieldname": "salutation",
"fieldtype": "Link",
"label": "Salutation",
"options": "Salutation"
},
{
"fieldname": "gender",
"fieldtype": "Link",
"label": "Gender",
@ -216,40 +232,74 @@
"fieldtype": "Text Editor",
"label": "Notes"
},
{
"collapsible": 1,
"fieldname": "contact_info",
"fieldtype": "Section Break",
"label": "Address & Contact",
"oldfieldtype": "Column Break",
"options": "fa fa-map-marker"
},
{
"depends_on": "eval:doc.__islocal",
"fieldname": "address_desc",
"fieldtype": "HTML",
"label": "Address Desc",
"print_hide": 1
},
{
"fieldname": "address_html",
"fieldtype": "HTML",
"label": "Address HTML",
"read_only": 1
},
{
"depends_on": "eval: doc.__islocal",
"fieldname": "address_title",
"fieldtype": "Data",
"label": "Address Title"
},
{
"depends_on": "eval: doc.__islocal",
"fieldname": "address_line1",
"fieldtype": "Data",
"label": "Address Line 1"
},
{
"depends_on": "eval: doc.__islocal",
"fieldname": "address_line2",
"fieldtype": "Data",
"label": "Address Line 2"
},
{
"depends_on": "eval: doc.__islocal",
"fieldname": "city",
"fieldtype": "Data",
"label": "City/Town"
},
{
"depends_on": "eval: doc.__islocal",
"fieldname": "county",
"fieldtype": "Data",
"label": "County"
},
{
"depends_on": "eval: doc.__islocal",
"fieldname": "state",
"fieldtype": "Data",
"label": "State"
},
{
"depends_on": "eval: doc.__islocal",
"fieldname": "country",
"fieldtype": "Link",
"label": "Country",
"options": "Country"
},
{
"depends_on": "eval: doc.__islocal",
"fieldname": "pincode",
"fieldtype": "Data",
"label": "Postal Code",
"options": "Country"
},
{
"fieldname": "column_break2",
"fieldtype": "Column Break"
},
{
"depends_on": "eval:doc.organization_lead",
"fieldname": "contact_html",
"fieldtype": "HTML",
"label": "Contact HTML",
"read_only": 1
},
{
"depends_on": "eval:!doc.organization_lead",
"depends_on": "eval: doc.__islocal",
"fieldname": "phone",
"fieldtype": "Data",
"label": "Phone",
@ -257,14 +307,7 @@
"oldfieldtype": "Data"
},
{
"depends_on": "eval:!doc.organization_lead",
"fieldname": "salutation",
"fieldtype": "Link",
"label": "Salutation",
"options": "Salutation"
},
{
"depends_on": "eval:!doc.organization_lead",
"depends_on": "eval: doc.__islocal",
"fieldname": "mobile_no",
"fieldtype": "Data",
"label": "Mobile No.",
@ -272,29 +315,13 @@
"oldfieldtype": "Data"
},
{
"depends_on": "eval:!doc.organization_lead",
"depends_on": "eval: doc.__islocal",
"fieldname": "fax",
"fieldtype": "Data",
"label": "Fax",
"oldfieldname": "fax",
"oldfieldtype": "Data"
},
{
"fieldname": "website",
"fieldtype": "Data",
"label": "Website",
"oldfieldname": "website",
"oldfieldtype": "Data"
},
{
"fieldname": "territory",
"fieldtype": "Link",
"label": "Territory",
"oldfieldname": "territory",
"oldfieldtype": "Link",
"options": "Territory",
"print_hide": 1
},
{
"collapsible": 1,
"fieldname": "more_info",
@ -350,6 +377,22 @@
"options": "Company",
"remember_last_selected_value": 1
},
{
"fieldname": "website",
"fieldtype": "Data",
"label": "Website",
"oldfieldname": "website",
"oldfieldtype": "Data"
},
{
"fieldname": "territory",
"fieldtype": "Link",
"label": "Territory",
"oldfieldname": "territory",
"oldfieldtype": "Link",
"options": "Territory",
"print_hide": 1
},
{
"default": "0",
"fieldname": "unsubscribed",
@ -361,12 +404,42 @@
"fieldname": "blog_subscriber",
"fieldtype": "Check",
"label": "Blog Subscriber"
},
{
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
"label": "Title",
"print_hide": 1
},
{
"fieldname": "designation",
"fieldtype": "Link",
"label": "Designation",
"options": "Designation"
},
{
"collapsible": 1,
"collapsible_depends_on": "eval: doc.__islocal",
"fieldname": "address_info",
"fieldtype": "Section Break",
"label": "Address & Contact",
"oldfieldtype": "Column Break",
"options": "fa fa-map-marker"
},
{
"collapsible": 1,
"collapsible_depends_on": "eval: doc.__islocal",
"fieldname": "contact_section",
"fieldtype": "Section Break",
"label": "Contact"
}
],
"icon": "fa fa-user",
"idx": 5,
"image_field": "image",
"modified": "2019-09-19 12:49:02.536647",
"links": [],
"modified": "2019-12-24 16:00:44.239168",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
@ -438,5 +511,5 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "lead_name"
"title_field": "title"
}

View File

@ -2,18 +2,19 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import (cstr, validate_email_address, cint, comma_and, has_gravatar, now, getdate, nowdate)
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from frappe.contacts.address_and_contact import load_address_and_contact
import frappe
from erpnext.accounts.party import set_taxes
from erpnext.controllers.selling_controller import SellingController
from frappe import _
from frappe.contacts.address_and_contact import load_address_and_contact
from frappe.email.inbox import link_communication_to_document
from frappe.model.mapper import get_mapped_doc
from frappe.utils import cint, comma_and, cstr, getdate, has_gravatar, nowdate, validate_email_address
sender_field = "email_id"
class Lead(SellingController):
def get_feed(self):
return '{0}: {1}'.format(_(self.status), self.lead_name)
@ -23,15 +24,23 @@ class Lead(SellingController):
self.get("__onload").is_customer = customer
load_address_and_contact(self)
def before_insert(self):
self.address_doc = self.create_address()
self.contact_doc = self.create_contact()
def after_insert(self):
self.update_links()
# after the address and contact are created, flush the field values
# to avoid inconsistent reporting in case the documents are changed
self.flush_address_and_contact_fields()
def validate(self):
self.set_lead_name()
self.set_title()
self._prev = frappe._dict({
"contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if \
(not cint(self.get("__islocal"))) else None,
"ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if \
(not cint(self.get("__islocal"))) else None,
"contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if \
(not cint(self.get("__islocal"))) else None,
"contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if (not cint(self.is_new())) else None,
"ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if (not cint(self.is_new())) else None,
"contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if (not cint(self.is_new())) else None,
})
self.set_status()
@ -39,7 +48,7 @@ class Lead(SellingController):
if self.email_id:
if not self.flags.ignore_email_validation:
validate_email_address(self.email_id, True)
validate_email_address(self.email_id, throw=True)
if self.email_id == self.lead_owner:
frappe.throw(_("Lead Owner cannot be same as the Lead"))
@ -53,8 +62,7 @@ class Lead(SellingController):
if self.contact_date and getdate(self.contact_date) < getdate(nowdate()):
frappe.throw(_("Next Contact Date cannot be in the past"))
if self.ends_on and self.contact_date and\
(self.ends_on < self.contact_date):
if self.ends_on and self.contact_date and (self.ends_on < self.contact_date):
frappe.throw(_("Ends On date cannot be before Next Contact Date."))
def on_update(self):
@ -66,23 +74,21 @@ class Lead(SellingController):
"starts_on": self.contact_date,
"ends_on": self.ends_on or "",
"subject": ('Contact ' + cstr(self.lead_name)),
"description": ('Contact ' + cstr(self.lead_name)) + \
(self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
"description": ('Contact ' + cstr(self.lead_name)) + (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
}, force)
def check_email_id_is_unique(self):
if self.email_id:
# validate email is unique
duplicate_leads = frappe.db.sql_list("""select name from tabLead
where email_id=%s and name!=%s""", (self.email_id, self.name))
duplicate_leads = frappe.get_all("Lead", filters={"email_id": self.email_id, "name": ["!=", self.name]})
duplicate_leads = [lead.name for lead in duplicate_leads]
if duplicate_leads:
frappe.throw(_("Email Address must be unique, already exists for {0}")
.format(comma_and(duplicate_leads)), frappe.DuplicateEntryError)
def on_trash(self):
frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""",
self.name)
frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""", self.name)
self.delete_events()
@ -115,10 +121,101 @@ class Lead(SellingController):
self.lead_name = self.company_name
def set_title(self):
if self.organization_lead:
self.title = self.company_name
else:
self.title = self.lead_name
def create_address(self):
address_fields = ["address_title", "address_line1", "address_line2",
"city", "county", "state", "country", "pincode"]
info_fields = ["email_id", "phone", "fax"]
# do not create an address if no fields are available,
# skipping country since the system auto-sets it from system defaults
if not any([self.get(field) for field in address_fields if field != "country"]):
return
address = frappe.new_doc("Address")
address.update({addr_field: self.get(addr_field) for addr_field in address_fields})
address.update({info_field: self.get(info_field) for info_field in info_fields})
address.insert()
return address
def create_contact(self):
if not self.lead_name:
self.set_lead_name()
names = self.lead_name.split(" ")
if len(names) > 1:
first_name, last_name = names[0], " ".join(names[1:])
else:
first_name, last_name = self.lead_name, None
contact = frappe.new_doc("Contact")
contact.update({
"first_name": first_name,
"last_name": last_name,
"salutation": self.salutation,
"gender": self.gender,
"designation": self.designation,
})
if self.email_id:
contact.append("email_ids", {
"email_id": self.email_id,
"is_primary": 1
})
if self.phone:
contact.append("phone_nos", {
"phone": self.phone,
"is_primary": 1
})
if self.mobile_no:
contact.append("phone_nos", {
"phone": self.mobile_no
})
contact.insert()
return contact
def update_links(self):
# update address links
if self.address_doc:
self.address_doc.append("links", {
"link_doctype": "Lead",
"link_name": self.name,
"link_title": self.lead_name
})
self.address_doc.save()
# update contact links
if self.contact_doc:
self.contact_doc.append("links", {
"link_doctype": "Lead",
"link_name": self.name,
"link_title": self.lead_name
})
self.contact_doc.save()
def flush_address_and_contact_fields(self):
fields = ['address_line1', 'address_line2', 'address_title',
'city', 'county', 'country', 'fax', 'pincode', 'state']
for field in fields:
self.set(field, None)
@frappe.whitelist()
def make_customer(source_name, target_doc=None):
return _make_customer(source_name, target_doc)
def _make_customer(source_name, target_doc=None, ignore_permissions=False):
def set_missing_values(source, target):
if source.company_name:
@ -143,6 +240,7 @@ def _make_customer(source_name, target_doc=None, ignore_permissions=False):
return doclist
@frappe.whitelist()
def make_opportunity(source_name, target_doc=None):
def set_missing_values(source, target):
@ -164,6 +262,7 @@ def make_opportunity(source_name, target_doc=None):
return target_doc
@frappe.whitelist()
def make_quotation(source_name, target_doc=None):
def set_missing_values(source, target):
@ -205,7 +304,8 @@ def _set_missing_values(source, target):
@frappe.whitelist()
def get_lead_details(lead, posting_date=None, company=None):
if not lead: return {}
if not lead:
return {}
from erpnext.accounts.party import set_address_details
out = frappe._dict()
@ -231,6 +331,7 @@ def get_lead_details(lead, posting_date=None, company=None):
return out
@frappe.whitelist()
def make_lead_from_communication(communication, ignore_communication_links=False):
""" raise a issue from email """
@ -267,4 +368,4 @@ def get_lead_with_phone_number(number):
lead = leads[0].name if leads else None
return lead
return lead

View File

@ -651,3 +651,4 @@ erpnext.patches.v12_0.update_price_or_product_discount
erpnext.patches.v12_0.set_production_capacity_in_workstation
erpnext.patches.v12_0.set_against_blanket_order_in_sales_and_purchase_order
erpnext.patches.v12_0.set_cost_center_in_child_table_of_expense_claim
erpnext.patches.v12_0.set_lead_title_field

View File

@ -0,0 +1,11 @@
import frappe
def execute():
frappe.reload_doc("crm", "doctype", "lead")
frappe.db.sql("""
UPDATE
`tabLead`
SET
title = IF(organization_lead = 1, company_name, lead_name)
""")