[minor] fixed conflict

This commit is contained in:
Nabin Hait
2013-10-15 11:53:03 +05:30
380 changed files with 7668 additions and 5785 deletions

View File

@@ -34,7 +34,6 @@ cur_frm.cscript.refresh = function(doc,dt,dn) {
cur_frm.cscript.make_contact(doc,dt,dn);
cur_frm.communication_view = new wn.views.CommunicationList({
list: wn.model.get("Communication", {"customer": doc.name}),
parent: cur_frm.fields_dict.communication_html.wrapper,
doc: doc,
});
@@ -45,7 +44,7 @@ cur_frm.cscript.setup_dashboard = function(doc) {
cur_frm.dashboard.reset(doc);
if(doc.__islocal)
return;
cur_frm.dashboard.set_headline('<span class="text-muted">Loading...</span>')
cur_frm.dashboard.set_headline('<span class="text-muted">'+ wn._('Loading...')+ '</span>')
cur_frm.dashboard.add_doctype_badge("Opportunity", "customer");
cur_frm.dashboard.add_doctype_badge("Quotation", "customer");
@@ -99,7 +98,7 @@ cur_frm.cscript.make_contact = function() {
return "select name, first_name, last_name, email_id, phone, mobile_no, department, designation, is_primary_contact from tabContact where customer='"+cur_frm.docname+"' and docstatus != 2 order by is_primary_contact desc"
},
as_dict: 1,
no_results_message: 'No contacts created',
no_results_message: wn._('No contacts created'),
render_row: cur_frm.cscript.render_contact_row,
});
// note: render_contact_row is defined in contact_control.js
@@ -119,4 +118,4 @@ cur_frm.fields_dict.lead_name.get_query = function(doc,cdt,cdn) {
return{
query:"controllers.queries.lead_query"
}
}
}

View File

@@ -67,9 +67,9 @@ class DocType(TransactionBase):
ac_bean.ignore_permissions = True
ac_bean.insert()
msgprint("Account Head: %s created" % ac_bean.doc.name)
msgprint(_("Account Head") + ": " + ac_bean.doc.name + _(" created"))
else :
msgprint("Please Select Company under which you want to create account head")
msgprint(_("Please Select Company under which you want to create account head"))
def update_credit_days_limit(self):
webnotes.conn.sql("""update tabAccount set credit_days = %s, credit_limit = %s

View File

@@ -38,7 +38,6 @@ class DocType(TransactionBase):
self.check_item_table()
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_active_sales_items(self)
sales_com_obj.get_prevdoc_date(self)
def validate_fiscal_year(self):
from accounts.utils import validate_fiscal_year

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-02-22 01:27:51",
"docstatus": 0,
"modified": "2013-07-10 14:54:09",
"modified": "2013-10-10 17:02:31",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -48,18 +48,6 @@
"read_only": 1,
"width": "300px"
},
{
"doctype": "DocField",
"fieldname": "prevdoc_date",
"fieldtype": "Date",
"hidden": 0,
"in_list_view": 1,
"label": "Delivery Date",
"oldfieldname": "prevdoc_date",
"oldfieldtype": "Date",
"print_hide": 0,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "serial_no",

View File

@@ -29,7 +29,7 @@ def add_sales_communication(subject, content, sender, real_name, mail=None,
parent_name = contact_name or lead_name
message = make(content=content, sender=sender, subject=subject,
doctype = parent_doctype, name = parent_name, date=date)
doctype = parent_doctype, name = parent_name, date=date, sent_or_received="Received")
if mail:
# save attachments to parent if from mail

View File

@@ -33,7 +33,7 @@ erpnext.LeadController = wn.ui.form.Controller.extend({
var doc = this.frm.doc;
erpnext.hide_naming_series();
this.frm.clear_custom_buttons();
this.frm.__is_customer = this.frm.__is_customer || this.frm.doc.__is_customer;
if(!this.frm.doc.__islocal && !this.frm.__is_customer) {
this.frm.add_custom_button("Create Customer", this.create_customer);

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):
@@ -125,7 +114,11 @@ def make_opportunity(source_name, target_doclist=None):
"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_doclist)
return [d.fields for d in doclist]
return [d if isinstance(d, dict) else d.fields for d in doclist]

View File

@@ -2,11 +2,12 @@
{
"creation": "2013-04-10 11:45:37",
"docstatus": 0,
"modified": "2013-09-10 10:52:20",
"modified": "2013-10-09 15:27:54",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"document_type": "Master",
@@ -101,7 +102,7 @@
"fieldtype": "Column Break"
},
{
"default": "Open",
"default": "Lead",
"doctype": "DocField",
"fieldname": "status",
"fieldtype": "Select",
@@ -111,7 +112,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nOpen\nReplied\nAttempted to Contact\nContact in Future\nContacted\nInterested\nNot interested\nLead Lost\nConverted\nPassive",
"options": "Lead\nOpen\nReplied\nOpportunity\nInterested\nConverted\nDo Not Contact",
"reqd": 1,
"search_index": 1
},
@@ -158,10 +159,64 @@
"doctype": "DocField",
"fieldname": "communication_history",
"fieldtype": "Section Break",
"label": "Communication History",
"label": "Communication",
"options": "icon-comments",
"print_hide": 1
},
{
"default": "__user",
"doctype": "DocField",
"fieldname": "lead_owner",
"fieldtype": "Link",
"in_filter": 1,
"label": "Lead Owner",
"oldfieldname": "lead_owner",
"oldfieldtype": "Link",
"options": "Profile",
"search_index": 1
},
{
"doctype": "DocField",
"fieldname": "col_break123",
"fieldtype": "Column Break",
"width": "50%"
},
{
"allow_on_submit": 0,
"description": "Your sales person who will contact the lead in future",
"doctype": "DocField",
"fieldname": "contact_by",
"fieldtype": "Link",
"hidden": 0,
"in_filter": 1,
"label": "Next Contact By",
"oldfieldname": "contact_by",
"oldfieldtype": "Link",
"options": "Profile",
"print_hide": 0,
"reqd": 0,
"width": "100px"
},
{
"allow_on_submit": 0,
"description": "Your sales person will get a reminder on this date to contact the lead",
"doctype": "DocField",
"fieldname": "contact_date",
"fieldtype": "Date",
"in_filter": 1,
"label": "Next Contact Date",
"no_copy": 1,
"oldfieldname": "contact_date",
"oldfieldtype": "Date",
"reqd": 0,
"width": "100px"
},
{
"doctype": "DocField",
"fieldname": "sec_break123",
"fieldtype": "Section Break",
"options": "Simple"
},
{
"allow_on_submit": 0,
"doctype": "DocField",
@@ -169,8 +224,7 @@
"fieldtype": "HTML",
"label": "Communication HTML",
"oldfieldname": "follow_up",
"oldfieldtype": "Table",
"print_hide": 1
"oldfieldtype": "Table"
},
{
"doctype": "DocField",
@@ -274,18 +328,6 @@
"oldfieldtype": "Select",
"options": "\nClient\nChannel Partner\nConsultant"
},
{
"default": "__user",
"doctype": "DocField",
"fieldname": "lead_owner",
"fieldtype": "Link",
"in_filter": 1,
"label": "Lead Owner",
"oldfieldname": "lead_owner",
"oldfieldtype": "Link",
"options": "Profile",
"search_index": 1
},
{
"doctype": "DocField",
"fieldname": "market_segment",
@@ -335,61 +377,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",
"doctype": "DocField",
"fieldname": "contact_by",
"fieldtype": "Link",
"hidden": 0,
"in_filter": 1,
"label": "Next Contact By",
"oldfieldname": "contact_by",
"oldfieldtype": "Link",
"options": "Profile",
"print_hide": 0,
"reqd": 0,
"width": "100px"
},
{
"allow_on_submit": 0,
"description": "Your sales person will get a reminder on this date to contact the lead",
"doctype": "DocField",
"fieldname": "contact_date",
"fieldtype": "Date",
"in_filter": 1,
"label": "Next Contact Date",
"no_copy": 1,
"oldfieldname": "contact_date",
"oldfieldtype": "Date",
"reqd": 0,
"width": "100px"
},
{
"depends_on": "eval:!doc.__islocal",
"description": "Date on which the lead was last contacted",
"doctype": "DocField",
"fieldname": "last_contact_date",
"fieldtype": "Date",
"label": "Last Contact Date",
"no_copy": 1,
"oldfieldname": "last_contact_date",
"oldfieldtype": "Date",
"print_hide": 1,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "company",
@@ -418,8 +405,7 @@
"fieldtype": "Table",
"hidden": 1,
"label": "Communications",
"options": "Communication",
"print_hide": 1
"options": "Communication"
},
{
"cancel": 1,

View File

@@ -53,6 +53,11 @@ erpnext.selling.Opportunity = wn.ui.form.Controller.extend({
this.frm.set_query("contact_by", erpnext.queries.profile);
}
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", "enquiry_details", function() {
return {
query: "controllers.queries.item_query",
@@ -63,7 +68,6 @@ erpnext.selling.Opportunity = wn.ui.form.Controller.extend({
$.each([["lead", "lead"],
["customer", "customer"],
["customer_address", "customer_filter"],
["contact_person", "customer_filter"],
["territory", "not_a_group_filter"]], function(i, opts) {
me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
@@ -97,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);
}
@@ -151,8 +148,14 @@ cur_frm.cscript.lead_cust_show = function(doc,cdt,cdn){
}
}
cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
if(doc.customer) return get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc, dt, dn) {
args = {
address: doc.customer_address,
contact: doc.contact_person
}
if(doc.customer) args.update({customer: doc.customer});
return get_server_fields('get_customer_address', JSON.stringify(args),'', doc, dt, dn, 1);
}
cur_frm.cscript.lead = function(doc, cdt, cdn) {
@@ -163,7 +166,7 @@ cur_frm.cscript.lead = function(doc, cdt, cdn) {
source_name: cur_frm.doc.lead
})
unhide_field(['customer_name', 'address_display','contact_mobile',
unhide_field(['customer_name', 'address_display','contact_mobile', 'customer_address',
'contact_email', 'territory']);
}

View File

@@ -4,9 +4,9 @@
from __future__ import unicode_literals
import webnotes
from webnotes.utils import cstr, getdate, cint
from webnotes.utils import cstr, cint
from webnotes.model.bean import getlist
from webnotes import msgprint
from webnotes import msgprint, _
from utilities.transaction_base import TransactionBase
@@ -17,7 +17,7 @@ class DocType(TransactionBase):
self.doclist = doclist
self.fname = 'enq_details'
self.tname = 'Opportunity Item'
self._prev = webnotes._dict({
"contact_date": webnotes.conn.get_value("Opportunity", self.doc.name, "contact_date") if \
(not cint(self.doc.fields.get("__islocal"))) else None,
@@ -67,12 +67,9 @@ 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:
webnotes.conn.set(self.doc, 'contact_date_ref',self.doc.contact_date)
self.add_calendar_event()
def add_calendar_event(self, opts=None, force=False):
@@ -100,14 +97,6 @@ class DocType(TransactionBase):
super(DocType, self).add_calendar_event(opts, force)
def set_last_contact_date(self):
if self.doc.contact_date_ref and self.doc.contact_date_ref != self.doc.contact_date:
if getdate(self.doc.contact_date_ref) < getdate(self.doc.contact_date):
self.doc.last_contact_date=self.doc.contact_date_ref
else:
msgprint("Contact Date Cannot be before Last Contact Date")
raise Exception
def validate_item_details(self):
if not getlist(self.doclist, 'enquiry_details'):
msgprint("Please select items for which enquiry needs to be made")
@@ -120,41 +109,36 @@ class DocType(TransactionBase):
msgprint("Customer is mandatory if 'Opportunity From' is selected as Customer", raise_exception=1)
def validate(self):
self.set_last_contact_date()
self.set_status()
self.validate_item_details()
self.validate_uom_is_integer("uom", "qty")
self.validate_lead_cust()
from accounts.utils import validate_fiscal_year
validate_fiscal_year(self.doc.transaction_date, self.doc.fiscal_year, "Opportunity Date")
if not self.doc.status:
self.doc.status = "Draft"
def on_submit(self):
webnotes.conn.set(self.doc, 'status', 'Submitted')
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.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,11 +2,12 @@
{
"creation": "2013-03-07 18:50:30",
"docstatus": 0,
"modified": "2013-09-10 10:52:49",
"modified": "2013-10-09 15:26:29",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_import": 1,
"autoname": "naming_series:",
"description": "Potential Sales Deal",
"doctype": "DocType",
@@ -125,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
},
@@ -168,7 +169,6 @@
"label": "Communication History",
"oldfieldtype": "Section Break",
"options": "icon-comments",
"print_hide": 1,
"read_only": 0
},
{
@@ -179,7 +179,6 @@
"label": "Communication HTML",
"oldfieldname": "follow_up",
"oldfieldtype": "Table",
"print_hide": 1,
"read_only": 0
},
{
@@ -190,34 +189,16 @@
"options": "icon-bullhorn",
"read_only": 0
},
{
"doctype": "DocField",
"fieldname": "contact_person",
"fieldtype": "Link",
"in_filter": 1,
"label": "Contact Person",
"options": "Contact",
"print_hide": 1,
"read_only": 0
},
{
"doctype": "DocField",
"fieldname": "customer_address",
"fieldtype": "Link",
"in_filter": 1,
"label": "Customer Address",
"label": "Customer / Lead Address",
"options": "Address",
"print_hide": 1,
"read_only": 0
},
{
"doctype": "DocField",
"fieldname": "customer_name",
"fieldtype": "Data",
"label": "Customer Name",
"print_hide": 0,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "address_display",
@@ -228,12 +209,60 @@
"oldfieldtype": "Small Text",
"read_only": 1
},
{
"description": "<a href=\"#Sales Browser/Territory\">To manage Territory, click here</a>",
"doctype": "DocField",
"fieldname": "territory",
"fieldtype": "Link",
"in_filter": 1,
"label": "Territory",
"options": "Territory",
"print_hide": 1,
"read_only": 0,
"reqd": 0,
"search_index": 1
},
{
"depends_on": "eval:doc.enquiry_from==\"Customer\"",
"description": "<a href=\"#Sales Browser/Customer Group\">To manage Territory, click here</a>",
"doctype": "DocField",
"fieldname": "customer_group",
"fieldtype": "Link",
"hidden": 0,
"in_filter": 1,
"label": "Customer Group",
"oldfieldname": "customer_group",
"oldfieldtype": "Link",
"options": "Customer Group",
"print_hide": 1,
"read_only": 0,
"reqd": 0,
"search_index": 1
},
{
"doctype": "DocField",
"fieldname": "column_break3",
"fieldtype": "Column Break",
"read_only": 0
},
{
"doctype": "DocField",
"fieldname": "customer_name",
"fieldtype": "Data",
"label": "Customer Name",
"print_hide": 0,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "contact_person",
"fieldtype": "Link",
"in_filter": 1,
"label": "Contact Person",
"options": "Contact",
"print_hide": 1,
"read_only": 0
},
{
"doctype": "DocField",
"fieldname": "contact_display",
@@ -255,36 +284,6 @@
"label": "Contact Mobile No",
"read_only": 1
},
{
"depends_on": "eval:doc.enquiry_from==\"Customer\"",
"description": "<a href=\"#Sales Browser/Customer Group\">To manage Territory, click here</a>",
"doctype": "DocField",
"fieldname": "customer_group",
"fieldtype": "Link",
"hidden": 0,
"in_filter": 1,
"label": "Customer Group",
"oldfieldname": "customer_group",
"oldfieldtype": "Link",
"options": "Customer Group",
"print_hide": 1,
"read_only": 0,
"reqd": 0,
"search_index": 1
},
{
"description": "<a href=\"#Sales Browser/Territory\">To manage Territory, click here</a>",
"doctype": "DocField",
"fieldname": "territory",
"fieldtype": "Link",
"in_filter": 1,
"label": "Territory",
"options": "Territory",
"print_hide": 1,
"read_only": 0,
"reqd": 0,
"search_index": 1
},
{
"description": "Filing in Additional Information about the Opportunity will help you analyze your data better.",
"doctype": "DocField",
@@ -351,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",
@@ -377,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",
@@ -408,20 +404,6 @@
"oldfieldtype": "Date",
"read_only": 0
},
{
"allow_on_submit": 0,
"depends_on": "eval:!doc.__islocal",
"description": "Date on which the lead was last contacted",
"doctype": "DocField",
"fieldname": "last_contact_date",
"fieldtype": "Date",
"label": "Last Contact Date",
"no_copy": 1,
"oldfieldname": "last_contact_date",
"oldfieldtype": "Date",
"print_hide": 1,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "to_discuss",
@@ -450,8 +432,7 @@
"fieldtype": "Table",
"hidden": 1,
"label": "Communications",
"options": "Communication",
"print_hide": 1
"options": "Communication"
},
{
"doctype": "DocPerm",

View File

@@ -11,6 +11,7 @@ cur_frm.cscript.sales_team_fname = "sales_team";
wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');
wn.require('app/selling/doctype/sales_common/sales_common.js');
wn.require('app/accounts/doctype/sales_invoice/pos.js');
erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
onload: function(doc, dt, dn) {
@@ -23,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);
@@ -82,12 +74,12 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
},
validate_company_and_party: function(party_field) {
if(this.frm.doc.quotation_to == "Lead") {
return true;
} else if(!this.frm.doc.quotation_to) {
if(!this.frm.doc.quotation_to) {
msgprint(wn._("Please select a value for" + " " + wn.meta.get_label(this.frm.doc.doctype,
"quotation_to", this.frm.doc.name)));
return false;
} else if (this.frm.doc.quotation_to == "Lead") {
return true;
} else {
return this._super(party_field);
}

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import webnotes
from webnotes.utils import cstr, getdate
from webnotes.utils import cstr
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
from webnotes import _, msgprint
@@ -47,17 +47,16 @@ 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=''):
get_obj('Sales Common').get_adj_percent(self)
# Get Tax rate if account type is TAX
# -----------------------------------
def get_rate(self,arg):
return get_obj('Sales Common').get_rate(arg)
# Does not allow same item code to be entered twice
# -------------------------------------------------
def validate_for_items(self):
@@ -92,30 +91,9 @@ class DocType(SellingController):
msgprint("You can not select non sales item "+d.item_code+" in Sales Quotation")
raise Exception
#--------------Validation For Last Contact Date-----------------
# ====================================================================================================================
def set_last_contact_date(self):
#if not self.doc.contact_date_ref:
#self.doc.contact_date_ref=self.doc.contact_date
#self.doc.last_contact_date=self.doc.contact_date_ref
if self.doc.contact_date_ref and self.doc.contact_date_ref != self.doc.contact_date:
if getdate(self.doc.contact_date_ref) < getdate(self.doc.contact_date):
self.doc.last_contact_date=self.doc.contact_date_ref
else:
msgprint("Contact Date Cannot be before Last Contact Date")
raise Exception
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_last_contact_date()
self.set_status()
self.validate_order_type()
self.validate_for_items()
@@ -125,42 +103,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 +134,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 +156,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,13 +2,14 @@
{
"creation": "2013-05-24 19:29:08",
"docstatus": 0,
"modified": "2013-09-10 10:46:33",
"modified": "2013-10-03 16:31:55",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
"allow_email": 0,
"allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"document_type": "Transaction",
@@ -733,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,
@@ -841,8 +842,7 @@
"fieldtype": "Table",
"hidden": 1,
"label": "Communications",
"options": "Communication",
"print_hide": 1
"options": "Communication"
},
{
"amend": 1,

View File

@@ -10,6 +10,7 @@
wn.provide("erpnext.selling");
wn.require("app/js/transaction.js");
wn.require("app/js/controllers/accounts.js");
erpnext.selling.SellingController = erpnext.TransactionController.extend({
onload: function() {
@@ -162,8 +163,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
var item = wn.model.get_doc(cdt, cdn);
if(item.item_code || item.barcode) {
if(!this.validate_company_and_party("customer")) {
item.item_code = null;
refresh_field("item_code", item.name, item.parentfield);
cur_frm.fields_dict[me.frm.cscript.fname].grid.grid_rows[item.idx - 1].remove();
} else {
return this.frm.call({
method: "selling.utils.get_item_details",

View File

@@ -86,26 +86,6 @@ class DocType(TransactionBase):
if (obj.doc.price_list_currency == default_currency and flt(obj.doc.plc_conversion_rate) != 1.00) or not obj.doc.plc_conversion_rate or (obj.doc.price_list_currency != default_currency and flt(obj.doc.plc_conversion_rate) == 1.00):
msgprint("Please Enter Appropriate Conversion Rate for Price List Currency to Base Currency (%s --> %s)" % (obj.doc.price_list_currency, default_currency), raise_exception = 1)
# Get Tax rate if account type is TAX
# =========================================================================
def get_rate(self, arg):
arg = eval(arg)
rate = webnotes.conn.sql("select account_type, tax_rate from `tabAccount` where name = '%s' and docstatus != 2" %(arg['account_head']), as_dict=1)
ret = {'rate' : 0}
if arg['charge_type'] == 'Actual' and rate[0]['account_type'] == 'Tax':
msgprint("You cannot select ACCOUNT HEAD of type TAX as your CHARGE TYPE is 'ACTUAL'")
ret = {
'account_head' : ''
}
elif rate[0]['account_type'] in ['Tax', 'Chargeable'] and not arg['charge_type'] == 'Actual':
ret = {
'rate' : rate and flt(rate[0]['tax_rate']) or 0
}
return ret
def get_item_list(self, obj, is_stopped=0):
"""get item list"""
@@ -123,12 +103,12 @@ class DocType(TransactionBase):
if flt(d.qty) > flt(d.delivered_qty):
reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty)
if obj.doc.doctype == "Delivery Note" and d.prevdoc_doctype == 'Sales Order':
if obj.doc.doctype == "Delivery Note" and d.against_sales_order:
# if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12.
# But in this case reserved qty should only be reduced by 10 and not 12
already_delivered_qty = self.get_already_delivered_qty(obj.doc.name,
d.prevdoc_docname, d.prevdoc_detail_docname)
d.against_sales_order, d.prevdoc_detail_docname)
so_qty, reserved_warehouse = self.get_so_qty_and_warehouse(d.prevdoc_detail_docname)
if already_delivered_qty + d.qty > so_qty:
@@ -140,7 +120,7 @@ class DocType(TransactionBase):
for p in getlist(obj.doclist, 'packing_details'):
if p.parent_detail_docname == d.name and p.parent_item == d.item_code:
# the packing details table's qty is already multiplied with parent's qty
il.append({
il.append(webnotes._dict({
'warehouse': p.warehouse,
'reserved_warehouse': reserved_warehouse,
'item_code': p.item_code,
@@ -150,9 +130,9 @@ class DocType(TransactionBase):
'batch_no': cstr(p.batch_no).strip(),
'serial_no': cstr(p.serial_no).strip(),
'name': d.name
})
}))
else:
il.append({
il.append(webnotes._dict({
'warehouse': d.warehouse,
'reserved_warehouse': reserved_warehouse,
'item_code': d.item_code,
@@ -162,13 +142,13 @@ class DocType(TransactionBase):
'batch_no': cstr(d.batch_no).strip(),
'serial_no': cstr(d.serial_no).strip(),
'name': d.name
})
}))
return il
def get_already_delivered_qty(self, dn, so, so_detail):
qty = webnotes.conn.sql("""select sum(qty) from `tabDelivery Note Item`
where prevdoc_detail_docname = %s and docstatus = 1
and prevdoc_doctype = 'Sales Order' and prevdoc_docname = %s
and against_sales_order = %s
and parent != %s""", (so_detail, so, dn))
return qty and flt(qty[0][0]) or 0.0
@@ -218,7 +198,6 @@ class DocType(TransactionBase):
pi.qty = flt(qty)
pi.actual_qty = bin and flt(bin['actual_qty']) or 0
pi.projected_qty = bin and flt(bin['projected_qty']) or 0
pi.prevdoc_doctype = line.prevdoc_doctype
if not pi.warehouse:
pi.warehouse = warehouse
if not pi.batch_no:
@@ -283,8 +262,8 @@ class DocType(TransactionBase):
def check_stop_sales_order(self,obj):
for d in getlist(obj.doclist,obj.fname):
ref_doc_name = ''
if d.fields.has_key('prevdoc_docname') and d.prevdoc_docname and d.prevdoc_doctype == 'Sales Order':
ref_doc_name = d.prevdoc_docname
if d.fields.has_key('against_sales_order') and d.against_sales_order:
ref_doc_name = d.against_sales_order
elif d.fields.has_key('sales_order') and d.sales_order and not d.delivery_note:
ref_doc_name = d.sales_order
if ref_doc_name:
@@ -311,24 +290,14 @@ class DocType(TransactionBase):
acc_head = webnotes.conn.sql("select name from `tabAccount` where company = '%s' and master_name = '%s'"%(obj.doc.company, obj.doc.customer))
if acc_head:
tot_outstanding = 0
dbcr = webnotes.conn.sql("select sum(debit), sum(credit) from `tabGL Entry` where account = '%s' and ifnull(is_cancelled, 'No')='No'" % acc_head[0][0])
dbcr = webnotes.conn.sql("""select sum(debit), sum(credit) from `tabGL Entry`
where account = %s""", acc_head[0][0])
if dbcr:
tot_outstanding = flt(dbcr[0][0])-flt(dbcr[0][1])
exact_outstanding = flt(tot_outstanding) + flt(grand_total)
get_obj('Account',acc_head[0][0]).check_credit_limit(acc_head[0][0], obj.doc.company, exact_outstanding)
def get_prevdoc_date(self, obj):
for d in getlist(obj.doclist, obj.fname):
if d.prevdoc_doctype and d.prevdoc_docname:
if d.prevdoc_doctype in ["Sales Invoice", "Delivery Note"]:
date_field = "posting_date"
else:
date_field = "transaction_date"
d.prevdoc_date = webnotes.conn.get_value(d.prevdoc_doctype,
d.prevdoc_docname, date_field)
def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
from controllers.queries import get_match_cond
@@ -336,7 +305,6 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
return webnotes.conn.sql("""select batch_no from `tabStock Ledger Entry` sle
where item_code = '%(item_code)s'
and warehouse = '%(warehouse)s'
and ifnull(is_cancelled, 'No') = 'No'
and batch_no like '%(txt)s'
and exists(select * from `tabBatch`
where name = sle.batch_no

View File

@@ -12,6 +12,7 @@ cur_frm.cscript.sales_team_fname = "sales_team";
wn.require('app/selling/doctype/sales_common/sales_common.js');
wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');
wn.require('app/accounts/doctype/sales_invoice/pos.js');
erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
refresh: function(doc, dt, dn) {
@@ -26,34 +27,34 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
cur_frm.dashboard.add_progress(cint(doc.per_billed) + wn._("% Billed"),
doc.per_billed);
cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
cur_frm.add_custom_button(wn._('Send SMS'), cur_frm.cscript.send_sms);
// delivery note
if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales')
cur_frm.add_custom_button('Make Delivery', this.make_delivery_note);
cur_frm.add_custom_button(wn._('Make Delivery'), this.make_delivery_note);
// maintenance
if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) {
cur_frm.add_custom_button('Make Maint. Visit', this.make_maintenance_visit);
cur_frm.add_custom_button('Make Maint. Schedule',
cur_frm.add_custom_button(wn._('Make Maint. Visit'), this.make_maintenance_visit);
cur_frm.add_custom_button(wn._('Make Maint. Schedule'),
this.make_maintenance_schedule);
}
// indent
if(!doc.order_type || (doc.order_type == 'Sales'))
cur_frm.add_custom_button('Make ' + wn._('Material Request'),
cur_frm.add_custom_button(wn._('Make ') + wn._('Material Request'),
this.make_material_request);
// sales invoice
if(flt(doc.per_billed, 2) < 100)
cur_frm.add_custom_button('Make Invoice', this.make_sales_invoice);
cur_frm.add_custom_button(wn._('Make Invoice'), this.make_sales_invoice);
// stop
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Sales Order']);
cur_frm.add_custom_button(wn._('Stop!'), cur_frm.cscript['Stop Sales Order']);
} else {
// un-stop
cur_frm.dashboard.set_headline_alert("Stopped", "alert-danger", "icon-stop");
cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Sales Order']);
cur_frm.dashboard.set_headline_alert(wn._("Stopped"), "alert-danger", "icon-stop");
cur_frm.add_custom_button(wn._('Unstop'), cur_frm.cscript['Unstop Sales Order']);
}
}
@@ -65,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
@@ -157,7 +158,7 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, cdt, cdn) {
cur_frm.cscript['Stop Sales Order'] = function() {
var doc = cur_frm.doc;
var check = confirm("Are you sure you want to STOP " + doc.name);
var check = confirm(wn._("Are you sure you want to STOP ") + doc.name);
if (check) {
return $c('runserverobj', {
@@ -172,7 +173,7 @@ cur_frm.cscript['Stop Sales Order'] = function() {
cur_frm.cscript['Unstop Sales Order'] = function() {
var doc = cur_frm.doc;
var check = confirm("Are you sure you want to UNSTOP " + doc.name);
var check = confirm(wn._("Are you sure you want to UNSTOP ") + doc.name);
if (check) {
return $c('runserverobj', {
@@ -188,4 +189,4 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
if(cint(wn.boot.notification_settings.sales_order)) {
cur_frm.email_doc(wn.boot.notification_settings.sales_order_message);
}
};
};

View File

@@ -38,9 +38,6 @@ class DocType(SellingController):
def get_available_qty(self,args):
return get_obj('Sales Common').get_available_qty(eval(args))
def get_rate(self,arg):
return get_obj('Sales Common').get_rate(arg)
def validate_mandatory(self):
# validate transaction date v/s delivery date
if self.doc.delivery_date:
@@ -126,7 +123,7 @@ class DocType(SellingController):
self.validate_po()
self.validate_uom_is_integer("stock_uom", "qty")
self.validate_for_items()
self.validate_warehouse_user()
self.validate_warehouse()
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_active_sales_items(self)
sales_com_obj.check_conversion_rate(self)
@@ -147,14 +144,15 @@ class DocType(SellingController):
if not self.doc.delivery_status: self.doc.delivery_status = 'Not Delivered'
def validate_warehouse_user(self):
from stock.utils import validate_warehouse_user
def validate_warehouse(self):
from stock.utils import validate_warehouse_user, validate_warehouse_company
warehouses = list(set([d.reserved_warehouse for d in
self.doclist.get({"doctype": self.tname}) if d.reserved_warehouse]))
for w in warehouses:
validate_warehouse_user(w)
validate_warehouse_company(w, self.doc.company)
def validate_with_previous_doc(self):
super(DocType, self).validate_with_previous_doc(self.tname, {
@@ -165,36 +163,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)
@@ -218,7 +200,7 @@ class DocType(SellingController):
def check_nextdoc_docstatus(self):
# Checks Delivery Note
submit_dn = webnotes.conn.sql("select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2 where t1.name = t2.parent and t2.prevdoc_docname = '%s' and t1.docstatus = 1" % (self.doc.name))
submit_dn = webnotes.conn.sql("select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2 where t1.name = t2.parent and t2.against_sales_order = %s and t1.docstatus = 1", self.doc.name)
if submit_dn:
msgprint("Delivery Note : " + cstr(submit_dn[0][0]) + " has been submitted against " + cstr(self.doc.doctype) + ". Please cancel Delivery Note : " + cstr(submit_dn[0][0]) + " first and then cancel "+ cstr(self.doc.doctype), raise_exception = 1)
@@ -266,17 +248,19 @@ class DocType(SellingController):
def update_stock_ledger(self, update_stock, is_stopped = 0):
from stock.utils import update_bin
for d in self.get_item_list(is_stopped):
if webnotes.conn.get_value("Item", d['item_code'], "is_stock_item") == "Yes":
args = {
"item_code": d['item_code'],
"warehouse": d['reserved_warehouse'],
"reserved_qty": flt(update_stock) * flt(d['reserved_qty']),
"posting_date": self.doc.transaction_date,
"voucher_type": self.doc.doctype,
"voucher_no": self.doc.name,
"is_amended": self.doc.amended_from and 'Yes' or 'No'
}
get_obj('Warehouse', d['reserved_warehouse']).update_bin(args)
update_bin(args)
def get_item_list(self, is_stopped):
@@ -339,8 +323,7 @@ def make_delivery_note(source_name, target_doclist=None):
"field_map": {
"export_rate": "export_rate",
"name": "prevdoc_detail_docname",
"parent": "prevdoc_docname",
"parenttype": "prevdoc_doctype",
"parent": "against_sales_order",
"reserved_warehouse": "warehouse"
},
"postprocess": update_item,

View File

@@ -2,12 +2,13 @@
{
"creation": "2013-06-18 12:39:59",
"docstatus": 0,
"modified": "2013-08-09 14:46:17",
"modified": "2013-10-02 14:24:37",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
"allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"document_type": "Transaction",

View File

@@ -74,8 +74,7 @@ class TestSalesOrder(unittest.TestCase):
from stock.doctype.delivery_note.test_delivery_note import test_records as dn_test_records
dn = webnotes.bean(webnotes.copy_doclist(dn_test_records[0]))
dn.doclist[1].item_code = so.doclist[1].item_code
dn.doclist[1].prevdoc_doctype = "Sales Order"
dn.doclist[1].prevdoc_docname = so.doc.name
dn.doclist[1].against_sales_order = so.doc.name
dn.doclist[1].prevdoc_detail_docname = so.doclist[1].name
if delivered_qty:
dn.doclist[1].qty = delivered_qty

View File

@@ -41,7 +41,7 @@ class DocType:
for d in rec:
rec_list += d[0] + ' - ' + d[1] + '\n'
self.doc.receiver_list = rec_list
webnotes.errprint(rec_list)
def get_receiver_nos(self):
receiver_nos = []
for d in self.doc.receiver_list.split('\n'):

View File

View File

@@ -0,0 +1,3 @@
.funnel-wrapper {
margin: 15px;
}

View File

@@ -0,0 +1,189 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
// License: GNU General Public License v3. See license.txt
wn.pages['sales-funnel'].onload = function(wrapper) {
wn.ui.make_app_page({
parent: wrapper,
title: 'Sales Funnel',
single_column: true
});
wrapper.crm_funnel = new erpnext.CRMFunnel(wrapper);
wrapper.appframe.add_module_icon("Selling", "sales-funnel", function() {
wn.set_route("selling-home");
});
}
erpnext.CRMFunnel = Class.extend({
init: function(wrapper) {
var me = this;
// 0 setTimeout hack - this gives time for canvas to get width and height
setTimeout(function() {
me.setup(wrapper);
me.get_data();
}, 0);
},
setup: function(wrapper) {
var me = this;
this.elements = {
layout: $(wrapper).find(".layout-main"),
from_date: wrapper.appframe.add_date("From Date"),
to_date: wrapper.appframe.add_date("To Date"),
refresh_btn: wrapper.appframe.add_button("Refresh",
function() { me.get_data(); }, "icon-refresh"),
};
this.elements.no_data = $('<div class="alert alert-warning">No Data</div>')
.toggle(false)
.appendTo(this.elements.layout);
this.elements.funnel_wrapper = $('<div class="funnel-wrapper text-center"></div>')
.appendTo(this.elements.layout);
this.options = {
from_date: wn.datetime.add_months(wn.datetime.get_today(), -1),
to_date: wn.datetime.get_today()
};
// set defaults and bind on change
$.each(this.options, function(k, v) {
me.elements[k].val(wn.datetime.str_to_user(v));
me.elements[k].on("change", function() {
me.options[k] = wn.datetime.user_to_str($(this).val());
me.get_data();
});
});
// bind refresh
this.elements.refresh_btn.on("click", function() {
me.get_data(this);
});
// bind resize
$(window).resize(function() {
me.render();
});
},
get_data: function(btn) {
var me = this;
wn.call({
module: "selling",
page: "crm_funnel",
method: "get_funnel_data",
args: {
from_date: this.options.from_date,
to_date: this.options.to_date
},
btn: btn,
callback: function(r) {
if(!r.exc) {
me.options.data = r.message;
me.render();
}
}
});
},
render: function() {
var me = this;
this.prepare();
var context = this.elements.context,
x_start = 0.0,
x_end = this.options.width,
x_mid = (x_end - x_start) / 2.0,
y = 0,
y_old = 0.0;
if(this.options.total_value === 0) {
this.elements.no_data.toggle(true);
return;
}
this.options.data.forEach(function(d) {
context.fillStyle = d.color;
context.strokeStyle = d.color;
me.draw_triangle(x_start, x_mid, x_end, y, me.options.height);
y_old = y;
// new y
y = y + d.height;
// new x
var half_side = (me.options.height - y) / Math.sqrt(3);
x_start = x_mid - half_side;
x_end = x_mid + half_side;
var y_mid = y_old + (y - y_old) / 2.0;
me.draw_legend(x_mid, y_mid, me.options.width, me.options.height, d.value + " - " + d.title);
});
},
prepare: function() {
var me = this;
this.elements.no_data.toggle(false);
// calculate width and height options
this.options.width = $(this.elements.funnel_wrapper).width() * 2.0 / 3.0;
this.options.height = (Math.sqrt(3) * this.options.width) / 2.0;
// calculate total weightage
// as height decreases, area decreases by the square of the reduction
// hence, compensating by squaring the index value
this.options.total_weightage = this.options.data.reduce(
function(prev, curr, i) { return prev + Math.pow(i+1, 2) * curr.value; }, 0.0);
// calculate height for each data
$.each(this.options.data, function(i, d) {
d.height = me.options.height * d.value * Math.pow(i+1, 2) / me.options.total_weightage;
});
this.elements.canvas = $('<canvas></canvas>')
.appendTo(this.elements.funnel_wrapper.empty())
.attr("width", $(this.elements.funnel_wrapper).width())
.attr("height", this.options.height);
this.elements.context = this.elements.canvas.get(0).getContext("2d");
},
draw_triangle: function(x_start, x_mid, x_end, y, height) {
var context = this.elements.context;
context.beginPath();
context.moveTo(x_start, y);
context.lineTo(x_end, y);
context.lineTo(x_mid, height);
context.lineTo(x_start, y);
context.closePath();
context.fill();
},
draw_legend: function(x_mid, y_mid, width, height, title) {
var context = this.elements.context;
// draw line
context.beginPath();
context.moveTo(x_mid, y_mid);
context.lineTo(width, y_mid);
context.closePath();
context.stroke();
// draw circle
context.beginPath();
context.arc(width, y_mid, 5, 0, Math.PI * 2, false);
context.closePath();
context.fill();
// draw text
context.fillStyle = "black";
context.textBaseline = "middle";
context.font = "1.1em sans-serif";
context.fillText(title, width + 20, y_mid);
}
});

View File

@@ -0,0 +1,33 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import webnotes
@webnotes.whitelist()
def get_funnel_data(from_date, to_date):
active_leads = webnotes.conn.sql("""select count(*) from `tabLead`
where (`modified` between %s and %s)
and status != "Do Not Contact" """, (from_date, to_date))[0][0]
active_leads += webnotes.conn.sql("""select count(distinct customer) from `tabContact`
where (`modified` between %s and %s)
and status != "Passive" """, (from_date, to_date))[0][0]
opportunities = webnotes.conn.sql("""select count(*) from `tabOpportunity`
where docstatus = 1 and (`modified` between %s and %s)
and status != "Lost" """, (from_date, to_date))[0][0]
quotations = webnotes.conn.sql("""select count(*) from `tabQuotation`
where docstatus = 1 and (`modified` between %s and %s)
and status != "Lost" """, (from_date, to_date))[0][0]
sales_orders = webnotes.conn.sql("""select count(*) from `tabQuotation`
where docstatus = 1 and (`modified` between %s and %s)""", (from_date, to_date))[0][0]
return [
{ "title": "Active Leads / Customers", "value": active_leads, "color": "#B03B46" },
{ "title": "Opportunities", "value": opportunities, "color": "#F09C00" },
{ "title": "Quotations", "value": quotations, "color": "#006685" },
{ "title": "Sales Orders", "value": sales_orders, "color": "#00AD65" }
]

View File

@@ -0,0 +1,33 @@
[
{
"creation": "2013-10-04 13:17:18",
"docstatus": 0,
"modified": "2013-10-04 13:17:18",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Page",
"icon": "icon-filter",
"module": "Selling",
"name": "__common__",
"page_name": "sales-funnel",
"standard": "Yes",
"title": "Sales Funnel"
},
{
"doctype": "Page Role",
"name": "__common__",
"parent": "sales-funnel",
"parentfield": "roles",
"parenttype": "Page",
"role": "Sales Manager"
},
{
"doctype": "Page",
"name": "sales-funnel"
},
{
"doctype": "Page Role"
}
]

View File

@@ -155,6 +155,10 @@ wn.module_page["Selling"] = [
"label":wn._("Sales Analytics"),
page: "sales-analytics"
},
{
"label":wn._("Sales Funnel"),
page: "sales-funnel"
},
]
},
{

View File

@@ -34,6 +34,7 @@ def get_item_details(args):
"plc_conversion_rate": 1.0
}
"""
if isinstance(args, basestring):
args = json.loads(args)
args = webnotes._dict(args)
@@ -73,7 +74,7 @@ def get_item_details(args):
out.update(apply_pos_settings(pos_settings, out))
if args.doctype in ("Sales Invoice", "Delivery Note"):
if item_bean.doc.has_serial_no and not args.serial_no:
if item_bean.doc.has_serial_no == "Yes" and not args.serial_no:
out.serial_no = _get_serial_nos_by_fifo(args, item_bean)
return out

View File

@@ -12,8 +12,8 @@ class WebsitePriceListMissingError(webnotes.ValidationError): pass
def set_cart_count(quotation=None):
if not quotation:
quotation = _get_cart_quotation()
webnotes.add_cookies["cart_count"] = cstr(len(quotation.doclist.get(
{"parentfield": "quotation_details"})) or "")
cart_count = cstr(len(quotation.doclist.get({"parentfield": "quotation_details"})))
webnotes._response.set_cookie("cart_count", cart_count)
@webnotes.whitelist()
def get_cart_quotation(doclist=None):
@@ -47,7 +47,7 @@ def place_order():
sales_order.ignore_permissions = True
sales_order.insert()
sales_order.submit()
webnotes.add_cookies["cart_count"] = ""
webnotes._response.set_cookie("cart_count", "")
return sales_order.doc.name
@@ -286,7 +286,7 @@ def apply_cart_settings(party=None, quotation=None):
cart_settings = webnotes.get_obj("Shopping Cart Settings")
billing_territory = get_address_territory(quotation.doc.customer_address) or \
party.territory
party.territory or "All Territories"
set_price_list_and_rate(quotation, cart_settings, billing_territory)

View File

@@ -27,7 +27,7 @@ def get_product_info(item_code):
else:
in_stock = -1
price = price_list and webnotes.conn.sql("""select ip.ref_rate, pl.ref_currency from
price = price_list and webnotes.conn.sql("""select ip.ref_rate, pl.currency from
`tabItem Price` ip, `tabPrice List` pl where ip.parent = pl.name and
ip.item_code=%s and ip.parent=%s""",
(item_code, price_list), as_dict=1) or []
@@ -36,10 +36,10 @@ def get_product_info(item_code):
qty = 0
if price:
price["formatted_price"] = fmt_money(price["ref_rate"], currency=price["ref_currency"])
price["formatted_price"] = fmt_money(price["ref_rate"], currency=price["currency"])
price["ref_currency"] = not cint(webnotes.conn.get_default("hide_currency_symbol")) \
and (webnotes.conn.get_value("Currency", price.ref_currency, "symbol") or price.ref_currency) \
price["currency"] = not cint(webnotes.conn.get_default("hide_currency_symbol")) \
and (webnotes.conn.get_value("Currency", price.currency, "symbol") or price.currency) \
or ""
if webnotes.session.user != "Guest":