feat: create address and contact after lead creation
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index 122e2b4..0c88d28 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -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 @@
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 @@
},
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 @@
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));
}
}
diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json
index eb68c67..d2a98b6 100644
--- a/erpnext/crm/doctype/lead/lead.json
+++ b/erpnext/crm/doctype/lead/lead.json
@@ -16,6 +16,7 @@
"col_break123",
"lead_owner",
"status",
+ "salutation",
"gender",
"source",
"customer",
@@ -29,16 +30,20 @@
"notes_section",
"notes",
"contact_info",
- "address_desc",
"address_html",
+ "address_title",
+ "address_line1",
+ "address_line2",
+ "city",
+ "county",
+ "state",
+ "country",
+ "pincode",
"column_break2",
"contact_html",
"phone",
- "salutation",
"mobile_no",
"fax",
- "website",
- "territory",
"more_info",
"type",
"market_segment",
@@ -46,6 +51,8 @@
"request_type",
"column_break3",
"company",
+ "website",
+ "territory",
"unsubscribed",
"blog_subscriber"
],
@@ -73,7 +80,6 @@
"set_only_once": 1
},
{
- "depends_on": "eval:!doc.organization_lead",
"fieldname": "lead_name",
"fieldtype": "Data",
"in_global_search": 1,
@@ -130,7 +136,6 @@
"search_index": 1
},
{
- "depends_on": "eval:!doc.organization_lead",
"fieldname": "gender",
"fieldtype": "Link",
"label": "Gender",
@@ -218,6 +223,7 @@
},
{
"collapsible": 1,
+ "collapsible_depends_on": "eval: doc.__islocal",
"fieldname": "contact_info",
"fieldtype": "Section Break",
"label": "Address & Contact",
@@ -225,13 +231,6 @@
"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",
@@ -242,14 +241,13 @@
"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 +255,14 @@
"oldfieldtype": "Data"
},
{
- "depends_on": "eval:!doc.organization_lead",
+ "depends_on": "eval: doc.__islocal",
"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,7 +270,7 @@
"oldfieldtype": "Data"
},
{
- "depends_on": "eval:!doc.organization_lead",
+ "depends_on": "eval: doc.__islocal",
"fieldname": "fax",
"fieldtype": "Data",
"label": "Fax",
@@ -361,12 +359,62 @@
"fieldname": "blog_subscriber",
"fieldtype": "Check",
"label": "Blog Subscriber"
+ },
+ {
+ "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"
}
],
"icon": "fa fa-user",
"idx": 5,
"image_field": "image",
- "modified": "2019-09-19 12:49:02.536647",
+ "modified": "2019-09-20 12:49:02.536647",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
@@ -423,15 +471,6 @@
"read": 1,
"report": 1,
"role": "Sales User"
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Guest",
- "share": 1
}
],
"search_fields": "lead_name,lead_owner,status",
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index 1dae4b9..cc2badf 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -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,22 @@
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._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 +47,7 @@
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 +61,7 @@
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,8 +73,7 @@
"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):
@@ -81,8 +87,7 @@
.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 +120,74 @@
self.lead_name = self.company_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):
+ 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_fields = ["email_id", "salutation", "gender", "phone", "mobile_no"]
+
+ contact = frappe.new_doc("Contact")
+ contact.update({contact_field: self.get(contact_field) for contact_field in contact_fields})
+ contact.update({
+ "first_name": first_name,
+ "last_name": last_name
+ })
+ 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', 'country',
+ 'county', 'fax', 'mobile_no', 'phone', 'pincode', 'salutation', '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 +212,7 @@
return doclist
+
@frappe.whitelist()
def make_opportunity(source_name, target_doc=None):
def set_missing_values(source, target):
@@ -164,6 +234,7 @@
return target_doc
+
@frappe.whitelist()
def make_quotation(source_name, target_doc=None):
def set_missing_values(source, target):
@@ -205,7 +276,8 @@
@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 +303,7 @@
return out
+
@frappe.whitelist()
def make_lead_from_communication(communication, ignore_communication_links=False):
""" raise a issue from email """
@@ -267,4 +340,4 @@
lead = leads[0].name if leads else None
- return lead
\ No newline at end of file
+ return lead