fix: RFQ UX fixes (#23382)

* chore: RFQ UX fixes

* fix: RFQ to SUpplier Quotation Dialog

* fix: Remove 'No Quote' functionality

- No Quote in Suppliers table is removed
- It's use everywhere has been removed too (tests, server side files, DOM event)

* chore: More Info section and Project field

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
This commit is contained in:
Marica 2020-10-23 19:38:30 +05:30 committed by GitHub
parent c4be397954
commit 0bd576aa44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 235 additions and 268 deletions

View File

@ -29,14 +29,12 @@ frappe.ui.form.on("Request for Quotation",{
refresh: function(frm, cdt, cdn) {
if (frm.doc.docstatus === 1) {
frm.add_custom_button(__('Create'),
function(){ frm.trigger("make_suppplier_quotation") }, __("Supplier Quotation"));
frm.add_custom_button(__("View"),
function(){ frappe.set_route('List', 'Supplier Quotation',
{'request_for_quotation': frm.doc.name}) }, __("Supplier Quotation"));
frm.add_custom_button(__('Supplier Quotation'),
function(){ frm.trigger("make_suppplier_quotation") }, __("Create"));
frm.add_custom_button(__("Send Supplier Emails"), function() {
frm.add_custom_button(__("Send Emails to Suppliers"), function() {
frappe.call({
method: 'erpnext.buying.doctype.request_for_quotation.request_for_quotation.send_supplier_emails',
freeze: true,
@ -47,134 +45,64 @@ frappe.ui.form.on("Request for Quotation",{
frm.reload_doc();
}
});
});
}
}, __("Tools"));
},
get_suppliers_button: function (frm) {
var doc = frm.doc;
var dialog = new frappe.ui.Dialog({
title: __("Get Suppliers"),
fields: [
{
"fieldtype": "Select", "label": __("Get Suppliers By"),
"fieldname": "search_type",
"options": ["Tag","Supplier Group"],
"reqd": 1,
onchange() {
if(dialog.get_value('search_type') == 'Tag'){
frappe.call({
method: 'erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_supplier_tag',
}).then(r => {
dialog.set_df_property("tag", "options", r.message)
});
}
}
},
{
"fieldtype": "Link", "label": __("Supplier Group"),
"fieldname": "supplier_group",
"options": "Supplier Group",
"reqd": 0,
"depends_on": "eval:doc.search_type == 'Supplier Group'"
},
{
"fieldtype": "Select", "label": __("Tag"),
"fieldname": "tag",
"reqd": 0,
"depends_on": "eval:doc.search_type == 'Tag'",
},
{
"fieldtype": "Button", "label": __("Add All Suppliers"),
"fieldname": "add_suppliers"
},
frm.add_custom_button(__('Download PDF'), () => {
var suppliers = [];
const fields = [{
fieldtype: 'Link',
label: __('Select a Supplier'),
fieldname: 'supplier',
options: 'Supplier',
reqd: 1,
get_query: () => {
return {
filters: [
["Supplier", "name", "in", frm.doc.suppliers.map((row) => {return row.supplier;})]
]
});
}
}
}];
dialog.fields_dict.add_suppliers.$input.click(function() {
var args = dialog.get_values();
if(!args) return;
dialog.hide();
frappe.prompt(fields, data => {
var child = locals[cdt][cdn]
//Remove blanks
for (var j = 0; j < frm.doc.suppliers.length; j++) {
if(!frm.doc.suppliers[j].hasOwnProperty("supplier")) {
frm.get_field("suppliers").grid.grid_rows[j].remove();
var w = window.open(
frappe.urllib.get_full_url("/api/method/erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_pdf?"
+"doctype="+encodeURIComponent(frm.doc.doctype)
+"&name="+encodeURIComponent(frm.doc.name)
+"&supplier="+encodeURIComponent(data.supplier)
+"&no_letterhead=0"));
if(!w) {
frappe.msgprint(__("Please enable pop-ups")); return;
}
}
function load_suppliers(r) {
if(r.message) {
for (var i = 0; i < r.message.length; i++) {
var exists = false;
if (r.message[i].constructor === Array){
var supplier = r.message[i][0];
} else {
var supplier = r.message[i].name;
}
for (var j = 0; j < doc.suppliers.length;j++) {
if (supplier === doc.suppliers[j].supplier) {
exists = true;
}
}
if(!exists) {
var d = frm.add_child('suppliers');
d.supplier = supplier;
frm.script_manager.trigger("supplier", d.doctype, d.name);
}
}
}
frm.refresh_field("suppliers");
}
if (args.search_type === "Tag" && args.tag) {
return frappe.call({
type: "GET",
method: "frappe.desk.doctype.tag.tag.get_tagged_docs",
args: {
"doctype": "Supplier",
"tag": args.tag
},
callback: load_suppliers
});
} else if (args.supplier_group) {
return frappe.call({
method: "frappe.client.get_list",
args: {
doctype: "Supplier",
order_by: "name",
fields: ["name"],
filters: [["Supplier", "supplier_group", "=", args.supplier_group]]
'Download PDF for Supplier',
'Download');
},
__("Tools"));
frm.page.set_inner_btn_group_as_primary(__('Create'));
}
},
callback: load_suppliers
});
}
});
dialog.show();
},
make_suppplier_quotation: function(frm) {
var doc = frm.doc;
var dialog = new frappe.ui.Dialog({
title: __("For Supplier"),
title: __("Create Supplier Quotation"),
fields: [
{ "fieldtype": "Select", "label": __("Supplier"),
"fieldname": "supplier",
"options": doc.suppliers.map(d => d.supplier),
"reqd": 1,
"default": doc.suppliers.length === 1 ? doc.suppliers[0].supplier_name : "" },
{ "fieldtype": "Button", "label": __('Create Supplier Quotation'),
"fieldname": "make_supplier_quotation", "cssClass": "btn-primary" },
]
});
dialog.fields_dict.make_supplier_quotation.$input.click(function() {
var args = dialog.get_values();
],
primary_action_label: __("Create"),
primary_action: (args) => {
if(!args) return;
dialog.hide();
return frappe.call({
type: "GET",
method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.make_supplier_quotation_from_rfq",
@ -190,7 +118,9 @@ frappe.ui.form.on("Request for Quotation",{
}
}
});
}
});
dialog.show()
},
@ -273,42 +203,6 @@ frappe.ui.form.on("Request for Quotation Supplier",{
})
},
download_pdf: function(frm, cdt, cdn) {
var child = locals[cdt][cdn]
var w = window.open(
frappe.urllib.get_full_url("/api/method/erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_pdf?"
+"doctype="+encodeURIComponent(frm.doc.doctype)
+"&name="+encodeURIComponent(frm.doc.name)
+"&supplier_idx="+encodeURIComponent(child.idx)
+"&no_letterhead=0"));
if(!w) {
frappe.msgprint(__("Please enable pop-ups")); return;
}
},
no_quote: function(frm, cdt, cdn) {
var d = locals[cdt][cdn];
if (d.no_quote) {
if (d.quote_status != __('Received')) {
frappe.model.set_value(cdt, cdn, 'quote_status', 'No Quote');
} else {
frappe.msgprint(__("Cannot set a received RFQ to No Quote"));
frappe.model.set_value(cdt, cdn, 'no_quote', 0);
}
} else {
d.quote_status = __('Pending');
frm.call({
method:"update_rfq_supplier_status",
doc: frm.doc,
args: {
sup_name: d.supplier
},
callback: function(r) {
frm.refresh_field("suppliers");
}
});
}
}
})
erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.extend({
@ -332,7 +226,8 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
per_ordered: ["<", 99.99]
}
})
}, __("Get items from"));
}, __("Get Items From"));
// Get items from Opportunity
this.frm.add_custom_button(__('Opportunity'),
function() {
@ -344,7 +239,8 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
company: me.frm.doc.company
},
})
}, __("Get items from"));
}, __("Get Items From"));
// Get items from open Material Requests based on supplier
this.frm.add_custom_button(__('Possible Supplier'), function() {
// Create a dialog window for the user to pick their supplier
@ -382,8 +278,13 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
}
}
d.show();
}, __("Get items from"));
}, __("Get Items From"));
// Get Suppliers
this.frm.add_custom_button(__('Get Suppliers'),
function() {
me.get_suppliers_button(me.frm);
});
}
},
@ -393,9 +294,108 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
tc_name: function() {
this.get_terms();
}
});
},
get_suppliers_button: function (frm) {
var doc = frm.doc;
var dialog = new frappe.ui.Dialog({
title: __("Get Suppliers"),
fields: [
{
"fieldtype": "Select", "label": __("Get Suppliers By"),
"fieldname": "search_type",
"options": ["Tag","Supplier Group"],
"reqd": 1,
onchange() {
if(dialog.get_value('search_type') == 'Tag'){
frappe.call({
method: 'erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_supplier_tag',
}).then(r => {
dialog.set_df_property("tag", "options", r.message)
});
}
}
},
{
"fieldtype": "Link", "label": __("Supplier Group"),
"fieldname": "supplier_group",
"options": "Supplier Group",
"reqd": 0,
"depends_on": "eval:doc.search_type == 'Supplier Group'"
},
{
"fieldtype": "Select", "label": __("Tag"),
"fieldname": "tag",
"reqd": 0,
"depends_on": "eval:doc.search_type == 'Tag'",
}
],
primary_action_label: __("Add Suppliers"),
primary_action : (args) => {
if(!args) return;
dialog.hide();
//Remove blanks
for (var j = 0; j < frm.doc.suppliers.length; j++) {
if(!frm.doc.suppliers[j].hasOwnProperty("supplier")) {
frm.get_field("suppliers").grid.grid_rows[j].remove();
}
}
function load_suppliers(r) {
if(r.message) {
for (var i = 0; i < r.message.length; i++) {
var exists = false;
if (r.message[i].constructor === Array){
var supplier = r.message[i][0];
} else {
var supplier = r.message[i].name;
}
for (var j = 0; j < doc.suppliers.length;j++) {
if (supplier === doc.suppliers[j].supplier) {
exists = true;
}
}
if(!exists) {
var d = frm.add_child('suppliers');
d.supplier = supplier;
frm.script_manager.trigger("supplier", d.doctype, d.name);
}
}
}
frm.refresh_field("suppliers");
}
if (args.search_type === "Tag" && args.tag) {
return frappe.call({
type: "GET",
method: "frappe.desk.doctype.tag.tag.get_tagged_docs",
args: {
"doctype": "Supplier",
"tag": args.tag
},
callback: load_suppliers
});
} else if (args.supplier_group) {
return frappe.call({
method: "frappe.client.get_list",
args: {
doctype: "Supplier",
order_by: "name",
fields: ["name"],
filters: [["Supplier", "supplier_group", "=", args.supplier_group]]
},
callback: load_suppliers
});
}
}
});
dialog.show();
},
});
// for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.buying.RequestforQuotationController({frm: cur_frm}));

View File

@ -12,9 +12,10 @@
"vendor",
"column_break1",
"transaction_date",
"status",
"amended_from",
"suppliers_section",
"suppliers",
"get_suppliers_button",
"items_section",
"items",
"link_to_mrs",
@ -31,11 +32,7 @@
"terms",
"printing_settings",
"select_print_heading",
"letter_head",
"more_info",
"status",
"column_break3",
"amended_from"
"letter_head"
],
"fields": [
{
@ -83,6 +80,7 @@
"width": "50%"
},
{
"default": "Today",
"fieldname": "transaction_date",
"fieldtype": "Date",
"in_list_view": 1,
@ -99,16 +97,11 @@
{
"fieldname": "suppliers",
"fieldtype": "Table",
"label": "Supplier Detail",
"label": "Suppliers",
"options": "Request for Quotation Supplier",
"print_hide": 1,
"reqd": 1
},
{
"fieldname": "get_suppliers_button",
"fieldtype": "Button",
"label": "Get Suppliers"
},
{
"fieldname": "items_section",
"fieldtype": "Section Break",
@ -144,6 +137,7 @@
"print_hide": 1
},
{
"allow_on_submit": 1,
"fetch_from": "email_template.response",
"fetch_if_empty": 1,
"fieldname": "message_for_supplier",
@ -206,14 +200,6 @@
"options": "Letter Head",
"print_hide": 1
},
{
"collapsible": 1,
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Information",
"oldfieldtype": "Section Break",
"options": "fa fa-file-text"
},
{
"fieldname": "status",
"fieldtype": "Select",
@ -227,10 +213,6 @@
"reqd": 1,
"search_index": 1
},
{
"fieldname": "column_break3",
"fieldtype": "Column Break"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
@ -275,9 +257,10 @@
}
],
"icon": "fa fa-shopping-cart",
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-10-01 14:54:50.888729",
"modified": "2020-10-16 17:49:09.561929",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation",

View File

@ -28,6 +28,10 @@ class RequestforQuotation(BuyingController):
super(RequestforQuotation, self).set_qty_as_per_stock_uom()
self.update_email_id()
if self.docstatus < 1:
# after amend and save, status still shows as cancelled, until submit
frappe.db.set(self, 'status', 'Draft')
def validate_duplicate_supplier(self):
supplier_list = [d.supplier for d in self.suppliers]
if len(supplier_list) != len(set(supplier_list)):
@ -82,7 +86,7 @@ class RequestforQuotation(BuyingController):
# make new user if required
update_password_link, contact = self.update_supplier_contact(rfq_supplier, self.get_link())
self.update_supplier_part_no(rfq_supplier)
self.update_supplier_part_no(rfq_supplier.supplier)
self.supplier_rfq_mail(rfq_supplier, update_password_link, self.get_link())
rfq_supplier.email_sent = 1
if not rfq_supplier.contact:
@ -93,11 +97,11 @@ class RequestforQuotation(BuyingController):
# RFQ link for supplier portal
return get_url("/rfq/" + self.name)
def update_supplier_part_no(self, args):
self.vendor = args.supplier
def update_supplier_part_no(self, supplier):
self.vendor = supplier
for item in self.items:
item.supplier_part_no = frappe.db.get_value('Item Supplier',
{'parent': item.item_code, 'supplier': args.supplier}, 'supplier_part_no')
{'parent': item.item_code, 'supplier': supplier}, 'supplier_part_no')
def update_supplier_contact(self, rfq_supplier, link):
'''Create a new user for the supplier if not set in contact'''
@ -197,7 +201,6 @@ class RequestforQuotation(BuyingController):
def update_rfq_supplier_status(self, sup_name=None):
for supplier in self.suppliers:
if sup_name == None or supplier.supplier == sup_name:
if supplier.quote_status != _('No Quote'):
quote_status = _('Received')
for item in self.items:
sqi_count = frappe.db.sql("""
@ -322,16 +325,15 @@ def create_rfq_items(sq_doc, supplier, data):
})
@frappe.whitelist()
def get_pdf(doctype, name, supplier_idx):
doc = get_rfq_doc(doctype, name, supplier_idx)
def get_pdf(doctype, name, supplier):
doc = get_rfq_doc(doctype, name, supplier)
if doc:
download_pdf(doctype, name, doc=doc)
def get_rfq_doc(doctype, name, supplier_idx):
if cint(supplier_idx):
def get_rfq_doc(doctype, name, supplier):
if supplier:
doc = frappe.get_doc(doctype, name)
args = doc.get('suppliers')[cint(supplier_idx) - 1]
doc.update_supplier_part_no(args)
doc.update_supplier_part_no(supplier)
return doc
@frappe.whitelist()

View File

@ -25,14 +25,10 @@ class TestRequestforQuotation(unittest.TestCase):
sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[0].supplier)
sq.submit()
# No Quote first supplier quotation
rfq.get('suppliers')[1].no_quote = 1
rfq.get('suppliers')[1].quote_status = 'No Quote'
rfq.update_rfq_supplier_status() #rfq.get('suppliers')[1].supplier)
self.assertEqual(rfq.get('suppliers')[0].quote_status, 'Received')
self.assertEqual(rfq.get('suppliers')[1].quote_status, 'No Quote')
self.assertEqual(rfq.get('suppliers')[1].quote_status, 'Pending')
def test_make_supplier_quotation(self):
rfq = make_request_for_quotation()

View File

@ -84,9 +84,6 @@ QUnit.test("Test: Request for Quotation", function (assert) {
cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view();
},
() => frappe.timeout(1),
() => {
frappe.click_check('No Quote');
},
() => frappe.timeout(1),
() => {
cur_frm.cur_grid.toggle_view();
@ -125,7 +122,6 @@ QUnit.test("Test: Request for Quotation", function (assert) {
() => frappe.timeout(1),
() => {
assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.quote_status == "Received");
assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[0].doc.no_quote == 1);
},
() => done()
]);

View File

@ -27,10 +27,11 @@
"stock_qty",
"warehouse_and_reference",
"warehouse",
"project_name",
"col_break4",
"material_request",
"material_request_item",
"section_break_24",
"project_name",
"section_break_23",
"page_break"
],
@ -161,7 +162,7 @@
{
"fieldname": "project_name",
"fieldtype": "Link",
"label": "Project Name",
"label": "Project",
"options": "Project",
"print_hide": 1
},
@ -249,11 +250,18 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "section_break_24",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-06-12 19:10:36.333441",
"modified": "2020-09-24 17:26:46.276934",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation Item",

View File

@ -9,19 +9,19 @@
"email_sent",
"supplier",
"contact",
"no_quote",
"quote_status",
"column_break_3",
"supplier_name",
"email_id",
"download_pdf"
"email_id"
],
"fields": [
{
"allow_on_submit": 1,
"columns": 2,
"default": "1",
"fieldname": "send_email",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Send Email"
},
{
@ -35,7 +35,7 @@
"read_only": 1
},
{
"columns": 4,
"columns": 2,
"fieldname": "supplier",
"fieldtype": "Link",
"in_list_view": 1,
@ -45,7 +45,7 @@
},
{
"allow_on_submit": 1,
"columns": 3,
"columns": 2,
"fieldname": "contact",
"fieldtype": "Link",
"in_list_view": 1,
@ -55,19 +55,11 @@
},
{
"allow_on_submit": 1,
"default": "0",
"depends_on": "eval:doc.docstatus >= 1 && doc.quote_status != 'Received'",
"fieldname": "no_quote",
"fieldtype": "Check",
"label": "No Quote"
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.docstatus >= 1 && !doc.no_quote",
"depends_on": "eval:doc.docstatus >= 1",
"fieldname": "quote_status",
"fieldtype": "Select",
"label": "Quote Status",
"options": "Pending\nReceived\nNo Quote",
"options": "Pending\nReceived",
"read_only": 1
},
{
@ -90,17 +82,12 @@
"in_list_view": 1,
"label": "Email Id",
"no_copy": 1
},
{
"allow_on_submit": 1,
"fieldname": "download_pdf",
"fieldtype": "Button",
"label": "Download PDF"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-09-28 19:31:11.855588",
"modified": "2020-10-16 12:23:41.769820",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation Supplier",

View File

@ -91,12 +91,7 @@ class SupplierQuotation(BuyingController):
for my_item in self.items) if include_me else 0
if (sqi_count.count + self_count) == 0:
quote_status = _('Pending')
if quote_status == _('Received') and doc_sup.quote_status == _('No Quote'):
frappe.msgprint(_("{0} indicates that {1} will not provide a quotation, but all items \
have been quoted. Updating the RFQ quote status.").format(doc.name, self.supplier))
frappe.db.set_value('Request for Quotation Supplier', doc_sup.name, 'quote_status', quote_status)
frappe.db.set_value('Request for Quotation Supplier', doc_sup.name, 'no_quote', 0)
elif doc_sup.quote_status != _('No Quote'):
frappe.db.set_value('Request for Quotation Supplier', doc_sup.name, 'quote_status', quote_status)
def get_list_context(context=None):