From 4a529f8039ccf594628494e15f18744b785cba68 Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Tue, 24 Aug 2021 09:35:59 +0530 Subject: [PATCH 01/11] feat: Prospect --- erpnext/crm/doctype/lead/lead.js | 2 +- erpnext/crm/doctype/lead/lead.py | 10 ++ erpnext/crm/doctype/lead/lead_list.js | 36 ++++ .../crm/doctype/opportunity/opportunity.js | 10 +- .../crm/doctype/opportunity/opportunity.json | 6 +- erpnext/crm/doctype/prospect/__init__.py | 0 erpnext/crm/doctype/prospect/prospect.js | 39 +++++ erpnext/crm/doctype/prospect/prospect.json | 165 ++++++++++++++++++ erpnext/crm/doctype/prospect/prospect.py | 45 +++++ erpnext/crm/doctype/prospect/test_prospect.py | 8 + erpnext/crm/doctype/prospect_lead/__init__.py | 0 .../doctype/prospect_lead/prospect_lead.json | 71 ++++++++ .../doctype/prospect_lead/prospect_lead.py | 8 + .../selling/doctype/customer/customer.json | 12 +- 14 files changed, 396 insertions(+), 16 deletions(-) create mode 100644 erpnext/crm/doctype/lead/lead_list.js create mode 100644 erpnext/crm/doctype/prospect/__init__.py create mode 100644 erpnext/crm/doctype/prospect/prospect.js create mode 100644 erpnext/crm/doctype/prospect/prospect.json create mode 100644 erpnext/crm/doctype/prospect/prospect.py create mode 100644 erpnext/crm/doctype/prospect/test_prospect.py create mode 100644 erpnext/crm/doctype/prospect_lead/__init__.py create mode 100644 erpnext/crm/doctype/prospect_lead/prospect_lead.json create mode 100644 erpnext/crm/doctype/prospect_lead/prospect_lead.py diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 75af937990..9c21099088 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -51,7 +51,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller make_customer () { frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.lead.lead.make_customer", + method: "erpnext.crm.doctype.lead.lead.make_prospect", frm: cur_frm }) } diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 7f028cb316..ecf6a41e6c 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -190,6 +190,16 @@ class Lead(SellingController): return contact +@frappe.whitelist() +def make_prospect(source_name, target_doc=None, leads=None): + print("``````````````````````````") + print(source_name) + print("``````````````````````````") + print(target_doc) + print("``````````````````````````") + print(leads) + print("``````````````````````````") + @frappe.whitelist() def make_customer(source_name, target_doc=None): return _make_customer(source_name, target_doc) diff --git a/erpnext/crm/doctype/lead/lead_list.js b/erpnext/crm/doctype/lead/lead_list.js new file mode 100644 index 0000000000..5cbbf76480 --- /dev/null +++ b/erpnext/crm/doctype/lead/lead_list.js @@ -0,0 +1,36 @@ +frappe.listview_settings['Lead'] = { + onload: function(listview) { + if (frappe.boot.user.can_create.includes("Prospect")) { + listview.page.add_action_item(__("Create Prospect"), function() { + let leads = listview.get_checked_items(); + console.log(listview.get_checked_items()); + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.lead.lead.make_prospect", + frm: cur_frm, + leads: leads + }) + + // listview.call_for_selected_items(method, {"status": "Open"}); + // let prospect_lead = [] + // leads.forEach(lead => { + // prospect_lead.push({ + // "lead": lead.name + // }); + // }); + // console.log("check"); + // console.log(prospect_lead); + // frappe.new_doc("Prospect", { + // "company_name": leads[0].company_name, + // "industry": leads[0].industry, + // "market_segment": leads[0].market_segment, + // "territory": leads[0].territory, + // "no_of_employees": leads[0].no_of_employees, + // "fax": leads[0].fax, + // "website": leads[0].website, + // "prospect_owner": leads[0].lead_owner, + // "prospect_lead": prospect_lead + // }); + }); + } + } +}; diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index e9a7a95fc7..bcfae11a05 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -10,15 +10,7 @@ frappe.ui.form.on("Opportunity", { frm.custom_make_buttons = { 'Quotation': 'Quotation', 'Supplier Quotation': 'Supplier Quotation' - }, - - frm.set_query("opportunity_from", function() { - return{ - "filters": { - "name": ["in", ["Customer", "Lead"]], - } - } - }); + }; if (frm.doc.opportunity_from && frm.doc.party_name){ frm.trigger('set_contact_link'); diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 4ba4140244..e4b0e47309 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -78,13 +78,13 @@ }, { "fieldname": "opportunity_from", - "fieldtype": "Link", + "fieldtype": "Select", "in_list_view": 1, "in_standard_filter": 1, "label": "Opportunity From", "oldfieldname": "enquiry_from", "oldfieldtype": "Select", - "options": "DocType", + "options": "\nLead\nProspect\nCustomer", "print_hide": 1, "reqd": 1 }, @@ -430,7 +430,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2021-06-04 10:11:22.831139", + "modified": "2021-08-23 14:43:09.484227", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/crm/doctype/prospect/__init__.py b/erpnext/crm/doctype/prospect/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/crm/doctype/prospect/prospect.js b/erpnext/crm/doctype/prospect/prospect.js new file mode 100644 index 0000000000..793afccf3d --- /dev/null +++ b/erpnext/crm/doctype/prospect/prospect.js @@ -0,0 +1,39 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Prospect', { + refresh () { + if (!cur_frm.is_new() && frappe.boot.user.can_create.includes("Customer")) { + cur_frm.add_custom_button(__("Customer"), function() { + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.prospect.prospect.make_customer", + frm: cur_frm + }) + }, __("Create")); + } + if (!cur_frm.is_new() && frappe.boot.user.can_create.includes("Opportunity")) { + cur_frm.add_custom_button(__("Opportunity"), function() { + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.prospect.prospect.make_opportunity", + frm: cur_frm + }) + }, __("Create")); + } + }, + + make_customer () { + console.log("Make Customer"); + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.prospect.prospect.make_customer", + frm: cur_frm + }) + }, + + make_opportunity () { + console.log("Make Opportunity"); + // frappe.model.open_mapped_doc({ + // method: "erpnext.crm.doctype.lead.lead.make_opportunity", + // frm: cur_frm + // }) + } +}); diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json new file mode 100644 index 0000000000..6f476a6b65 --- /dev/null +++ b/erpnext/crm/doctype/prospect/prospect.json @@ -0,0 +1,165 @@ +{ + "actions": [], + "autoname": "field:company_name", + "creation": "2021-08-19 00:21:06.995448", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company_name", + "industry", + "market_segment", + "customer_group", + "territory", + "column_break_6", + "no_of_employees", + "currency", + "annual_revenue", + "fax", + "website", + "prospect_owner", + "leads_section", + "prospect_lead", + "addresses_and_contacts_section", + "address_html", + "column_break_17", + "contact_html", + "notes_section", + "notes" + ], + "fields": [ + { + "fieldname": "company_name", + "fieldtype": "Data", + "label": "Company Name", + "unique": 1 + }, + { + "fieldname": "industry", + "fieldtype": "Link", + "label": "Industry", + "options": "Industry Type" + }, + { + "fieldname": "market_segment", + "fieldtype": "Link", + "label": "Market Segment", + "options": "Market Segment" + }, + { + "fieldname": "customer_group", + "fieldtype": "Link", + "label": "Customer Group", + "options": "Customer Group" + }, + { + "fieldname": "territory", + "fieldtype": "Link", + "label": "Territory", + "options": "Territory" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "no_of_employees", + "fieldtype": "Int", + "label": "No. of Employees" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency" + }, + { + "fieldname": "annual_revenue", + "fieldtype": "Currency", + "label": "Annual Revenue", + "options": "currency" + }, + { + "fieldname": "fax", + "fieldtype": "Data", + "label": "Fax", + "options": "Phone" + }, + { + "fieldname": "website", + "fieldtype": "Data", + "label": "Website", + "options": "URL" + }, + { + "fieldname": "prospect_owner", + "fieldtype": "Link", + "label": "Prospect Owner", + "options": "User" + }, + { + "fieldname": "leads_section", + "fieldtype": "Section Break", + "label": "Leads" + }, + { + "fieldname": "prospect_lead", + "fieldtype": "Table", + "options": "Prospect Lead" + }, + { + "depends_on": "eval: !doc.__islocal", + "fieldname": "addresses_and_contacts_section", + "fieldtype": "Section Break", + "label": "Addresses and Contacts" + }, + { + "fieldname": "address_html", + "fieldtype": "HTML", + "label": "Address HTML" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "contact_html", + "fieldtype": "HTML", + "label": "Contact HTML" + }, + { + "collapsible": 1, + "fieldname": "notes_section", + "fieldtype": "Section Break", + "label": "Notes" + }, + { + "fieldname": "notes", + "fieldtype": "Text Editor" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-08-19 00:29:00.767038", + "modified_by": "Administrator", + "module": "CRM", + "name": "Prospect", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py new file mode 100644 index 0000000000..a0175cd310 --- /dev/null +++ b/erpnext/crm/doctype/prospect/prospect.py @@ -0,0 +1,45 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document +from frappe.model.mapper import get_mapped_doc + +class Prospect(Document): + pass + +@frappe.whitelist() +def make_customer(source_name, target_doc=None): + def set_missing_values(source, target): + target.customer_type = "Company" + target.company_name = source.name + target.customer_group = source.customer_group or frappe.db.get_default("Customer Group") + + doclist = get_mapped_doc("Prospect", source_name, + {"Prospect": { + "doctype": "Customer", + "field_map": { + "company_name": "customer_name", + "currency": "default_currency", + "fax": "fax" + } + }}, target_doc, set_missing_values, ignore_permissions=False) + + return doclist + +@frappe.whitelist() +def make_opportunity(source_name, target_doc=None): + def set_missing_values(source, target): + target.opportunity_from = "Prospect" + target.customer_name = source.company_name + target.customer_group = source.customer_group or frappe.db.get_default("Customer Group") + + doclist = get_mapped_doc("Prospect", source_name, + {"Prospect": { + "doctype": "Opportunity", + "field_map": { + "name": "party_name", + } + }}, target_doc, set_missing_values, ignore_permissions=False) + + return doclist diff --git a/erpnext/crm/doctype/prospect/test_prospect.py b/erpnext/crm/doctype/prospect/test_prospect.py new file mode 100644 index 0000000000..f266a50593 --- /dev/null +++ b/erpnext/crm/doctype/prospect/test_prospect.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestProspect(unittest.TestCase): + pass diff --git a/erpnext/crm/doctype/prospect_lead/__init__.py b/erpnext/crm/doctype/prospect_lead/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/crm/doctype/prospect_lead/prospect_lead.json b/erpnext/crm/doctype/prospect_lead/prospect_lead.json new file mode 100644 index 0000000000..1797712a55 --- /dev/null +++ b/erpnext/crm/doctype/prospect_lead/prospect_lead.json @@ -0,0 +1,71 @@ +{ + "actions": [], + "creation": "2021-08-19 00:14:14.857421", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "lead", + "lead_name", + "status", + "email", + "mobile_no" + ], + "fields": [ + { + "fieldname": "lead", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Lead", + "options": "Lead", + "reqd": 1 + }, + { + "fetch_from": "lead.lead_name", + "fetch_if_empty": 1, + "fieldname": "lead_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Lead Name" + }, + { + "fetch_from": "lead.status", + "fetch_if_empty": 1, + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Lead\nOpen\nReplied\nOpportunity\nQuotation\nLost Quotation\nInterested\nConverted\nDo Not Contact" + }, + { + "fetch_from": "lead.email_id", + "fetch_if_empty": 1, + "fieldname": "email", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Email", + "options": "Email" + }, + { + "fetch_from": "lead.mobile_no", + "fetch_if_empty": 1, + "fieldname": "mobile_no", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Mobile No", + "options": "Phone" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-08-20 01:58:39.387874", + "modified_by": "Administrator", + "module": "CRM", + "name": "Prospect Lead", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/crm/doctype/prospect_lead/prospect_lead.py b/erpnext/crm/doctype/prospect_lead/prospect_lead.py new file mode 100644 index 0000000000..2be5a5f39a --- /dev/null +++ b/erpnext/crm/doctype/prospect_lead/prospect_lead.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class ProspectLead(Document): + pass diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index cd94ee101a..608b703862 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -20,6 +20,7 @@ "tax_withholding_category", "default_bank_account", "lead_name", + "prospect", "image", "column_break0", "account_manager", @@ -212,8 +213,7 @@ "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Represents Company", - "options": "Company", - "unique": 1 + "options": "Company" }, { "depends_on": "represents_company", @@ -493,6 +493,12 @@ "fieldtype": "Link", "label": "Tax Withholding Category", "options": "Tax Withholding Category" + }, + { + "fieldname": "prospect", + "fieldtype": "Link", + "label": "Prospect", + "options": "Prospect" } ], "icon": "fa fa-user", @@ -500,7 +506,7 @@ "image_field": "image", "index_web_pages_for_search": 1, "links": [], - "modified": "2021-01-28 12:54:57.258959", + "modified": "2021-08-23 14:40:15.214350", "modified_by": "Administrator", "module": "Selling", "name": "Customer", From 797b19a14a4b201a2b03dca6a17978793225a22a Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Tue, 24 Aug 2021 15:50:15 +0530 Subject: [PATCH 02/11] feat: creation of prospect from lead --- erpnext/crm/doctype/lead/lead.js | 8 ++++ erpnext/crm/doctype/lead/lead.py | 19 +++++---- erpnext/crm/doctype/lead/lead_dashboard.py | 2 +- erpnext/crm/doctype/lead/lead_list.js | 46 +++++++++------------- erpnext/crm/doctype/prospect/prospect.py | 21 +++++++++- 5 files changed, 57 insertions(+), 39 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 9c21099088..f6eb3f4836 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -39,6 +39,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller 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")); + this.frm.add_custom_button(__("Prospect"), this.make_prospect, __("Create")); } if (!this.frm.is_new()) { @@ -70,6 +71,13 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller }) } + make_prospect () { + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.lead.lead.make_prospect", + frm: cur_frm + }) + } + company_name () { if (!this.frm.doc.lead_name) { this.frm.set_value("lead_name", this.frm.doc.company_name); diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index ecf6a41e6c..f76595e978 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -190,16 +190,6 @@ class Lead(SellingController): return contact -@frappe.whitelist() -def make_prospect(source_name, target_doc=None, leads=None): - print("``````````````````````````") - print(source_name) - print("``````````````````````````") - print(target_doc) - print("``````````````````````````") - print(leads) - print("``````````````````````````") - @frappe.whitelist() def make_customer(source_name, target_doc=None): return _make_customer(source_name, target_doc) @@ -272,6 +262,15 @@ def make_quotation(source_name, target_doc=None): return target_doc +@frappe.whitelist() +def make_prospect(source_name, target_doc=None): + target_doc = get_mapped_doc("Lead", source_name, + {"Lead": { + "doctype": "Prospect", + }}, target_doc) + + return target_doc + def _set_missing_values(source, target): address = frappe.get_all('Dynamic Link', { 'link_doctype': source.doctype, diff --git a/erpnext/crm/doctype/lead/lead_dashboard.py b/erpnext/crm/doctype/lead/lead_dashboard.py index 69d8ca7092..6038811a66 100644 --- a/erpnext/crm/doctype/lead/lead_dashboard.py +++ b/erpnext/crm/doctype/lead/lead_dashboard.py @@ -13,7 +13,7 @@ def get_data(): }, 'transactions': [ { - 'items': ['Opportunity', 'Quotation'] + 'items': ['Opportunity', 'Quotation', 'Prospect'] }, ] } \ No newline at end of file diff --git a/erpnext/crm/doctype/lead/lead_list.js b/erpnext/crm/doctype/lead/lead_list.js index 5cbbf76480..75208fa64b 100644 --- a/erpnext/crm/doctype/lead/lead_list.js +++ b/erpnext/crm/doctype/lead/lead_list.js @@ -2,34 +2,26 @@ frappe.listview_settings['Lead'] = { onload: function(listview) { if (frappe.boot.user.can_create.includes("Prospect")) { listview.page.add_action_item(__("Create Prospect"), function() { - let leads = listview.get_checked_items(); - console.log(listview.get_checked_items()); - frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.lead.lead.make_prospect", - frm: cur_frm, - leads: leads - }) + frappe.model.with_doctype("Prospect", function() { + let prospect = frappe.model.get_new_doc("Prospect"); + let leads = listview.get_checked_items(); + frappe.db.get_value("Lead", leads[0].name, ["company_name", "no_of_employees", "industry", "market_segment", "territory", "fax", "website", "lead_owner"], (r) => { + prospect.company_name = r.company_name; + prospect.no_of_employees = r.no_of_employees; + prospect.industry = r.industry; + prospect.market_segment = r.market_segment; + prospect.territory = r.territory; + prospect.fax = r.fax; + prospect.website = r.website; + prospect.prospect_owner = r.lead_owner; - // listview.call_for_selected_items(method, {"status": "Open"}); - // let prospect_lead = [] - // leads.forEach(lead => { - // prospect_lead.push({ - // "lead": lead.name - // }); - // }); - // console.log("check"); - // console.log(prospect_lead); - // frappe.new_doc("Prospect", { - // "company_name": leads[0].company_name, - // "industry": leads[0].industry, - // "market_segment": leads[0].market_segment, - // "territory": leads[0].territory, - // "no_of_employees": leads[0].no_of_employees, - // "fax": leads[0].fax, - // "website": leads[0].website, - // "prospect_owner": leads[0].lead_owner, - // "prospect_lead": prospect_lead - // }); + leads.forEach(function(lead) { + let lead_prospect_row = frappe.model.add_child(prospect, 'prospect_lead'); + lead_prospect_row.lead = lead.name; + }); + frappe.set_route("Form", "Prospect", prospect.name); + }); + }); }); } } diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py index a0175cd310..80e2459a90 100644 --- a/erpnext/crm/doctype/prospect/prospect.py +++ b/erpnext/crm/doctype/prospect/prospect.py @@ -6,7 +6,26 @@ from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc class Prospect(Document): - pass + def validate(self): + self.link_with_lead_contact_and_address() + + def link_with_lead_contact_and_address(self): + for row in self.prospect_lead: + links = frappe.get_all('Dynamic Link', filters={'link_doctype': 'Lead', 'link_name': row.lead}, fields=['parent', 'parenttype']) + for link in links: + linked_doc = frappe.get_doc(link['parenttype'], link['parent']) + exists = False + + for d in linked_doc.get('links'): + if d.link_doctype == self.doctype and d.link_name == self.name: + exists = True + + if not exists: + linked_doc.append('links', { + 'link_doctype': self.doctype, + 'link_name': self.name + }) + linked_doc.save(ignore_permissions=True) @frappe.whitelist() def make_customer(source_name, target_doc=None): From 3610882077d5108a0fbc402254026c8f60685359 Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Tue, 24 Aug 2021 15:51:57 +0530 Subject: [PATCH 03/11] fix: reverting local chnages --- erpnext/crm/doctype/lead/lead.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index f6eb3f4836..0bf2ab9615 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -52,7 +52,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller make_customer () { frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.lead.lead.make_prospect", + method: "erpnext.crm.doctype.lead.lead.make_customer", frm: cur_frm }) } From 0c6212189ebe7ff08e7d30b05ff904fb11fc4002 Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Tue, 24 Aug 2021 16:49:53 +0530 Subject: [PATCH 04/11] feat: link lead communication to prospect --- erpnext/crm/doctype/lead/lead.py | 10 ++++++++++ erpnext/crm/doctype/prospect/prospect.py | 22 +++++++++++++++++++++- erpnext/hooks.py | 3 +++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index f76595e978..693fc6a160 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -362,3 +362,13 @@ def daily_open_lead(): leads = frappe.get_all("Lead", filters = [["contact_date", "Between", [nowdate(), nowdate()]]]) for lead in leads: frappe.db.set_value("Lead", lead.name, "status", "Open") + +def add_prospect_link_in_communication(communication, method): + if communication.get('reference_doctype') == "Lead": + links = frappe.get_all('Prospect Lead', filters={'lead': communication.get('reference_name')}, fields=['parent', 'parenttype']) + + for link in links: + communication.append('timeline_links', { + 'link_doctype': link['parenttype'], + 'link_name': link['parent'] + }) diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py index 80e2459a90..bd278dca35 100644 --- a/erpnext/crm/doctype/prospect/prospect.py +++ b/erpnext/crm/doctype/prospect/prospect.py @@ -6,9 +6,12 @@ from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc class Prospect(Document): - def validate(self): + def after_save(self): self.link_with_lead_contact_and_address() + def on_trash(self): + self.unlink_dynamic_links() + def link_with_lead_contact_and_address(self): for row in self.prospect_lead: links = frappe.get_all('Dynamic Link', filters={'link_doctype': 'Lead', 'link_name': row.lead}, fields=['parent', 'parenttype']) @@ -27,6 +30,23 @@ class Prospect(Document): }) linked_doc.save(ignore_permissions=True) + def unlink_dynamic_links(self): + links = frappe.get_all('Dynamic Link', filters={'link_doctype': self.doctype, 'link_name': self.name}, fields=['parent', 'parenttype']) + + for link in links: + linked_doc = frappe.get_doc(link['parenttype'], link['parent']) + + if len(linked_doc.get('links')) == 1: + linked_doc.delete(ignore_permissions=True) + else: + to_remove = None + for d in linked_doc.get('links'): + if d.link_doctype == self.doctype and d.link_name == self.name: + to_remove = d + if to_remove: + linked_doc.remove(to_remove) + linked_doc.save(ignore_permissions=True) + @frappe.whitelist() def make_customer(source_name, target_doc=None): def set_missing_values(source, target): diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 8f7c7db208..8069a1566f 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -249,6 +249,9 @@ doc_events = { "on_update": [ "erpnext.support.doctype.service_level_agreement.service_level_agreement.update_hold_time", "erpnext.support.doctype.issue.issue.set_first_response_time" + ], + "after_insert": [ + "erpnext.crm.doctype.lead.lead.add_prospect_link_in_communication" ] }, ("Sales Taxes and Charges Template", 'Price List'): { From dfcac64b442b60fd329586f01b60b262d1be40de Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Wed, 25 Aug 2021 16:24:46 +0530 Subject: [PATCH 05/11] fix: review chnages --- erpnext/crm/doctype/lead/lead.js | 51 +++++++++++++++++-- erpnext/crm/doctype/lead/lead.py | 35 +++++++------ .../crm/doctype/opportunity/opportunity.js | 29 +++++++++-- .../crm/doctype/opportunity/opportunity.json | 6 +-- .../crm/doctype/opportunity/opportunity.py | 20 ++++++++ erpnext/crm/doctype/prospect/prospect.js | 21 +++----- erpnext/crm/doctype/prospect/prospect.py | 13 ++++- .../doctype/prospect_lead/prospect_lead.json | 22 ++++---- erpnext/hooks.py | 3 -- 9 files changed, 140 insertions(+), 60 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 0bf2ab9615..7f5f1a4cb4 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -40,6 +40,9 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller this.frm.add_custom_button(__("Opportunity"), this.make_opportunity, __("Create")); this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create")); this.frm.add_custom_button(__("Prospect"), this.make_prospect, __("Create")); + this.frm.add_custom_button(__('Add to Prospect'), function() { + cur_frm.trigger('add_lead_to_prospect') + }, __('Action')); } if (!this.frm.is_new()) { @@ -50,6 +53,34 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller } } + add_lead_to_prospect () { + frappe.prompt([ + { + fieldname: 'prospect', + label: __('Prospect'), + fieldtype: 'Link', + options: 'Prospect', + reqd: 1 + } + ], + function(data) { + frappe.call({ + method: 'erpnext.crm.doctype.lead.lead.add_lead_to_prospect', + args: { + 'lead': cur_frm.doc.name, + 'prospect': data.prospect + }, + callback: function(r) { + if (!r.exc) { + cur_frm.reload_doc(); + } + }, + freeze: true, + freeze_message: __('...Adding Lead to Prospect') + }) + }, __('Add Lead to Prospect'), __('Add')); + } + make_customer () { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_customer", @@ -72,10 +103,22 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller } make_prospect () { - frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.lead.lead.make_prospect", - frm: cur_frm - }) + frappe.model.with_doctype("Prospect", function() { + let prospect = frappe.model.get_new_doc("Prospect"); + prospect.company_name = cur_frm.doc.company_name; + prospect.no_of_employees = cur_frm.doc.no_of_employees; + prospect.industry = cur_frm.doc.industry; + prospect.market_segment = cur_frm.doc.market_segment; + prospect.territory = cur_frm.doc.territory; + prospect.fax = cur_frm.doc.fax; + prospect.website = cur_frm.doc.website; + prospect.prospect_owner = cur_frm.doc.lead_owner; + + let lead_prospect_row = frappe.model.add_child(prospect, 'prospect_lead'); + lead_prospect_row.lead = cur_frm.doc.name; + + frappe.set_route("Form", "Prospect", prospect.name); + }); } company_name () { diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 693fc6a160..c6115ee438 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -62,6 +62,7 @@ class Lead(SellingController): def on_update(self): self.add_calendar_event() + self.update_prospects() def before_insert(self): self.contact_doc = self.create_contact() @@ -88,6 +89,12 @@ class Lead(SellingController): "description": ('Contact ' + cstr(self.lead_name)) + (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '') }, force) + def update_prospects(self): + prospects = frappe.get_all('Prospect Lead', filters={'lead': self.name}, fields=['parent']) + for row in prospects: + prospect = frappe.get_doc('Prospect', row.parent) + prospect.save(ignore_permissions=True) + def check_email_id_is_unique(self): if self.email_id: # validate email is unique @@ -262,15 +269,6 @@ def make_quotation(source_name, target_doc=None): return target_doc -@frappe.whitelist() -def make_prospect(source_name, target_doc=None): - target_doc = get_mapped_doc("Lead", source_name, - {"Lead": { - "doctype": "Prospect", - }}, target_doc) - - return target_doc - def _set_missing_values(source, target): address = frappe.get_all('Dynamic Link', { 'link_doctype': source.doctype, @@ -363,12 +361,13 @@ def daily_open_lead(): for lead in leads: frappe.db.set_value("Lead", lead.name, "status", "Open") -def add_prospect_link_in_communication(communication, method): - if communication.get('reference_doctype') == "Lead": - links = frappe.get_all('Prospect Lead', filters={'lead': communication.get('reference_name')}, fields=['parent', 'parenttype']) - - for link in links: - communication.append('timeline_links', { - 'link_doctype': link['parenttype'], - 'link_name': link['parent'] - }) +@frappe.whitelist() +def add_lead_to_prospect(lead, prospect): + prospect = frappe.get_doc('Prospect', prospect) + prospect.append('prospect_lead', { + 'lead': lead + }) + prospect.save(ignore_permissions=True) + frappe.msgprint(_('Lead {0} has been added to prospect {1}.').format(frappe.bold(lead), frappe.bold(prospect.name)), + title=_('Lead Added'), indicator='green') + \ No newline at end of file diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index bcfae11a05..80588ee002 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -12,6 +12,14 @@ frappe.ui.form.on("Opportunity", { 'Supplier Quotation': 'Supplier Quotation' }; + frm.set_query("opportunity_from", function() { + return{ + "filters": { + "name": ["in", ["Customer", "Lead", "Prospect"]], + } + } + }); + if (frm.doc.opportunity_from && frm.doc.party_name){ frm.trigger('set_contact_link'); } @@ -87,10 +95,18 @@ frappe.ui.form.on("Opportunity", { }, __('Create')); } - frm.add_custom_button(__('Quotation'), - cur_frm.cscript.create_quotation, __('Create')); + if (frm.doc.opportunity_from != "Customer") { + frm.add_custom_button(__('Customer'), + function() { + frm.trigger("make_customer") + }, __('Create')); + } - } + frm.add_custom_button(__('Quotation'), + function() { + frm.trigger("create_quotation") + }, __('Create')); + } if(!frm.doc.__islocal && frm.perm[0].write && frm.doc.docstatus==0) { if(frm.doc.status==="Open") { @@ -187,6 +203,13 @@ erpnext.crm.Opportunity = class Opportunity extends frappe.ui.form.Controller { frm: cur_frm }) } + + make_customer() { + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.opportunity.opportunity.make_customer", + frm: cur_frm + }) + } }; extend_cscript(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm})); diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index e4b0e47309..12a564a9cb 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -78,13 +78,13 @@ }, { "fieldname": "opportunity_from", - "fieldtype": "Select", + "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, "label": "Opportunity From", "oldfieldname": "enquiry_from", "oldfieldtype": "Select", - "options": "\nLead\nProspect\nCustomer", + "options": "DocType", "print_hide": 1, "reqd": 1 }, @@ -430,7 +430,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2021-08-23 14:43:09.484227", + "modified": "2021-08-25 10:28:24.923543", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 23ad98a282..9e620312b9 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -265,6 +265,26 @@ def make_quotation(source_name, target_doc=None): return doclist +@frappe.whitelist() +def make_customer(source_name, target_doc=None): + def set_missing_values(source, target): + if source.opportunity_from == "Lead": + target.lead_name = source.party_name + if source.opportunity_from == "Prospect": + target.prospect = source.party_name + + doclist = get_mapped_doc("Opportunity", source_name, { + "Opportunity": { + "doctype": "Customer", + "field_map": { + "currency": "default_currency", + "customer_name": "customer_name" + } + } + }, target_doc, set_missing_values) + + return doclist + @frappe.whitelist() def make_request_for_quotation(source_name, target_doc=None): def update_item(obj, target, source_parent): diff --git a/erpnext/crm/doctype/prospect/prospect.js b/erpnext/crm/doctype/prospect/prospect.js index 793afccf3d..64256cf31f 100644 --- a/erpnext/crm/doctype/prospect/prospect.js +++ b/erpnext/crm/doctype/prospect/prospect.js @@ -19,21 +19,12 @@ frappe.ui.form.on('Prospect', { }) }, __("Create")); } - }, - make_customer () { - console.log("Make Customer"); - frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.prospect.prospect.make_customer", - frm: cur_frm - }) - }, - - make_opportunity () { - console.log("Make Opportunity"); - // frappe.model.open_mapped_doc({ - // method: "erpnext.crm.doctype.lead.lead.make_opportunity", - // frm: cur_frm - // }) + if (!cur_frm.is_new()) { + frappe.contacts.render_address_and_contact(cur_frm); + cur_frm.trigger('render_contact_day_html'); + } else { + frappe.contacts.clear_address_and_contact(cur_frm); + } } }); diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py index bd278dca35..dc165af825 100644 --- a/erpnext/crm/doctype/prospect/prospect.py +++ b/erpnext/crm/doctype/prospect/prospect.py @@ -6,12 +6,23 @@ from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc class Prospect(Document): - def after_save(self): + def validate(self): + self.update_lead_details() + + def on_update(self): self.link_with_lead_contact_and_address() def on_trash(self): self.unlink_dynamic_links() + def update_lead_details(self): + for row in self.get('prospect_lead'): + lead = frappe.get_value('Lead', row.lead, ['lead_name', 'status', 'email_id', 'mobile_no'], as_dict=True) + row.lead_name = lead.lead_name + row.status = lead.status + row.email = lead.email_id + row.mobile_no = lead.mobile_no + def link_with_lead_contact_and_address(self): for row in self.prospect_lead: links = frappe.get_all('Dynamic Link', filters={'link_doctype': 'Lead', 'link_name': row.lead}, fields=['parent', 'parenttype']) diff --git a/erpnext/crm/doctype/prospect_lead/prospect_lead.json b/erpnext/crm/doctype/prospect_lead/prospect_lead.json index 1797712a55..3c160d9e80 100644 --- a/erpnext/crm/doctype/prospect_lead/prospect_lead.json +++ b/erpnext/crm/doctype/prospect_lead/prospect_lead.json @@ -21,45 +21,41 @@ "reqd": 1 }, { - "fetch_from": "lead.lead_name", - "fetch_if_empty": 1, "fieldname": "lead_name", "fieldtype": "Data", "in_list_view": 1, - "label": "Lead Name" + "label": "Lead Name", + "read_only": 1 }, { - "fetch_from": "lead.status", - "fetch_if_empty": 1, "fieldname": "status", "fieldtype": "Select", "in_list_view": 1, "label": "Status", - "options": "Lead\nOpen\nReplied\nOpportunity\nQuotation\nLost Quotation\nInterested\nConverted\nDo Not Contact" + "options": "Lead\nOpen\nReplied\nOpportunity\nQuotation\nLost Quotation\nInterested\nConverted\nDo Not Contact", + "read_only": 1 }, { - "fetch_from": "lead.email_id", - "fetch_if_empty": 1, "fieldname": "email", "fieldtype": "Data", "in_list_view": 1, "label": "Email", - "options": "Email" + "options": "Email", + "read_only": 1 }, { - "fetch_from": "lead.mobile_no", - "fetch_if_empty": 1, "fieldname": "mobile_no", "fieldtype": "Data", "in_list_view": 1, "label": "Mobile No", - "options": "Phone" + "options": "Phone", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-08-20 01:58:39.387874", + "modified": "2021-08-25 12:58:24.638054", "modified_by": "Administrator", "module": "CRM", "name": "Prospect Lead", diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 8069a1566f..8f7c7db208 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -249,9 +249,6 @@ doc_events = { "on_update": [ "erpnext.support.doctype.service_level_agreement.service_level_agreement.update_hold_time", "erpnext.support.doctype.issue.issue.set_first_response_time" - ], - "after_insert": [ - "erpnext.crm.doctype.lead.lead.add_prospect_link_in_communication" ] }, ("Sales Taxes and Charges Template", 'Price List'): { From c644fbb1023029edd1a30003e14f3dfb5ef4e1fa Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Wed, 25 Aug 2021 16:35:34 +0530 Subject: [PATCH 06/11] fix: sider issues --- erpnext/crm/doctype/lead/lead.js | 6 ++--- erpnext/crm/doctype/prospect/prospect.js | 4 ++-- erpnext/crm/doctype/prospect/prospect.json | 27 +++++++++++++++------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 7f5f1a4cb4..dfb3b094dc 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -40,9 +40,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller this.frm.add_custom_button(__("Opportunity"), this.make_opportunity, __("Create")); this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create")); this.frm.add_custom_button(__("Prospect"), this.make_prospect, __("Create")); - this.frm.add_custom_button(__('Add to Prospect'), function() { - cur_frm.trigger('add_lead_to_prospect') - }, __('Action')); + this.frm.add_custom_button(__('Add to Prospect'), this.add_lead_to_prospect, __('Action')); } if (!this.frm.is_new()) { @@ -77,7 +75,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller }, freeze: true, freeze_message: __('...Adding Lead to Prospect') - }) + }); }, __('Add Lead to Prospect'), __('Add')); } diff --git a/erpnext/crm/doctype/prospect/prospect.js b/erpnext/crm/doctype/prospect/prospect.js index 64256cf31f..814fc2b6db 100644 --- a/erpnext/crm/doctype/prospect/prospect.js +++ b/erpnext/crm/doctype/prospect/prospect.js @@ -8,7 +8,7 @@ frappe.ui.form.on('Prospect', { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.prospect.prospect.make_customer", frm: cur_frm - }) + }); }, __("Create")); } if (!cur_frm.is_new() && frappe.boot.user.can_create.includes("Opportunity")) { @@ -16,7 +16,7 @@ frappe.ui.form.on('Prospect', { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.prospect.prospect.make_opportunity", frm: cur_frm - }) + }); }, __("Create")); } diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json index 6f476a6b65..2eb7cde02d 100644 --- a/erpnext/crm/doctype/prospect/prospect.json +++ b/erpnext/crm/doctype/prospect/prospect.json @@ -15,12 +15,14 @@ "no_of_employees", "currency", "annual_revenue", + "more_details_section", "fax", "website", + "column_break_13", "prospect_owner", "leads_section", "prospect_lead", - "addresses_and_contacts_section", + "address_and_contact_section", "address_html", "column_break_17", "contact_html", @@ -107,12 +109,6 @@ "fieldtype": "Table", "options": "Prospect Lead" }, - { - "depends_on": "eval: !doc.__islocal", - "fieldname": "addresses_and_contacts_section", - "fieldtype": "Section Break", - "label": "Addresses and Contacts" - }, { "fieldname": "address_html", "fieldtype": "HTML", @@ -136,11 +132,26 @@ { "fieldname": "notes", "fieldtype": "Text Editor" + }, + { + "fieldname": "more_details_section", + "fieldtype": "Section Break", + "label": "More Details" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: !doc.__islocal", + "fieldname": "address_and_contact_section", + "fieldtype": "Section Break", + "label": "Address and Contact" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-08-19 00:29:00.767038", + "modified": "2021-08-25 12:35:00.759909", "modified_by": "Administrator", "module": "CRM", "name": "Prospect", From 5ad8afcc8d6f06c62685f3cf24155cca4e172bf4 Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Wed, 25 Aug 2021 17:36:53 +0530 Subject: [PATCH 07/11] revert: creation of customer from opportunity --- .../crm/doctype/opportunity/opportunity.js | 14 ------------- .../crm/doctype/opportunity/opportunity.py | 20 ------------------- 2 files changed, 34 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 80588ee002..6ebb0dab57 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -95,13 +95,6 @@ frappe.ui.form.on("Opportunity", { }, __('Create')); } - if (frm.doc.opportunity_from != "Customer") { - frm.add_custom_button(__('Customer'), - function() { - frm.trigger("make_customer") - }, __('Create')); - } - frm.add_custom_button(__('Quotation'), function() { frm.trigger("create_quotation") @@ -203,13 +196,6 @@ erpnext.crm.Opportunity = class Opportunity extends frappe.ui.form.Controller { frm: cur_frm }) } - - make_customer() { - frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.opportunity.opportunity.make_customer", - frm: cur_frm - }) - } }; extend_cscript(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm})); diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 9e620312b9..23ad98a282 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -265,26 +265,6 @@ def make_quotation(source_name, target_doc=None): return doclist -@frappe.whitelist() -def make_customer(source_name, target_doc=None): - def set_missing_values(source, target): - if source.opportunity_from == "Lead": - target.lead_name = source.party_name - if source.opportunity_from == "Prospect": - target.prospect = source.party_name - - doclist = get_mapped_doc("Opportunity", source_name, { - "Opportunity": { - "doctype": "Customer", - "field_map": { - "currency": "default_currency", - "customer_name": "customer_name" - } - } - }, target_doc, set_missing_values) - - return doclist - @frappe.whitelist() def make_request_for_quotation(source_name, target_doc=None): def update_item(obj, target, source_parent): From fbac5149298332b9ea893080e9ffbc895c4edea8 Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Thu, 26 Aug 2021 12:14:29 +0530 Subject: [PATCH 08/11] feat: prospect permission --- erpnext/crm/doctype/prospect/prospect.json | 25 ++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json index 2eb7cde02d..237c4255c7 100644 --- a/erpnext/crm/doctype/prospect/prospect.json +++ b/erpnext/crm/doctype/prospect/prospect.json @@ -151,7 +151,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-08-25 12:35:00.759909", + "modified": "2021-08-26 12:10:49.191839", "modified_by": "Administrator", "module": "CRM", "name": "Prospect", @@ -159,7 +159,6 @@ "permissions": [ { "create": 1, - "delete": 1, "email": 1, "export": 1, "print": 1, @@ -168,9 +167,31 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1 } ], "sort_field": "modified", "sort_order": "DESC", + "title_field": "company_name", "track_changes": 1 } \ No newline at end of file From de488f68c02654cdd29d2148b1be9919c8d07274 Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Thu, 26 Aug 2021 14:23:44 +0530 Subject: [PATCH 09/11] adding test cases --- erpnext/crm/doctype/prospect/test_prospect.py | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/prospect/test_prospect.py b/erpnext/crm/doctype/prospect/test_prospect.py index f266a50593..0fffad1939 100644 --- a/erpnext/crm/doctype/prospect/test_prospect.py +++ b/erpnext/crm/doctype/prospect/test_prospect.py @@ -1,8 +1,54 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -# import frappe +import frappe import unittest +from frappe.utils import random_string +from erpnext.crm.doctype.lead.test_lead import make_lead +from erpnext.crm.doctype.lead.lead import add_lead_to_prospect + class TestProspect(unittest.TestCase): - pass + def test_add_lead_to_prospect_and_address_linking(self): + lead_doc = make_lead() + address_doc = make_address(address_title=lead_doc.name) + address_doc.append('links', { + "link_doctype": lead_doc.doctype, + "link_name": lead_doc.name + }) + address_doc.save() + prospect_doc = make_prospect() + add_lead_to_prospect(lead_doc.name, prospect_doc.name) + prospect_doc.reload() + lead_exists_in_prosoect = False + for rec in prospect_doc.get('prospect_lead'): + if rec.lead == lead_doc.name: + lead_exists_in_prosoect = True + self.assertEqual(lead_exists_in_prosoect, True) + address_doc.reload() + self.assertEqual(address_doc.has_link('Prospect', prospect_doc.name), True) + + +def make_prospect(**args): + args = frappe._dict(args) + + prospect_doc = frappe.get_doc({ + "doctype": "Prospect", + "company_name": args.company_name or "_Test Company {}".format(random_string(3)), + }).insert() + + return prospect_doc + +def make_address(**args): + args = frappe._dict(args) + + address_doc = frappe.get_doc({ + "doctype": "Address", + "address_title": args.address_title or "Address Title", + "address_type": args.address_type or "Billing", + "city": args.city or "Mumbai", + "address_line1": args.address_line1 or "Vidya Vihar West", + "country": args.country or "India" + }).insert() + + return address_doc From d701ad5313bf2f33a9e8125f24bbf4c9b49a8525 Mon Sep 17 00:00:00 2001 From: Anupam <anupamvns0099@gmail.com> Date: Fri, 27 Aug 2021 13:09:52 +0530 Subject: [PATCH 10/11] fix: using frm instead of cur_frm --- erpnext/crm/doctype/lead/lead.js | 38 ++++++++++++------------ erpnext/crm/doctype/prospect/prospect.js | 21 +++++++------ erpnext/crm/doctype/prospect/prospect.py | 4 +++ 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index dfb3b094dc..95cf03241b 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -51,7 +51,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller } } - add_lead_to_prospect () { + add_lead_to_prospect (frm) { frappe.prompt([ { fieldname: 'prospect', @@ -65,12 +65,12 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller frappe.call({ method: 'erpnext.crm.doctype.lead.lead.add_lead_to_prospect', args: { - 'lead': cur_frm.doc.name, + 'lead': frm.doc.name, 'prospect': data.prospect }, callback: function(r) { if (!r.exc) { - cur_frm.reload_doc(); + frm.reload_doc(); } }, freeze: true, @@ -79,41 +79,41 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller }, __('Add Lead to Prospect'), __('Add')); } - make_customer () { + make_customer (frm) { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_customer", - frm: cur_frm + frm: frm }) } - make_opportunity () { + make_opportunity (frm) { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_opportunity", - frm: cur_frm + frm: frm }) } - make_quotation () { + make_quotation (frm) { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_quotation", - frm: cur_frm + frm: frm }) } - make_prospect () { + make_prospect (frm) { frappe.model.with_doctype("Prospect", function() { let prospect = frappe.model.get_new_doc("Prospect"); - prospect.company_name = cur_frm.doc.company_name; - prospect.no_of_employees = cur_frm.doc.no_of_employees; - prospect.industry = cur_frm.doc.industry; - prospect.market_segment = cur_frm.doc.market_segment; - prospect.territory = cur_frm.doc.territory; - prospect.fax = cur_frm.doc.fax; - prospect.website = cur_frm.doc.website; - prospect.prospect_owner = cur_frm.doc.lead_owner; + prospect.company_name = frm.doc.company_name; + prospect.no_of_employees = frm.doc.no_of_employees; + prospect.industry = frm.doc.industry; + prospect.market_segment = frm.doc.market_segment; + prospect.territory = frm.doc.territory; + prospect.fax = frm.doc.fax; + prospect.website = frm.doc.website; + prospect.prospect_owner = frm.doc.lead_owner; let lead_prospect_row = frappe.model.add_child(prospect, 'prospect_lead'); - lead_prospect_row.lead = cur_frm.doc.name; + lead_prospect_row.lead = frm.doc.name; frappe.set_route("Form", "Prospect", prospect.name); }); diff --git a/erpnext/crm/doctype/prospect/prospect.js b/erpnext/crm/doctype/prospect/prospect.js index 814fc2b6db..67018e1ef9 100644 --- a/erpnext/crm/doctype/prospect/prospect.js +++ b/erpnext/crm/doctype/prospect/prospect.js @@ -2,29 +2,28 @@ // For license information, please see license.txt frappe.ui.form.on('Prospect', { - refresh () { - if (!cur_frm.is_new() && frappe.boot.user.can_create.includes("Customer")) { - cur_frm.add_custom_button(__("Customer"), function() { + refresh (frm) { + if (!frm.is_new() && frappe.boot.user.can_create.includes("Customer")) { + frm.add_custom_button(__("Customer"), function() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.prospect.prospect.make_customer", - frm: cur_frm + frm: frm }); }, __("Create")); } - if (!cur_frm.is_new() && frappe.boot.user.can_create.includes("Opportunity")) { - cur_frm.add_custom_button(__("Opportunity"), function() { + if (!frm.is_new() && frappe.boot.user.can_create.includes("Opportunity")) { + frm.add_custom_button(__("Opportunity"), function() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.prospect.prospect.make_opportunity", - frm: cur_frm + frm: frm }); }, __("Create")); } - if (!cur_frm.is_new()) { - frappe.contacts.render_address_and_contact(cur_frm); - cur_frm.trigger('render_contact_day_html'); + if (!frm.is_new()) { + frappe.contacts.render_address_and_contact(frm); } else { - frappe.contacts.clear_address_and_contact(cur_frm); + frappe.contacts.clear_address_and_contact(frm); } } }); diff --git a/erpnext/crm/doctype/prospect/prospect.py b/erpnext/crm/doctype/prospect/prospect.py index dc165af825..5f5815de5e 100644 --- a/erpnext/crm/doctype/prospect/prospect.py +++ b/erpnext/crm/doctype/prospect/prospect.py @@ -4,8 +4,12 @@ import frappe from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc +from frappe.contacts.address_and_contact import load_address_and_contact class Prospect(Document): + def onload(self): + load_address_and_contact(self) + def validate(self): self.update_lead_details() From e170ad2df362f2b7fe086e5f0062896c3371fda3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal <ruchamahabal2@gmail.com> Date: Fri, 27 Aug 2021 16:26:12 +0530 Subject: [PATCH 11/11] chore: add more fields to Prospect list view --- erpnext/crm/doctype/prospect/prospect.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json index 237c4255c7..3d6fba5123 100644 --- a/erpnext/crm/doctype/prospect/prospect.json +++ b/erpnext/crm/doctype/prospect/prospect.json @@ -39,6 +39,8 @@ { "fieldname": "industry", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Industry", "options": "Industry Type" }, @@ -57,6 +59,8 @@ { "fieldname": "territory", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Territory", "options": "Territory" }, @@ -72,12 +76,14 @@ { "fieldname": "currency", "fieldtype": "Link", + "in_list_view": 1, "label": "Currency", "options": "Currency" }, { "fieldname": "annual_revenue", "fieldtype": "Currency", + "in_list_view": 1, "label": "Annual Revenue", "options": "currency" }, @@ -151,7 +157,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-08-26 12:10:49.191839", + "modified": "2021-08-27 16:24:42.961967", "modified_by": "Administrator", "module": "CRM", "name": "Prospect",