Hi all,
I am able to get the Opportunity document in Request for Quotation document by this way.
but unable to covert the Opportunity document into Request for Quotation document by this way.
here is my code, anyone please help me
python file
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.provide(“erpnext.crm”);
cur_frm.email_field = “contact_email”;
frappe.ui.form.on(“Opportunity”, {
setup: function(frm) {
frm.custom_make_buttons = {
‘Quotation’: ‘Quotation’,
‘Supplier Quotation’: ‘Supplier Quotation’,
‘Request for Quotation’: ‘Request for Quotation’
}
},
customer: function(frm) {
frm.trigger(‘set_contact_link’);
erpnext.utils.get_party_details(frm);
},
lead: function(frm) {
frm.trigger('set_contact_link');
},
customer_address: function(frm, cdt, cdn) {
erpnext.utils.get_address_display(frm, 'customer_address', 'address_display', false);
},
contact_person: erpnext.utils.get_contact_details,
enquiry_from: function(frm) {
frm.toggle_reqd("lead", frm.doc.enquiry_from==="Lead");
frm.toggle_reqd("customer", frm.doc.enquiry_from==="Customer");
},
refresh: function(frm) {
console.log(frm);
var doc = frm.doc;
frm.events.enquiry_from(frm);
frm.trigger('set_contact_link');
erpnext.toggle_naming_series();
if(!doc.__islocal && doc.status!=="Lost") {
if(doc.with_items){
frm.add_custom_button(__('Supplier Quotation'),
function() {
frm.trigger("make_supplier_quotation")
}, __("Make"));
frm.add_custom_button(__('Request for Quotation'),
function() {
frm.trigger("make_request_for_quotation")
}, __("Make"));
}
frm.add_custom_button(__('Quotation'),
cur_frm.cscript.create_quotation, __("Make"));
frm.page.set_inner_btn_group_as_primary(__("Make"));
if(doc.status!=="Quotation") {
frm.add_custom_button(__('Lost'),
cur_frm.cscript['Declare Opportunity Lost']);
}
}
if(!frm.doc.__islocal && frm.perm[0].write && frm.doc.docstatus==0) {
if(frm.doc.status==="Open") {
frm.add_custom_button(__("Close"), function() {
frm.set_value("status", "Closed");
frm.save();
});
} else {
frm.add_custom_button(__("Reopen"), function() {
frm.set_value("status", "Open");
frm.save();
});
}
}
},
set_contact_link: function(frm) {
if(frm.doc.customer) {
frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'}
} else if(frm.doc.lead) {
frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'}
}
},
make_supplier_quotation: function(frm) {
console.log("make_supplier_quotation");
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.opportunity.opportunity.make_supplier_quotation",
frm: cur_frm
})
}
make_request_for_quotation: function(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.opportunity.opportunity.make_request_for_quotation",
frm: cur_frm
})
}
})
// TODO commonify this code
erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
onload: function() {
console.log(“onload”);
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(this.frm.doc.doctype, this.frm.doc.name, { status:'Open' });
if(!this.frm.doc.company && frappe.defaults.get_user_default("Company"))
set_multiple(this.frm.doc.doctype, this.frm.doc.name,
{ company:frappe.defaults.get_user_default("Company") });
this.setup_queries();
},
setup_queries: function() {
console.log("setup_queries");
var me = this;
console.log("me",me);
if(this.frm.fields_dict.contact_by.df.options.match(/^User/)) {
this.frm.set_query("contact_by", erpnext.queries.user);
}
me.frm.set_query('customer_address', erpnext.queries.address_query);
this.frm.set_query("item_code", "items", function() {
return {
query: "erpnext.controllers.queries.item_query",
filters: {'is_sales_item': 1}
};
});
$.each([["lead", "lead"],
["customer", "customer"],
["contact_person", "contact_query"]],
function(i, opts) {
me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
});
},
create_quotation: function() {
console.log("create_quotation");
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.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];
console.log(“d”,d);
if (d.item_code) {
return frappe.call({
method: “erpnext.crm.doctype.opportunity.opportunity.get_item_details”,
args: {“item_code”:d.item_code},
callback: function(r, rt) {
if(r.message) {
$.each(r.message, function(k, v) {
frappe.model.set_value(cdt, cdn, k, v);
});
refresh_field(‘image_view’, d.name, ‘items’);
}
}
})
}
}
cur_frm.cscript.lead = function(doc, cdt, cdn) {
cur_frm.toggle_display(“contact_info”, doc.customer || doc.lead);
erpnext.utils.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() {
var args = dialog.get_values();
console.log("args",args);
if(!args) return;
return cur_frm.call({
doc: cur_frm.doc,
method: "declare_enquiry_lost",
args: args.reason,
callback: function(r) {
if(r.exc) {
frappe.msgprint(__("There were errors."));
} else {
dialog.hide();
cur_frm.refresh();
}
},
btn: this
})
});
dialog.show();
}
Javascript file
Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
License: GNU General Public License v3. See license.txt
from future import unicode_literals
import frappe, json
from frappe.utils import cstr, cint, get_fullname
from frappe import msgprint, _
from frappe.model.mapper import get_mapped_doc
from erpnext.setup.utils import get_exchange_rate
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import get_party_account_currency
subject_field = “title”
sender_field = “contact_email”
class Opportunity(TransactionBase):
def after_insert(self):
if self.lead:
frappe.get_doc(“Lead”, self.lead).set_status(update=True)
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,
})
self.make_new_lead_if_required()
if not self.enquiry_from:
frappe.throw(_("Opportunity From field is mandatory"))
self.validate_item_details()
self.validate_uom_is_integer("uom", "qty")
self.validate_lead_cust()
self.validate_cust_name()
if not self.title:
self.title = self.customer_name
if not self.with_items:
self.items = []
def make_new_lead_if_required(self):
"""Set lead against new opportunity"""
if not (self.lead or self.customer) and self.contact_email:
lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email})
if not lead_name:
sender_name = get_fullname(self.contact_email)
if sender_name == self.contact_email:
sender_name = None
if not sender_name and ('@' in self.contact_email):
email_name = self.contact_email.split('@')[0]
email_split = email_name.split('.')
sender_name = ''
for s in email_split:
sender_name += s.capitalize() + ' '
lead = frappe.get_doc({
"doctype": "Lead",
"email_id": self.contact_email,
"lead_name": sender_name or 'Unknown'
})
lead.flags.ignore_email_validation = True
lead.insert(ignore_permissions=True)
lead_name = lead.name
self.enquiry_from = "Lead"
self.lead = lead_name
def declare_enquiry_lost(self,arg):
if not self.has_active_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_active_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 not in ('Lost', 'Closed')""", self.name)
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 has_lost_quotation(self):
lost_quotation = 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 = 'Lost'
""", self.name)
if lost_quotation:
if self.has_active_quotation():
return False
return True
def validate_cust_name(self):
if self.customer:
self.customer_name = frappe.db.get_value("Customer", self.customer, "customer_name")
elif self.lead:
lead_name, company_name = frappe.db.get_value("Lead", self.lead, ["lead_name", "company_name"])
self.customer_name = company_name or lead_name
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'):
return
# set missing values
item_fields = ("item_name", "description", "item_group", "brand")
for d in self.items:
if not d.item_code:
continue
item = frappe.db.get_value("Item", d.item_code, item_fields, as_dict=True)
for key in item_fields:
if not d.get(key): d.set(key, item.get(key))
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 get_item_details(item_code):
item = frappe.db.sql(“”“select item_name, stock_uom, image, description, item_group, brand
from tabItem
where name = %s”“”, item_code, as_dict=1)
return {
‘item_name’: item and item[0][‘item_name’] or ‘’,
‘uom’: item and item[0][‘stock_uom’] or ‘’,
‘description’: item and item[0][‘description’] or ‘’,
‘image’: item and item[0][‘image’] or ‘’,
‘item_group’: item and item[0][‘item_group’] or ‘’,
‘brand’: item and item[0][‘brand’] or ‘’
}
@frappe.whitelist()
def make_request_for_quotation(source_name, target_doc=None):
doclist = get_mapped_doc(“Opportunity”, source_name, {
“Opportunity”: {
“doctype”: “Request for Quotation”,
“validation”: {
“enquiry_type”: [“=”, “Sales”]
}
},
“Opportunity Item”: {
“doctype”: “Request for Quotation Item”,
“field_map”: [
[“name”, “opportunity_item”],
[“parent”, “opportunity”],
[“uom”, “uom”]
]
}
}, target_doc)
return doclist
@frappe.whitelist()
def make_quotation(source_name, target_doc=None):
def set_missing_values(source, target):
from erpnext.controllers.accounts_controller import get_default_taxes_and_charges
quotation = frappe.get_doc(target)
company_currency = frappe.db.get_value("Company", quotation.company, "default_currency")
party_account_currency = get_party_account_currency("Customer", quotation.customer,
quotation.company) if quotation.customer else company_currency
quotation.currency = party_account_currency or company_currency
if company_currency == quotation.currency:
exchange_rate = 1
else:
exchange_rate = get_exchange_rate(quotation.currency, company_currency,
quotation.transaction_date)
quotation.conversion_rate = exchange_rate
# get default taxes
taxes = get_default_taxes_and_charges("Sales Taxes and Charges Template")
if taxes:
quotation.extend("taxes", taxes)
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",
}
},
"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
@frappe.whitelist()
def make_supplier_quotation(source_name, target_doc=None):
frappe.msgprint(“python”);
doclist = get_mapped_doc(“Opportunity”, source_name, {
“Opportunity”: {
“doctype”: “Supplier Quotation”,
“field_map”: {
“name”: “opportunity”
}
},
“Opportunity Item”: {
“doctype”: “Supplier Quotation Item”,
“field_map”: {
“uom”: “stock_uom”
}
}
}, target_doc)
return doclist
@frappe.whitelist()
def set_multiple_status(names, status):
names = json.loads(names)
for name in names:
opp = frappe.get_doc(“Opportunity”, name)
opp.status = status
opp.save()
def auto_close_opportunity():
“”" auto close the Replied
Opportunities after 7 days “”"
auto_close_after_days = frappe.db.get_value(“Support Settings”, “Support Settings”, “close_opportunity_after_days”) or 15
opportunities = frappe.db.sql(""" select name from tabOpportunity where status='Replied' and
modified<DATE_SUB(CURDATE(), INTERVAL %s DAY) """, (auto_close_after_days), as_dict=True)
for opportunity in opportunities:
doc = frappe.get_doc("Opportunity", opportunity.get("name"))
doc.status = "Closed"
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
doc.save()