Unable to make Request for Quotation from Opportunity

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 © 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 © 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()

@IT_Department very hard for someone to debug customizations with such a large code dump.

Try and debug it to a specific point, then someone might be able to help.

Noted @rmheta .

Here is my code, please help me

Opportunity.py
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”));
}
}

make_request_for_quotation: function(frm) {
frappe.model.open_mapped_doc({
method: “erpnext.crm.doctype.opportunity.opportunity.make_request_for_quotation”,
frm: cur_frm
})
}
})

Request for Quotation.js
@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