fixed conflict

This commit is contained in:
Nabin Hait
2015-02-17 11:12:04 +05:30
62 changed files with 1220 additions and 897 deletions

0
erpnext/crm/__init__.py Normal file
View File

View File

View File

@@ -0,0 +1,9 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class Lead(Document):
pass

View File

View File

@@ -0,0 +1,63 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.provide("erpnext");
cur_frm.email_field = "email_id";
erpnext.LeadController = frappe.ui.form.Controller.extend({
setup: function() {
this.frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.customer_query" } }
},
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" } }
}
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" } }
}
},
refresh: function() {
var doc = this.frm.doc;
erpnext.toggle_naming_series();
if(!this.frm.doc.__islocal && this.frm.doc.__onload && !this.frm.doc.__onload.is_customer) {
this.frm.add_custom_button(__("Create Customer"), this.create_customer,
frappe.boot.doctype_icons["Customer"], "btn-default");
this.frm.add_custom_button(__("Create Opportunity"), this.create_opportunity,
frappe.boot.doctype_icons["Opportunity"], "btn-default");
cur_frm.add_custom_button(__("Send SMS"), this.frm.cscript.send_sms, "icon-mobile-phone");
}
if(!this.frm.doc.__islocal) {
erpnext.utils.render_address_and_contact(cur_frm);
}
},
create_customer: function() {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_customer",
frm: cur_frm
})
},
create_opportunity: function() {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
frm: cur_frm
})
}
});
$.extend(cur_frm.cscript, new erpnext.LeadController({frm: cur_frm}));
cur_frm.cscript.send_sms = function() {
frappe.require("assets/erpnext/js/sms_manager.js");
var sms_man = new SMSManager(cur_frm.doc);
}

View File

@@ -0,0 +1,429 @@
{
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-04-10 11:45:37",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"fields": [
{
"fieldname": "lead_details",
"fieldtype": "Section Break",
"label": "Lead Details",
"options": "icon-user",
"permlevel": 0
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"no_copy": 1,
"oldfieldname": "naming_series",
"oldfieldtype": "Select",
"options": "LEAD-",
"permlevel": 0,
"reqd": 0
},
{
"fieldname": "lead_name",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 0,
"label": "Contact Name",
"oldfieldname": "lead_name",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 1,
"search_index": 1
},
{
"fieldname": "company_name",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 1,
"label": "Organization Name",
"oldfieldname": "company_name",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 0,
"search_index": 0
},
{
"fieldname": "email_id",
"fieldtype": "Data",
"label": "Email Id",
"oldfieldname": "email_id",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 0,
"search_index": 1
},
{
"default": "Lead",
"fieldname": "status",
"fieldtype": "Select",
"in_filter": 1,
"in_list_view": 0,
"label": "Status",
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "Lead\nOpen\nReplied\nOpportunity\nInterested\nConverted\nDo Not Contact",
"permlevel": 0,
"reqd": 1,
"search_index": 1
},
{
"fieldname": "source",
"fieldtype": "Select",
"in_filter": 1,
"in_list_view": 0,
"label": "Source",
"no_copy": 1,
"oldfieldname": "source",
"oldfieldtype": "Select",
"options": "\nAdvertisement\nBlog Post\nCampaign\nCall\nCustomer\nExhibition\nSupplier\nWebsite\nEmail",
"permlevel": 0,
"reqd": 0,
"search_index": 0
},
{
"fieldname": "col_break123",
"fieldtype": "Column Break",
"permlevel": 0,
"width": "50%"
},
{
"depends_on": "eval:doc.source == 'Customer'",
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
"label": "From Customer",
"oldfieldname": "customer",
"oldfieldtype": "Link",
"options": "Customer",
"permlevel": 0
},
{
"depends_on": "eval:doc.source == 'Campaign'",
"description": "Enter campaign name if the source of lead is campaign.",
"fieldname": "campaign_name",
"fieldtype": "Link",
"hidden": 0,
"label": "Campaign Name",
"oldfieldname": "campaign_name",
"oldfieldtype": "Link",
"options": "Campaign",
"permlevel": 0
},
{
"default": "__user",
"fieldname": "lead_owner",
"fieldtype": "Link",
"in_filter": 1,
"label": "Lead Owner",
"oldfieldname": "lead_owner",
"oldfieldtype": "Link",
"options": "User",
"permlevel": 0,
"search_index": 1
},
{
"allow_on_submit": 0,
"fieldname": "contact_by",
"fieldtype": "Link",
"hidden": 0,
"in_filter": 1,
"label": "Next Contact By",
"oldfieldname": "contact_by",
"oldfieldtype": "Link",
"options": "User",
"permlevel": 0,
"print_hide": 0,
"reqd": 0,
"width": "100px"
},
{
"allow_on_submit": 0,
"description": "Add to calendar on this date",
"fieldname": "contact_date",
"fieldtype": "Datetime",
"in_filter": 1,
"label": "Next Contact Date",
"no_copy": 1,
"oldfieldname": "contact_date",
"oldfieldtype": "Date",
"permlevel": 0,
"reqd": 0,
"width": "100px"
},
{
"fieldname": "fold",
"fieldtype": "Fold",
"label": "fold",
"permlevel": 0
},
{
"fieldname": "contact_info",
"fieldtype": "Section Break",
"label": "Address & Contact",
"oldfieldtype": "Column Break",
"options": "icon-map-marker",
"permlevel": 0
},
{
"depends_on": "eval:doc.__islocal",
"fieldname": "address_desc",
"fieldtype": "HTML",
"hidden": 0,
"label": "Address Desc",
"options": "<em>Addresses will appear only when you save the lead</em>",
"permlevel": 0,
"print_hide": 1
},
{
"fieldname": "address_html",
"fieldtype": "HTML",
"hidden": 0,
"label": "Address HTML",
"permlevel": 0,
"print_hide": 0,
"read_only": 1
},
{
"fieldname": "column_break2",
"fieldtype": "Column Break",
"permlevel": 0
},
{
"fieldname": "phone",
"fieldtype": "Data",
"label": "Phone",
"oldfieldname": "contact_no",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 0
},
{
"fieldname": "mobile_no",
"fieldtype": "Data",
"label": "Mobile No.",
"oldfieldname": "mobile_no",
"oldfieldtype": "Data",
"permlevel": 0
},
{
"fieldname": "fax",
"fieldtype": "Data",
"label": "Fax",
"oldfieldname": "fax",
"oldfieldtype": "Data",
"permlevel": 0
},
{
"fieldname": "website",
"fieldtype": "Data",
"label": "Website",
"oldfieldname": "website",
"oldfieldtype": "Data",
"permlevel": 0
},
{
"description": "",
"fieldname": "territory",
"fieldtype": "Link",
"label": "Territory",
"oldfieldname": "territory",
"oldfieldtype": "Link",
"options": "Territory",
"permlevel": 0,
"print_hide": 1
},
{
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Info",
"oldfieldtype": "Section Break",
"options": "icon-file-text",
"permlevel": 0
},
{
"fieldname": "type",
"fieldtype": "Select",
"in_filter": 1,
"label": "Lead Type",
"oldfieldname": "type",
"oldfieldtype": "Select",
"options": "\nClient\nChannel Partner\nConsultant",
"permlevel": 0
},
{
"fieldname": "market_segment",
"fieldtype": "Select",
"hidden": 0,
"in_filter": 1,
"label": "Market Segment",
"oldfieldname": "market_segment",
"oldfieldtype": "Select",
"options": "\nLower Income\nMiddle Income\nUpper Income",
"permlevel": 0,
"reqd": 0,
"search_index": 0
},
{
"fieldname": "industry",
"fieldtype": "Link",
"label": "Industry",
"oldfieldname": "industry",
"oldfieldtype": "Link",
"options": "Industry Type",
"permlevel": 0
},
{
"fieldname": "request_type",
"fieldtype": "Select",
"label": "Request Type",
"oldfieldname": "request_type",
"oldfieldtype": "Select",
"options": "\nProduct Enquiry\nRequest for Information\nSuggestions\nOther",
"permlevel": 0
},
{
"fieldname": "fiscal_year",
"fieldtype": "Link",
"hidden": 1,
"in_filter": 1,
"label": "Fiscal Year",
"oldfieldname": "fiscal_year",
"oldfieldtype": "Select",
"options": "Fiscal Year",
"permlevel": 0
},
{
"fieldname": "column_break3",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"permlevel": 0,
"width": "50%"
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"reqd": 0
},
{
"fieldname": "unsubscribed",
"fieldtype": "Check",
"label": "Unsubscribed",
"permlevel": 0
},
{
"fieldname": "blog_subscriber",
"fieldtype": "Check",
"label": "Blog Subscriber",
"permlevel": 0
}
],
"icon": "icon-user",
"idx": 1,
"modified": "2015-02-16 23:54:10.622839",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"match": "",
"permlevel": 1,
"read": 1,
"report": 1,
"role": "All",
"submit": 0,
"write": 0
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"match": "",
"permlevel": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"submit": 0,
"write": 0
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"match": "",
"permlevel": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"submit": 0,
"write": 0
}
],
"search_fields": "lead_name,lead_owner,status",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "lead_name"
}

View File

@@ -0,0 +1,150 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# 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_add, cint, comma_and
from frappe import session
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from erpnext.utilities.address_and_contact import load_address_and_contact
class Lead(SellingController):
def get_feed(self):
return '{0}: {1}'.format(_(self.status), self.lead_name)
def set_sender(self, sender):
"""Will be called by **Communication** when a Lead is created from an incoming email."""
self.email_id = sender
def onload(self):
customer = frappe.db.get_value("Customer", {"lead_name": self.name})
self.get("__onload").is_customer = customer
load_address_and_contact(self, "lead")
def validate(self):
self._prev = frappe._dict({
"contact_date": frappe.db.get_value("Lead", self.name, "contact_date") 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,
})
self.set_status()
if self.source == 'Campaign' and not self.campaign_name and session['user'] != 'Guest':
frappe.throw(_("Campaign Name is required"))
if self.email_id:
if not validate_email_add(self.email_id):
frappe.throw(_('{0} is not a valid email id').format(self.email_id))
if self.email_id == self.lead_owner:
# Lead Owner cannot be same as the Lead
self.lead_owner = None
def on_update(self):
self.check_email_id_is_unique()
self.add_calendar_event()
def add_calendar_event(self, opts=None, force=False):
super(Lead, self).add_calendar_event({
"owner": self.lead_owner,
"starts_on": self.contact_date,
"subject": ('Contact ' + cstr(self.lead_name)),
"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
email_list = frappe.db.sql("""select name from tabLead where email_id=%s""",
self.email_id)
if len(email_list) > 1:
items = [e[0] for e in email_list if e[0]!=self.name]
frappe.throw(_("Email id must be unique, already exists for {0}").format(comma_and(items)))
def on_trash(self):
frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""",
self.name)
self.delete_events()
def has_customer(self):
return frappe.db.get_value("Customer", {"lead_name": self.name})
def has_opportunity(self):
return frappe.db.get_value("Opportunity", {"lead": self.name, "docstatus": 1,
"status": ["!=", "Lost"]})
@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:
target.customer_type = "Company"
target.customer_name = source.company_name
else:
target.customer_type = "Individual"
target.customer_name = source.lead_name
target.customer_group = frappe.db.get_default("customer_group")
doclist = get_mapped_doc("Lead", source_name,
{"Lead": {
"doctype": "Customer",
"field_map": {
"name": "lead_name",
"company_name": "customer_name",
"contact_no": "phone_1",
"fax": "fax_1"
}
}}, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
return doclist
@frappe.whitelist()
def make_opportunity(source_name, target_doc=None):
target_doc = get_mapped_doc("Lead", source_name,
{"Lead": {
"doctype": "Opportunity",
"field_map": {
"campaign_name": "campaign",
"doctype": "enquiry_from",
"name": "lead",
"lead_name": "contact_display",
"company_name": "customer_name",
"email_id": "contact_email",
"mobile_no": "contact_mobile"
}
}}, target_doc)
return target_doc
@frappe.whitelist()
def get_lead_details(lead):
if not lead: return {}
from erpnext.accounts.party import set_address_details
out = frappe._dict()
lead_doc = frappe.get_doc("Lead", lead)
lead = lead_doc
out.update({
"territory": lead.territory,
"customer_name": lead.company_name or lead.lead_name,
"contact_display": lead.lead_name,
"contact_email": lead.email_id,
"contact_mobile": lead.mobile_no,
"contact_phone": lead.phone,
})
set_address_details(out, lead, "Lead")
return out

View File

@@ -0,0 +1,23 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import unittest
test_records = frappe.get_test_records('Lead')
class TestLead(unittest.TestCase):
def test_make_customer(self):
from erpnext.crm.doctype.lead.lead import make_customer
frappe.delete_doc_if_exists("Customer", "_Test Lead")
customer = make_customer("_T-Lead-00001")
self.assertEquals(customer.doctype, "Customer")
self.assertEquals(customer.lead_name, "_T-Lead-00001")
customer.company = "_Test Company"
customer.customer_group = "_Test Customer Group"
customer.insert()

View File

@@ -0,0 +1,27 @@
[
{
"doctype": "Lead",
"email_id": "test_lead@example.com",
"lead_name": "_Test Lead",
"status": "Open",
"territory": "_Test Territory"
},
{
"doctype": "Lead",
"email_id": "test_lead1@example.com",
"lead_name": "_Test Lead 1",
"status": "Open"
},
{
"doctype": "Lead",
"email_id": "test_lead2@example.com",
"lead_name": "_Test Lead 2",
"status": "Lead"
},
{
"doctype": "Lead",
"email_id": "test_lead3@example.com",
"lead_name": "_Test Lead 3",
"status": "Converted"
}
]

View File

@@ -0,0 +1 @@
Potential sales opportunity (deal) from a Lead or Customer.

View File

@@ -0,0 +1,153 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on_change("Opportunity", "customer", function(frm) {
erpnext.utils.get_party_details(frm) });
frappe.ui.form.on_change("Opportunity", "customer_address", erpnext.utils.get_address_display);
frappe.ui.form.on_change("Opportunity", "contact_person", erpnext.utils.get_contact_details);
frappe.provide("erpnext.crm");
frappe.require("assets/erpnext/js/utils.js");
cur_frm.email_field = "contact_email";
// TODO commonify this code
erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
onload: function() {
if(!this.frm.doc.enquiry_from && this.frm.doc.customer)
this.frm.doc.enquiry_from = "Customer";
if(!this.frm.doc.enquiry_from && this.frm.doc.lead)
this.frm.doc.enquiry_from = "Lead";
if(!this.frm.doc.status)
set_multiple(cdt, cdn, { status:'Draft' });
if(!this.frm.doc.date)
this.frm.doc.transaction_date = date.obj_to_str(new Date());
if(!this.frm.doc.company && frappe.defaults.get_user_default("company"))
set_multiple(cdt, cdn, { company:frappe.defaults.get_user_default("company") });
if(!this.frm.doc.fiscal_year && sys_defaults.fiscal_year)
set_multiple(cdt, cdn, { fiscal_year:sys_defaults.fiscal_year });
this.setup_queries();
},
setup_queries: function() {
var me = this;
if(this.frm.fields_dict.contact_by.df.options.match(/^User/)) {
this.frm.set_query("contact_by", erpnext.queries.user);
}
this.frm.set_query("customer_address", function() {
if(me.frm.doc.lead) return {filters: { lead: me.frm.doc.lead } };
else if(me.frm.doc.customer) return {filters: { customer: me.frm.doc.customer } };
});
this.frm.set_query("item_code", "items", function() {
return {
query: "erpnext.controllers.queries.item_query",
filters: me.frm.doc.enquiry_type === "Maintenance" ?
{"is_service_item": "Yes"} : {"is_sales_item": "Yes"}
};
});
$.each([["lead", "lead"],
["customer", "customer"],
["contact_person", "customer_filter"],
["territory", "not_a_group_filter"]], function(i, opts) {
me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
});
},
create_quotation: function() {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
frm: cur_frm
})
}
});
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
erpnext.toggle_naming_series();
if(doc.docstatus === 1 && doc.status!=="Lost") {
cur_frm.add_custom_button(__('Create Quotation'),
cur_frm.cscript.create_quotation, frappe.boot.doctype_icons["Quotation"],
"btn-default");
if(doc.status!=="Quotation")
cur_frm.add_custom_button(__('Opportunity Lost'),
cur_frm.cscript['Declare Opportunity Lost'], "icon-remove", "btn-default");
cur_frm.add_custom_button(__('Send SMS'), cur_frm.cscript.send_sms,
"icon-mobile-phone", true);
}
}
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
if(doc.enquiry_from == 'Lead' && doc.lead)
cur_frm.cscript.lead(doc, cdt, cdn);
}
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if (d.item_code) {
return get_server_fields('get_item_details', d.item_code,
'items', doc, cdt, cdn, 1);
}
}
cur_frm.cscript.lead = function(doc, cdt, cdn) {
cur_frm.toggle_display("contact_info", doc.customer || doc.lead);
frappe.model.map_current_doc({
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
source_name: cur_frm.doc.lead,
frm: cur_frm
});
}
cur_frm.cscript['Declare Opportunity Lost'] = function() {
var dialog = new frappe.ui.Dialog({
title: __("Set as Lost"),
fields: [
{"fieldtype": "Text", "label": __("Reason for losing"), "fieldname": "reason",
"reqd": 1 },
{"fieldtype": "Button", "label": __("Update"), "fieldname": "update"},
]
});
dialog.fields_dict.update.$input.click(function() {
args = dialog.get_values();
if(!args) return;
return cur_frm.call({
doc: cur_frm.doc,
method: "declare_enquiry_lost",
args: args.reason,
callback: function(r) {
if(r.exc) {
msgprint(__("There were errors."));
} else {
dialog.hide();
cur_frm.refresh();
}
},
btn: this
})
});
dialog.show();
}
cur_frm.cscript.send_sms = function() {
frappe.require("assets/erpnext/js/sms_manager.js");
var sms_man = new SMSManager(cur_frm.doc);
}
cur_frm.cscript.company = function(doc, cdt, cdn) {
erpnext.get_fiscal_year(doc.company, doc.transaction_date);
}
cur_frm.cscript.transaction_date = function(doc, cdt, cdn){
erpnext.get_fiscal_year(doc.company, doc.transaction_date);
}

View File

@@ -0,0 +1,436 @@
{
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-03-07 18:50:30",
"description": "Potential Sales Deal",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Transaction",
"fields": [
{
"fieldname": "from_section",
"fieldtype": "Section Break",
"label": "From",
"options": "icon-user",
"permlevel": 0
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"no_copy": 1,
"oldfieldname": "naming_series",
"oldfieldtype": "Select",
"options": "OPTY-",
"permlevel": 0,
"read_only": 0,
"reqd": 1
},
{
"fieldname": "enquiry_from",
"fieldtype": "Select",
"in_list_view": 0,
"label": "Opportunity From",
"oldfieldname": "enquiry_from",
"oldfieldtype": "Select",
"options": "\nLead\nCustomer",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 1
},
{
"depends_on": "eval:doc.enquiry_from===\"Customer\"",
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
"in_filter": 1,
"in_list_view": 0,
"label": "Customer",
"no_copy": 1,
"oldfieldname": "customer",
"oldfieldtype": "Link",
"options": "Customer",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"reqd": 0,
"search_index": 0
},
{
"depends_on": "eval:doc.enquiry_from===\"Lead\"",
"fieldname": "lead",
"fieldtype": "Link",
"hidden": 0,
"in_filter": 1,
"in_list_view": 0,
"label": "Lead",
"oldfieldname": "lead",
"oldfieldtype": "Link",
"options": "Lead",
"permlevel": 0,
"print_hide": 1,
"read_only": 0
},
{
"depends_on": "",
"fieldname": "customer_name",
"fieldtype": "Data",
"hidden": 1,
"label": "Customer / Lead Name",
"permlevel": 0,
"print_hide": 0,
"read_only": 1
},
{
"fieldname": "column_break0",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"permlevel": 0,
"read_only": 0,
"width": "50%"
},
{
"fieldname": "enquiry_type",
"fieldtype": "Select",
"label": "Opportunity Type",
"oldfieldname": "enquiry_type",
"oldfieldtype": "Select",
"options": "\nSales\nMaintenance",
"permlevel": 0,
"read_only": 0,
"reqd": 1
},
{
"default": "Draft",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 0,
"label": "Status",
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "Draft\nSubmitted\nQuotation\nLost\nCancelled\nReplied\nOpen",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
"reqd": 1
},
{
"fieldname": "items_section",
"fieldtype": "Section Break",
"label": "Items",
"oldfieldtype": "Section Break",
"options": "icon-shopping-cart",
"permlevel": 0,
"read_only": 0
},
{
"description": "Items which do not exist in Item master can also be entered on customer's request",
"fieldname": "items",
"fieldtype": "Table",
"label": "Items",
"oldfieldname": "enquiry_details",
"oldfieldtype": "Table",
"options": "Opportunity Item",
"permlevel": 0,
"read_only": 0
},
{
"fieldname": "fold",
"fieldtype": "Fold",
"permlevel": 0
},
{
"depends_on": "eval:doc.lead || doc.customer",
"fieldname": "contact_info",
"fieldtype": "Section Break",
"label": "Contact Info",
"options": "icon-bullhorn",
"permlevel": 0,
"read_only": 0
},
{
"depends_on": "eval:doc.customer || doc.lead",
"fieldname": "customer_address",
"fieldtype": "Link",
"in_filter": 1,
"label": "Customer / Lead Address",
"options": "Address",
"permlevel": 0,
"print_hide": 1,
"read_only": 0
},
{
"fieldname": "address_display",
"fieldtype": "Small Text",
"hidden": 1,
"label": "Address",
"oldfieldname": "address",
"oldfieldtype": "Small Text",
"permlevel": 0,
"read_only": 1
},
{
"depends_on": "customer",
"description": "",
"fieldname": "territory",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"label": "Territory",
"options": "Territory",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"reqd": 0,
"search_index": 1
},
{
"depends_on": "customer",
"description": "",
"fieldname": "customer_group",
"fieldtype": "Link",
"hidden": 0,
"in_filter": 1,
"label": "Customer Group",
"oldfieldname": "customer_group",
"oldfieldtype": "Link",
"options": "Customer Group",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"reqd": 0,
"search_index": 1
},
{
"fieldname": "column_break3",
"fieldtype": "Column Break",
"permlevel": 0,
"read_only": 0
},
{
"depends_on": "eval:doc.lead || doc.customer",
"fieldname": "contact_person",
"fieldtype": "Link",
"in_filter": 1,
"label": "Contact Person",
"options": "Contact",
"permlevel": 0,
"print_hide": 1,
"read_only": 0
},
{
"depends_on": "customer",
"fieldname": "contact_display",
"fieldtype": "Small Text",
"label": "Contact",
"permlevel": 0,
"read_only": 1
},
{
"depends_on": "eval:doc.lead || doc.customer",
"fieldname": "contact_email",
"fieldtype": "Small Text",
"label": "Contact Email",
"permlevel": 0,
"read_only": 1
},
{
"depends_on": "eval:doc.lead || doc.customer",
"fieldname": "contact_mobile",
"fieldtype": "Small Text",
"label": "Contact Mobile No",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Info",
"oldfieldtype": "Section Break",
"options": "icon-file-text",
"permlevel": 0,
"read_only": 0
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"permlevel": 0,
"read_only": 0,
"width": "50%"
},
{
"default": "Today",
"fieldname": "transaction_date",
"fieldtype": "Date",
"label": "Opportunity Date",
"oldfieldname": "transaction_date",
"oldfieldtype": "Date",
"permlevel": 0,
"read_only": 0,
"reqd": 1,
"width": "50px"
},
{
"fieldname": "company",
"fieldtype": "Link",
"in_filter": 1,
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"reqd": 1,
"search_index": 1
},
{
"fieldname": "source",
"fieldtype": "Select",
"label": "Source",
"oldfieldname": "source",
"oldfieldtype": "Select",
"options": "\nExisting Customer\nReference\nAdvertisement\nCold Calling\nExhibition\nSupplier Reference\nMass Mailing\nCustomer's Vendor\nCampaign\nWalk In",
"permlevel": 0,
"read_only": 0
},
{
"description": "Enter name of campaign if source of enquiry is campaign",
"fieldname": "campaign",
"fieldtype": "Link",
"label": "Campaign",
"oldfieldname": "campaign",
"oldfieldtype": "Link",
"options": "Campaign",
"permlevel": 0,
"read_only": 0
},
{
"fieldname": "fiscal_year",
"fieldtype": "Link",
"in_filter": 1,
"label": "Fiscal Year",
"oldfieldname": "fiscal_year",
"oldfieldtype": "Select",
"options": "Fiscal Year",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"reqd": 1,
"search_index": 1
},
{
"depends_on": "eval:!doc.__islocal",
"fieldname": "order_lost_reason",
"fieldtype": "Text",
"label": "Lost Reason",
"no_copy": 1,
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "column_break2",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"permlevel": 0,
"read_only": 0,
"width": "50%"
},
{
"description": "Your sales person who will contact the customer in future",
"fieldname": "contact_by",
"fieldtype": "Link",
"in_filter": 1,
"label": "Next Contact By",
"oldfieldname": "contact_by",
"oldfieldtype": "Link",
"options": "User",
"permlevel": 0,
"read_only": 0,
"width": "75px"
},
{
"description": "Your sales person will get a reminder on this date to contact the customer",
"fieldname": "contact_date",
"fieldtype": "Datetime",
"label": "Next Contact Date",
"oldfieldname": "contact_date",
"oldfieldtype": "Date",
"permlevel": 0,
"read_only": 0
},
{
"fieldname": "to_discuss",
"fieldtype": "Small Text",
"label": "To Discuss",
"no_copy": 1,
"oldfieldname": "to_discuss",
"oldfieldtype": "Small Text",
"permlevel": 0,
"read_only": 0
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Amended From",
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Opportunity",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
"width": "150px"
}
],
"icon": "icon-info-sign",
"idx": 1,
"is_submittable": 1,
"modified": "2015-02-16 23:52:23.489259",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"apply_user_permissions": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"submit": 1,
"write": 1
}
],
"search_fields": "status,transaction_date,customer,lead,enquiry_type,territory,company",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "customer_name"
}

View File

@@ -0,0 +1,174 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils import cstr, cint
from frappe import msgprint, _
from frappe.model.mapper import get_mapped_doc
from erpnext.utilities.transaction_base import TransactionBase
class Opportunity(TransactionBase):
def validate(self):
self._prev = frappe._dict({
"contact_date": frappe.db.get_value("Opportunity", self.name, "contact_date") if \
(not cint(self.get("__islocal"))) else None,
"contact_by": frappe.db.get_value("Opportunity", self.name, "contact_by") if \
(not cint(self.get("__islocal"))) else None,
})
if not self.enquiry_from:
frappe.throw(_("Opportunity From field is mandatory"))
self.set_status()
self.validate_item_details()
self.validate_uom_is_integer("uom", "qty")
self.validate_lead_cust()
self.validate_cust_name()
from erpnext.accounts.utils import validate_fiscal_year
validate_fiscal_year(self.transaction_date, self.fiscal_year, "Opportunity Date")
def on_submit(self):
if self.lead:
frappe.get_doc("Lead", self.lead).set_status(update=True)
def on_cancel(self):
if self.has_quotation():
frappe.throw(_("Cannot Cancel Opportunity as Quotation Exists"))
self.set_status(update=True)
def declare_enquiry_lost(self,arg):
if not self.has_quotation():
frappe.db.set(self, 'status', 'Lost')
frappe.db.set(self, 'order_lost_reason', arg)
else:
frappe.throw(_("Cannot declare as lost, because Quotation has been made."))
def on_trash(self):
self.delete_events()
def has_quotation(self):
return frappe.db.get_value("Quotation Item", {"prevdoc_docname": self.name, "docstatus": 1})
def has_ordered_quotation(self):
return frappe.db.sql("""select q.name from `tabQuotation` q, `tabQuotation Item` qi
where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s and q.status = 'Ordered'""", self.name)
def validate_cust_name(self):
self.customer_name = self.customer or self.lead
def get_item_details(self, item_code):
item = frappe.db.sql("""select item_name, stock_uom, description_html, description, item_group, brand
from `tabItem` where name = %s""", item_code, as_dict=1)
ret = {
'item_name': item and item[0]['item_name'] or '',
'uom': item and item[0]['stock_uom'] or '',
'description': item and item[0]['description_html'] or item[0]['description'] or '',
'item_group': item and item[0]['item_group'] or '',
'brand': item and item[0]['brand'] or ''
}
return ret
def get_cust_address(self,name):
details = frappe.db.sql("""select customer_name, address, territory, customer_group
from `tabCustomer` where name = %s and docstatus != 2""", (name), as_dict = 1)
if details:
ret = {
'customer_name': details and details[0]['customer_name'] or '',
'address' : details and details[0]['address'] or '',
'territory' : details and details[0]['territory'] or '',
'customer_group' : details and details[0]['customer_group'] or ''
}
# ********** get primary contact details (this is done separately coz. , in case there is no primary contact thn it would not be able to fetch customer details in case of join query)
contact_det = frappe.db.sql("""select contact_name, contact_no, email_id
from `tabContact` where customer = %s and is_customer = 1
and is_primary_contact = 'Yes' and docstatus != 2""", name, as_dict = 1)
ret['contact_person'] = contact_det and contact_det[0]['contact_name'] or ''
ret['contact_no'] = contact_det and contact_det[0]['contact_no'] or ''
ret['email_id'] = contact_det and contact_det[0]['email_id'] or ''
return ret
else:
frappe.throw(_("Customer {0} does not exist").format(name), frappe.DoesNotExistError)
def on_update(self):
self.add_calendar_event()
def add_calendar_event(self, opts=None, force=False):
if not opts:
opts = frappe._dict()
opts.description = ""
opts.contact_date = self.contact_date
if self.customer:
if self.contact_person:
opts.description = 'Contact '+cstr(self.contact_person)
else:
opts.description = 'Contact customer '+cstr(self.customer)
elif self.lead:
if self.contact_display:
opts.description = 'Contact '+cstr(self.contact_display)
else:
opts.description = 'Contact lead '+cstr(self.lead)
opts.subject = opts.description
opts.description += '. By : ' + cstr(self.contact_by)
if self.to_discuss:
opts.description += ' To Discuss : ' + cstr(self.to_discuss)
super(Opportunity, self).add_calendar_event(opts, force)
def validate_item_details(self):
if not self.get('items'):
frappe.throw(_("Items required"))
def validate_lead_cust(self):
if self.enquiry_from == 'Lead':
if not self.lead:
frappe.throw(_("Lead must be set if Opportunity is made from Lead"))
else:
self.customer = None
elif self.enquiry_from == 'Customer':
if not self.customer:
msgprint("Customer is mandatory if 'Opportunity From' is selected as Customer", raise_exception=1)
else:
self.lead = None
@frappe.whitelist()
def make_quotation(source_name, target_doc=None):
def set_missing_values(source, target):
quotation = frappe.get_doc(target)
quotation.run_method("set_missing_values")
quotation.run_method("calculate_taxes_and_totals")
doclist = get_mapped_doc("Opportunity", source_name, {
"Opportunity": {
"doctype": "Quotation",
"field_map": {
"enquiry_from": "quotation_to",
"enquiry_type": "order_type",
"name": "enq_no",
},
"validation": {
"docstatus": ["=", 1]
}
},
"Opportunity Item": {
"doctype": "Quotation Item",
"field_map": {
"parent": "prevdoc_docname",
"parenttype": "prevdoc_doctype",
"uom": "stock_uom"
},
"add_if_empty": True
}
}, target_doc, set_missing_values)
return doclist

View File

@@ -0,0 +1,10 @@
frappe.listview_settings['Opportunity'] = {
add_fields: ["customer_name", "enquiry_type", "enquiry_from", "status"],
get_indicator: function(doc) {
var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status];
if(doc.status=="Quotation") {
indicator[1] = "green";
}
return indicator;
}
};

View File

@@ -0,0 +1,10 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and Contributors
# See license.txt
import frappe
import unittest
test_records = frappe.get_test_records('Opportunity')
class TestOpportunity(unittest.TestCase):
pass

View File

@@ -0,0 +1,15 @@
[
{
"doctype": "Opportunity",
"name": "_Test Opportunity 1",
"enquiry_from": "Lead",
"enquiry_type": "Sales",
"lead": "_T-Lead-00001",
"transaction_date": "2013-12-12",
"fiscal_year": "_Test Fiscal Year 2013",
"items": [{
"item_name": "Test Item",
"description": "Some description"
}]
}
]