[crm] Updated status and workflow for Lead > Opportunity > Quotation > Sales Order

This commit is contained in:
Rushabh Mehta
2013-10-03 17:26:33 +05:30
parent 43247cedea
commit 800b3aa437
21 changed files with 213 additions and 202 deletions

View File

@@ -26,24 +26,9 @@ class DocType(SellingController):
customer = webnotes.conn.get_value("Customer", {"lead_name": self.doc.name})
if customer:
self.doc.fields["__is_customer"] = customer
def on_communication(self, comm):
if comm.sender == self.get_sender(comm) or \
webnotes.conn.get_value("Profile", extract_email_id(comm.sender), "user_type")=="System User":
status = "Replied"
else:
status = "Open"
webnotes.conn.set(self.doc, 'status', status)
def check_status(self):
chk = webnotes.conn.sql("select status from `tabLead` where name=%s", self.doc.name)
chk = chk and chk[0][0] or ''
return cstr(chk)
def validate(self):
if self.doc.status == 'Lead Lost' and not self.doc.order_lost_reason:
webnotes.throw("Please Enter Lost Reason under More Info section")
self.set_status()
if self.doc.source == 'Campaign' and not self.doc.campaign_name and session['user'] != 'Guest':
webnotes.throw("Please specify campaign name")
@@ -75,14 +60,18 @@ class DocType(SellingController):
webnotes.msgprint(_("""Email Id must be unique, already exists for: """) + \
", ".join(items), raise_exception=True)
def get_sender(self, comm):
return webnotes.conn.get_value('Sales Email Settings',None,'email_id')
def on_trash(self):
webnotes.conn.sql("""update `tabSupport Ticket` set lead='' where lead=%s""",
self.doc.name)
self.delete_events()
def has_customer(self):
return webnotes.conn.get_value("Customer", {"lead_name": self.doc.name})
def has_opportunity(self):
return webnotes.conn.get_value("Opportunity", {"lead": self.doc.name, "docstatus": 1,
"status": ["!=", "Lost"]})
@webnotes.whitelist()
def make_customer(source_name, target_doclist=None):

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-04-10 11:45:37",
"docstatus": 0,
"modified": "2013-10-02 14:24:30",
"modified": "2013-10-03 17:24:33",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -102,7 +102,7 @@
"fieldtype": "Column Break"
},
{
"default": "Open",
"default": "Lead",
"doctype": "DocField",
"fieldname": "status",
"fieldtype": "Select",
@@ -112,7 +112,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nOpen\nReplied\nAttempted to Contact\nContact in Future\nContacted\nOpportunity Made\nInterested\nNot interested\nLead Lost\nConverted\nPassive",
"options": "Lead\nOpen\nReplied\nOpportunity\nInterested\nConverted\nDo Not Contact",
"reqd": 1,
"search_index": 1
},
@@ -334,18 +334,6 @@
"oldfieldtype": "Column Break",
"width": "50%"
},
{
"allow_on_submit": 0,
"depends_on": "eval:doc.status == 'Lead Lost'",
"doctype": "DocField",
"fieldname": "order_lost_reason",
"fieldtype": "Link",
"hidden": 0,
"label": "Lost Reason",
"oldfieldname": "order_lost_reason",
"oldfieldtype": "Link",
"options": "Quotation Lost Reason"
},
{
"allow_on_submit": 0,
"description": "Your sales person who will contact the lead in future",

View File

@@ -101,20 +101,13 @@ $.extend(cur_frm.cscript, new erpnext.selling.Opportunity({frm: cur_frm}));
cur_frm.cscript.refresh = function(doc, cdt, cdn){
erpnext.hide_naming_series();
cur_frm.dashboard.reset(doc);
if(!doc.__islocal) {
if(doc.status=="Converted" || doc.status=="Order Confirmed") {
cur_frm.dashboard.set_headline_alert(wn._(doc.status), "alert-success", "icon-ok-sign");
} else if(doc.status=="Opportunity Lost") {
cur_frm.dashboard.set_headline_alert(wn._(doc.status), "alert-danger", "icon-exclamation-sign");
}
}
cur_frm.clear_custom_buttons();
if(doc.docstatus === 1 && doc.status!=="Opportunity Lost") {
if(doc.docstatus === 1 && doc.status!=="Lost") {
cur_frm.add_custom_button('Create Quotation', cur_frm.cscript.create_quotation);
cur_frm.add_custom_button('Opportunity Lost', cur_frm.cscript['Declare Opportunity Lost']);
if(doc.status!=="Quotation") {
cur_frm.add_custom_button('Opportunity Lost', cur_frm.cscript['Declare Opportunity Lost']);
}
cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
}

View File

@@ -6,7 +6,7 @@ import webnotes
from webnotes.utils import cstr, getdate, cint
from webnotes.model.bean import getlist
from webnotes import msgprint
from webnotes import msgprint, _
from utilities.transaction_base import TransactionBase
@@ -67,7 +67,8 @@ class DocType(TransactionBase):
'email_id' : contact and contact[0]['email_id'] or ''
}
return ret
def on_update(self):
# Add to calendar
if self.doc.contact_date and self.doc.contact_date_ref != self.doc.contact_date:
@@ -120,6 +121,7 @@ class DocType(TransactionBase):
msgprint("Customer is mandatory if 'Opportunity From' is selected as Customer", raise_exception=1)
def validate(self):
self.set_status()
self.set_last_contact_date()
self.validate_item_details()
self.validate_uom_is_integer("uom", "qty")
@@ -127,42 +129,29 @@ class DocType(TransactionBase):
from accounts.utils import validate_fiscal_year
validate_fiscal_year(self.doc.transaction_date, self.doc.fiscal_year, "Opportunity Date")
self.doc.status = "Draft"
def on_submit(self):
webnotes.conn.set(self.doc, 'status', 'Submitted')
if self.doc.lead and webnotes.conn.get_value("Lead", self.doc.lead, "status")!="Converted":
webnotes.conn.set_value("Lead", self.doc.lead, "status", "Opportunity Made")
if self.doc.lead:
webnotes.bean("Lead", self.doc.lead).get_controller().set_status(update=True)
def on_cancel(self):
chk = webnotes.conn.sql("select t1.name from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.docstatus=1 and (t1.status!='Order Lost' and t1.status!='Cancelled') and t2.prevdoc_docname = %s",self.doc.name)
if chk:
msgprint("Quotation No. "+cstr(chk[0][0])+" is submitted against this Opportunity. Thus can not be cancelled.")
raise Exception
else:
webnotes.conn.set(self.doc, 'status', 'Cancelled')
if self.doc.lead and webnotes.conn.get_value("Lead", self.doc.lead,
"status")!="Converted":
if webnotes.conn.get_value("Communication", {"parent": self.doc.lead}):
status = "Contacted"
else:
status = "Open"
webnotes.conn.set_value("Lead", self.doc.lead, "status", status)
if self.has_quotation():
webnotes.throw(_("Cannot Cancel Opportunity as Quotation Exists"))
self.set_status(update=True)
def declare_enquiry_lost(self,arg):
chk = webnotes.conn.sql("select t1.name from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.docstatus=1 and (t1.status!='Order Lost' and t1.status!='Cancelled') and t2.prevdoc_docname = %s",self.doc.name)
if chk:
msgprint("Quotation No. "+cstr(chk[0][0])+" is submitted against this Opportunity. Thus 'Opportunity Lost' can not be declared against it.")
raise Exception
else:
webnotes.conn.set(self.doc, 'status', 'Opportunity Lost')
if not self.has_quotation():
webnotes.conn.set(self.doc, 'status', 'Lost')
webnotes.conn.set(self.doc, 'order_lost_reason', arg)
return 'true'
else:
webnotes.throw(_("Cannot declare as lost, because Quotation has been made."))
def on_trash(self):
self.delete_events()
def has_quotation(self):
return webnotes.conn.get_value("Quotation Item", {"prevdoc_docname": self.doc.name, "docstatus": 1})
@webnotes.whitelist()
def make_quotation(source_name, target_doclist=None):
from webnotes.model.mapper import get_mapped_doclist

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-03-07 18:50:30",
"docstatus": 0,
"modified": "2013-10-02 14:24:30",
"modified": "2013-10-03 16:30:58",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -126,7 +126,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nQuotation Sent\nOrder Confirmed\nOpportunity Lost\nCancelled",
"options": "Draft\nSubmitted\nQuotation\nLost\nCancelled\nReplied\nOpen",
"read_only": 1,
"reqd": 1
},
@@ -350,18 +350,6 @@
"options": "Campaign",
"read_only": 0
},
{
"depends_on": "eval:!doc.__islocal",
"doctype": "DocField",
"fieldname": "order_lost_reason",
"fieldtype": "Small Text",
"label": "Quotation Lost Reason",
"no_copy": 1,
"oldfieldname": "order_lost_reason",
"oldfieldtype": "Small Text",
"read_only": 1,
"report_hide": 0
},
{
"doctype": "DocField",
"fieldname": "company",
@@ -376,6 +364,15 @@
"reqd": 1,
"search_index": 1
},
{
"depends_on": "eval:!doc.__islocal",
"doctype": "DocField",
"fieldname": "order_lost_reason",
"fieldtype": "Text",
"label": "Lost Reason",
"no_copy": 1,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "column_break2",

View File

@@ -24,19 +24,10 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
},
refresh: function(doc, dt, dn) {
this._super(doc, dt, dn);
cur_frm.dashboard.reset(doc);
if(!doc.__islocal) {
if(doc.status=="Converted" || doc.status=="Order Confirmed") {
cur_frm.dashboard.set_headline_alert(wn._(doc.status), "alert-success", "icon-ok-sign");
} else if(doc.status==="Order Lost") {
cur_frm.dashboard.set_headline_alert(wn._(doc.status), "alert-danger", "icon-exclamation-sign");
}
}
if(doc.docstatus == 1 && doc.status!=='Order Lost') {
if(doc.docstatus == 1 && doc.status!=='Lost') {
cur_frm.add_custom_button('Make Sales Order', cur_frm.cscript['Make Sales Order']);
if(doc.status!=="Order Confirmed") {
if(doc.status!=="Ordered") {
cur_frm.add_custom_button('Set as Lost', cur_frm.cscript['Declare Order Lost']);
}
cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);

View File

@@ -47,6 +47,11 @@ class DocType(SellingController):
if not doc.fields.get(r):
doc.fields[r] = res[r]
def has_sales_order(self):
return webnotes.conn.get_value("Sales Order Item", {"prevdoc_docname": self.doc.name, "docstatus": 1})
# Re-calculates Basic Rate & amount based on Price List Selected
# --------------------------------------------------------------
def get_adj_percent(self, arg=''):
@@ -108,13 +113,7 @@ class DocType(SellingController):
def validate(self):
super(DocType, self).validate()
import utilities
if not self.doc.status:
self.doc.status = "Draft"
else:
utilities.validate_status(self.doc.status, ["Draft", "Submitted",
"Order Confirmed", "Order Lost", "Cancelled"])
self.set_status()
self.set_last_contact_date()
self.validate_order_type()
self.validate_for_items()
@@ -125,42 +124,22 @@ class DocType(SellingController):
sales_com_obj.check_active_sales_items(self)
sales_com_obj.validate_max_discount(self,'quotation_details')
sales_com_obj.check_conversion_rate(self)
def on_update(self):
# Set Quotation Status
webnotes.conn.set(self.doc, 'status', 'Draft')
#update enquiry
#------------------
def update_enquiry(self, flag):
prevdoc=''
for d in getlist(self.doclist, 'quotation_details'):
if d.prevdoc_docname:
prevdoc = d.prevdoc_docname
if prevdoc:
if flag == 'submit': #on submit
webnotes.conn.sql("update `tabOpportunity` set status = 'Quotation Sent' where name = %s", prevdoc)
elif flag == 'cancel': #on cancel
webnotes.conn.sql("update `tabOpportunity` set status = 'Open' where name = %s", prevdoc)
elif flag == 'order lost': #order lost
webnotes.conn.sql("update `tabOpportunity` set status = 'Opportunity Lost' where name=%s", prevdoc)
elif flag == 'order confirm': #order confirm
webnotes.conn.sql("update `tabOpportunity` set status='Order Confirmed' where name=%s", prevdoc)
def update_opportunity(self):
for opportunity in self.doclist.get_distinct_values("prevdoc_docname"):
webnotes.bean("Opportunity", opportunity).get_controller().set_status(update=True)
# declare as order lost
#-------------------------
def declare_order_lost(self, arg):
chk = webnotes.conn.sql("select t1.name from `tabSales Order` t1, `tabSales Order Item` t2 where t2.parent = t1.name and t1.docstatus=1 and t2.prevdoc_docname = %s",self.doc.name)
if chk:
msgprint("Sales Order No. "+cstr(chk[0][0])+" is submitted against this Quotation. Thus 'Order Lost' can not be declared against it.")
raise Exception
else:
webnotes.conn.set(self.doc, 'status', 'Order Lost')
if not self.has_sales_order():
webnotes.conn.set(self.doc, 'status', 'Lost')
webnotes.conn.set(self.doc, 'order_lost_reason', arg)
self.update_enquiry('order lost')
return 'true'
self.update_opportunity()
else:
webnotes.throw(_("Cannot set as Lost as Sales Order is made."))
#check if value entered in item table
#--------------------------------------
@@ -176,21 +155,17 @@ class DocType(SellingController):
# Check for Approving Authority
get_obj('Authorization Control').validate_approving_authority(self.doc.doctype, self.doc.company, self.doc.grand_total, self)
# Set Quotation Status
webnotes.conn.set(self.doc, 'status', 'Submitted')
#update enquiry status
self.update_enquiry('submit')
self.update_opportunity()
# ON CANCEL
# ==========================================================================
def on_cancel(self):
#update enquiry status
self.update_enquiry('cancel')
webnotes.conn.set(self.doc,'status','Cancelled')
self.set_status()
self.update_opportunity()
# Print other charges
# ===========================================================================
@@ -202,6 +177,7 @@ class DocType(SellingController):
lst1.append(d.total)
print_lst.append(lst1)
return print_lst
@webnotes.whitelist()
def make_sales_order(source_name, target_doclist=None):

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-05-24 19:29:08",
"docstatus": 0,
"modified": "2013-10-02 14:24:35",
"modified": "2013-10-03 16:31:55",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -734,7 +734,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nOrder Confirmed\nOrder Lost\nCancelled",
"options": "Draft\nSubmitted\nOrdered\nLost\nCancelled",
"print_hide": 1,
"read_only": 1,
"reqd": 1,

View File

@@ -66,7 +66,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
source_doctype: "Quotation",
get_query_filters: {
docstatus: 1,
status: ["!=", "Order Lost"],
status: ["!=", "Lost"],
order_type: cur_frm.doc.order_type,
customer: cur_frm.doc.customer || undefined,
company: cur_frm.doc.company

View File

@@ -165,36 +165,20 @@ class DocType(SellingController):
})
def check_prev_docstatus(self):
for d in getlist(self.doclist, 'sales_order_details'):
cancel_quo = webnotes.conn.sql("select name from `tabQuotation` where docstatus = 2 and name = '%s'" % d.prevdoc_docname)
if cancel_quo:
msgprint("Quotation :" + cstr(cancel_quo[0][0]) + " is already cancelled !")
raise Exception , "Validation Error. "
def update_enquiry_status(self, prevdoc, flag):
enq = webnotes.conn.sql("select t2.prevdoc_docname from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.name=%s", prevdoc)
if enq:
webnotes.conn.sql("update `tabOpportunity` set status = %s where name=%s",(flag,enq[0][0]))
def update_prevdoc_status(self, flag):
for d in getlist(self.doclist, 'sales_order_details'):
if d.prevdoc_docname:
if flag=='submit':
webnotes.conn.sql("update `tabQuotation` set status = 'Order Confirmed' where name=%s",d.prevdoc_docname)
#update enquiry
self.update_enquiry_status(d.prevdoc_docname, 'Order Confirmed')
elif flag == 'cancel':
chk = webnotes.conn.sql("select t1.name from `tabSales Order` t1, `tabSales Order Item` t2 where t2.parent = t1.name and t2.prevdoc_docname=%s and t1.name!=%s and t1.docstatus=1", (d.prevdoc_docname,self.doc.name))
if not chk:
webnotes.conn.sql("update `tabQuotation` set status = 'Submitted' where name=%s",d.prevdoc_docname)
#update enquiry
self.update_enquiry_status(d.prevdoc_docname, 'Quotation Sent')
def update_prevdoc_status(self, flag):
for quotation in self.doclist.get_distinct_values("prevdoc_docname"):
bean = webnotes.bean("Quotation", quotation)
if bean.doc.docstatus==2:
webnotes.throw(d.prevdoc_docname + ": " + webnotes._("Quotation is cancelled."))
bean.get_controller().set_status(update=True)
def on_submit(self):
self.check_prev_docstatus()
self.update_stock_ledger(update_stock = 1)
get_obj('Sales Common').check_credit(self,self.doc.grand_total)