From bd36f2b94d0eb43cbac5f8b2a0c6dbcb51376cf0 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 28 Sep 2020 15:59:03 +0530 Subject: [PATCH 1/7] feat: Supplier Email Preview in RFQ --- .../request_for_quotation.js | 54 +++++++++++++++++-- .../request_for_quotation.json | 44 +++++++++++++-- .../request_for_quotation.py | 25 +++++++++ .../emails/request_for_quotation.html | 31 +++++++---- 4 files changed, 139 insertions(+), 15 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 4a937f7f0d..46327574f7 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -22,8 +22,6 @@ frappe.ui.form.on("Request for Quotation",{ }, onload: function(frm) { - frm.add_fetch('email_template', 'response', 'message_for_supplier'); - if(!frm.doc.message_for_supplier) { frm.set_value("message_for_supplier", __("Please supply the specified items at the best possible rates")) } @@ -194,6 +192,56 @@ frappe.ui.form.on("Request for Quotation",{ }); }); dialog.show() + }, + + preview: (frm) => { + let dialog = new frappe.ui.Dialog({ + title: __('Preview Email'), + fields: [ + { + label: __('Supplier'), + fieldtype: 'Select', + fieldname: 'supplier', + options: frm.doc.suppliers.map(row => row.supplier), + reqd: 1 + }, + { + fieldtype: 'Column Break', + fieldname: 'col_break_1', + }, + { + label: __('Subject'), + fieldtype: 'Data', + fieldname: 'subject', + read_only: 1 + }, + { + fieldtype: 'Section Break', + fieldname: 'sec_break_1', + hide_border: 1 + }, + { + label: __('Email'), + fieldtype: 'HTML', + fieldname: 'email_preview', + }, + ] + }); + + dialog.fields_dict['supplier'].df.onchange = () => { + var args = { + 'supplier' : dialog.get_value('supplier'), + 'salutation' : frm.doc.salutation || null, + 'message' : frm.doc.message_for_supplier + } + frm.call('get_supplier_email_preview', args).then(result => { + dialog.fields_dict.email_preview.$wrapper.empty(); + dialog.fields_dict.email_preview.$wrapper.append(result.message); + }); + + } + dialog.set_value("subject", frm.doc.subject); + dialog.show(); } }) @@ -276,7 +324,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e }) }, __("Get items from")); // Get items from Opportunity - this.frm.add_custom_button(__('Opportunity'), + this.frm.add_custom_button(__('Opportunity'), function() { erpnext.utils.map_current_doc({ method: "erpnext.crm.doctype.opportunity.opportunity.make_request_for_quotation", diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json index 5cd8e6f4fa..715556c204 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json @@ -1,5 +1,5 @@ { - "actions": "", + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2016-02-25 01:24:07.224790", @@ -19,7 +19,12 @@ "items", "link_to_mrs", "supplier_response_section", + "salutation", "email_template", + "col_break_email_1", + "subject", + "preview", + "sec_break_email_2", "message_for_supplier", "terms_section_break", "tc_name", @@ -126,8 +131,10 @@ "label": "Link to Material Requests" }, { + "depends_on": "eval:!doc.__islocal", "fieldname": "supplier_response_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Email Details" }, { "fieldname": "email_template", @@ -137,6 +144,8 @@ "print_hide": 1 }, { + "fetch_from": "email_template.response", + "fetch_if_empty": 1, "fieldname": "message_for_supplier", "fieldtype": "Text Editor", "in_list_view": 1, @@ -230,12 +239,41 @@ "options": "Request for Quotation", "print_hide": 1, "read_only": 1 + }, + { + "fetch_from": "email_template.subject", + "fetch_if_empty": 1, + "fieldname": "subject", + "fieldtype": "Data", + "label": "Subject" + }, + { + "description": "Select a greeting for the receiver. E.g. Mr., Ms., etc.", + "fieldname": "salutation", + "fieldtype": "Link", + "label": "Salutation", + "options": "Salutation" + }, + { + "fieldname": "col_break_email_1", + "fieldtype": "Column Break" + }, + { + "fieldname": "preview", + "fieldtype": "Button", + "label": "Preview Email" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "sec_break_email_2", + "fieldtype": "Section Break", + "hide_border": 1 } ], "icon": "fa fa-shopping-cart", "is_submittable": 1, "links": [], - "modified": "2020-06-25 14:37:21.140194", + "modified": "2020-09-28 14:25:31.357817", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation", diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index b54a585b97..c3f69d7b05 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -62,6 +62,31 @@ class RequestforQuotation(BuyingController): def on_cancel(self): frappe.db.set(self, 'status', 'Cancelled') + def get_supplier_email_preview(self, args): + rfq_suppliers = list(filter(lambda row: row.supplier == args.get('supplier'), self.suppliers)) + rfq_supplier = rfq_suppliers[0].as_dict() + + update_password_link = self.update_supplier_contact(rfq_supplier, self.get_link()) + + full_name = get_user_fullname(frappe.session['user']) + if full_name == "Guest": + full_name = "Administrator" + + args = { + 'update_password_link': update_password_link, + 'message': frappe.render_template(self.message_for_supplier, args), + 'rfq_link': self.get_link(), + 'user_fullname': full_name, + 'supplier': rfq_supplier.supplier_name, + 'salutation': args.get('salutation') + } + args.update(self.as_dict()) + + subject = _("Request for Quotation") + template = "templates/emails/request_for_quotation.html" + message = frappe.get_template(template).render(args) + return message + def send_to_supplier(self): for rfq_supplier in self.suppliers: if rfq_supplier.send_email: diff --git a/erpnext/templates/emails/request_for_quotation.html b/erpnext/templates/emails/request_for_quotation.html index b4dfb88c67..414dd0f742 100644 --- a/erpnext/templates/emails/request_for_quotation.html +++ b/erpnext/templates/emails/request_for_quotation.html @@ -1,11 +1,24 @@ -

{{_("Request for Quotation")}}

+

{{_("Request for Quotation")}}

+

{{_("Dear")}} {{ salutation if salutation else ''}} {{ supplier }},

{{ message }}

-{% if update_password_link %} -

{{_("Please click on the following link to set your new password")}}:

-

{{ update_password_link }}

-{% else %} +

{{_("The request for quotation can be accessed by clicking on the following link")}}:

-

Submit your Quotation

-{% endif %} -

{{_("Thank you")}},
-{{ user_fullname }}

+

+ +


+ +

{{_("Regards")}},
+{{ user_fullname }}


+ +{% if update_password_link %} +
+

{{_("Please click on the following link to set your new password")}}:

+

+ +

+
+{% endif %} \ No newline at end of file From 056c86033dead9067d13f4852747bdbed289ba64 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 28 Sep 2020 17:42:22 +0530 Subject: [PATCH 2/7] fix: Reuse function to render email preview - Reused `supplier_rfq_mail` to render preview - Print hid Salutation and Subject - Completely dynamic greeting --- .../request_for_quotation.js | 11 +++--- .../request_for_quotation.json | 9 +++-- .../request_for_quotation.py | 36 ++++++++----------- .../emails/request_for_quotation.html | 10 +++--- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 46327574f7..880a4cdf42 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -213,7 +213,8 @@ frappe.ui.form.on("Request for Quotation",{ label: __('Subject'), fieldtype: 'Data', fieldname: 'subject', - read_only: 1 + read_only: 1, + depends_on: 'subject' }, { fieldtype: 'Section Break', @@ -229,12 +230,8 @@ frappe.ui.form.on("Request for Quotation",{ }); dialog.fields_dict['supplier'].df.onchange = () => { - var args = { - 'supplier' : dialog.get_value('supplier'), - 'salutation' : frm.doc.salutation || null, - 'message' : frm.doc.message_for_supplier - } - frm.call('get_supplier_email_preview', args).then(result => { + var supplier = dialog.get_value('supplier'); + frm.call('get_supplier_email_preview', {supplier: supplier}).then(result => { dialog.fields_dict.email_preview.$wrapper.empty(); dialog.fields_dict.email_preview.$wrapper.append(result.message); }); diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json index 715556c204..7e5650f670 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json @@ -245,14 +245,17 @@ "fetch_if_empty": 1, "fieldname": "subject", "fieldtype": "Data", - "label": "Subject" + "label": "Subject", + "print_hide": 1 }, { "description": "Select a greeting for the receiver. E.g. Mr., Ms., etc.", "fieldname": "salutation", "fieldtype": "Link", "label": "Salutation", - "options": "Salutation" + "no_copy": 1, + "options": "Salutation", + "print_hide": 1 }, { "fieldname": "col_break_email_1", @@ -273,7 +276,7 @@ "icon": "fa fa-shopping-cart", "is_submittable": 1, "links": [], - "modified": "2020-09-28 14:25:31.357817", + "modified": "2020-09-28 17:37:10.313350", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation", diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index c3f69d7b05..5c9b91eea4 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -62,29 +62,15 @@ class RequestforQuotation(BuyingController): def on_cancel(self): frappe.db.set(self, 'status', 'Cancelled') - def get_supplier_email_preview(self, args): - rfq_suppliers = list(filter(lambda row: row.supplier == args.get('supplier'), self.suppliers)) - rfq_supplier = rfq_suppliers[0].as_dict() + def get_supplier_email_preview(self, supplier): + # Returns formatted email preview as string + rfq_suppliers = list(filter(lambda row: row.supplier == supplier, self.suppliers)) + rfq_supplier = rfq_suppliers[0] update_password_link = self.update_supplier_contact(rfq_supplier, self.get_link()) - full_name = get_user_fullname(frappe.session['user']) - if full_name == "Guest": - full_name = "Administrator" + message = self.supplier_rfq_mail(rfq_supplier, update_password_link, self.get_link(), True) - args = { - 'update_password_link': update_password_link, - 'message': frappe.render_template(self.message_for_supplier, args), - 'rfq_link': self.get_link(), - 'user_fullname': full_name, - 'supplier': rfq_supplier.supplier_name, - 'salutation': args.get('salutation') - } - args.update(self.as_dict()) - - subject = _("Request for Quotation") - template = "templates/emails/request_for_quotation.html" - message = frappe.get_template(template).render(args) return message def send_to_supplier(self): @@ -154,7 +140,7 @@ class RequestforQuotation(BuyingController): return user, update_password_link - def supplier_rfq_mail(self, data, update_password_link, rfq_link): + def supplier_rfq_mail(self, data, update_password_link, rfq_link, preview=False): full_name = get_user_fullname(frappe.session['user']) if full_name == "Guest": full_name = "Administrator" @@ -163,13 +149,19 @@ class RequestforQuotation(BuyingController): 'update_password_link': update_password_link, 'message': frappe.render_template(self.message_for_supplier, data.as_dict()), 'rfq_link': rfq_link, - 'user_fullname': full_name + 'user_fullname': full_name, + 'supplier_name' : data.get('supplier_name'), + 'supplier_salutation' : self.salutation or 'Dear Mx.', } - subject = _("Request for Quotation") + subject = self.subject or _("Request for Quotation") template = "templates/emails/request_for_quotation.html" sender = frappe.session.user not in STANDARD_USERS and frappe.session.user or None message = frappe.get_template(template).render(args) + + if preview: + return message + attachments = self.get_attachments() self.send_email(data, sender, subject, message, attachments) diff --git a/erpnext/templates/emails/request_for_quotation.html b/erpnext/templates/emails/request_for_quotation.html index 414dd0f742..216bd81d35 100644 --- a/erpnext/templates/emails/request_for_quotation.html +++ b/erpnext/templates/emails/request_for_quotation.html @@ -1,11 +1,11 @@

{{_("Request for Quotation")}}

-

{{_("Dear")}} {{ salutation if salutation else ''}} {{ supplier }},

+

{{ supplier_salutation if supplier_salutation else ''}} {{ supplier_name }},

{{ message }}

-

{{_("The request for quotation can be accessed by clicking on the following link")}}:

+

{{_("The Request for Quotation can be accessed by clicking on the following button")}}:


@@ -14,10 +14,10 @@ {% if update_password_link %}
-

{{_("Please click on the following link to set your new password")}}:

+

{{_("Please click on the following button to set your new password")}}:

From f91122887136156f0fa48e9dd81f96ffc35d2fd9 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 28 Sep 2020 20:21:44 +0530 Subject: [PATCH 3/7] fix: UX and code cleanup - Added a small note in muted text at bottom of Dialog - Dont call 'update_supplier_contact' at the time of preview, its a dummy view anyway - Renamed `update_contact_of_supplier` to `link_supplier_contact` - On sending mail, new users and contacts that were created get updated in RFQ doc - Added docstrings to functions --- .../request_for_quotation.js | 15 +- .../request_for_quotation.py | 33 +- .../request_for_quotation_supplier.json | 442 ++++-------------- 3 files changed, 135 insertions(+), 355 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 880a4cdf42..9b9104d47e 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -224,8 +224,17 @@ frappe.ui.form.on("Request for Quotation",{ { label: __('Email'), fieldtype: 'HTML', - fieldname: 'email_preview', + fieldname: 'email_preview' }, + { + fieldtype: 'Section Break', + fieldname: 'sec_break_2' + }, + { + label: __('Note'), + fieldtype: 'HTML', + fieldname: 'note' + } ] }); @@ -237,6 +246,10 @@ frappe.ui.form.on("Request for Quotation",{ }); } + + dialog.fields_dict.note.$wrapper.append(`

This is a preview of the email to be sent. A PDF of this document will + automatically be attached with the mail.

`); + dialog.set_value("subject", frm.doc.subject); dialog.show(); } diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 5c9b91eea4..84ddac4361 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -51,7 +51,7 @@ class RequestforQuotation(BuyingController): def validate_email_id(self, args): if not args.email_id: - frappe.throw(_("Row {0}: For Supplier {0}, Email Address is Required to Send Email").format(args.idx, args.supplier)) + frappe.throw(_("Row {0}: For Supplier {1}, Email Address is Required to send an email").format(args.idx, frappe.bold(args.supplier))) def on_submit(self): frappe.db.set(self, 'status', 'Submitted') @@ -63,27 +63,36 @@ class RequestforQuotation(BuyingController): frappe.db.set(self, 'status', 'Cancelled') def get_supplier_email_preview(self, supplier): - # Returns formatted email preview as string + '''Returns formatted email preview as string''' rfq_suppliers = list(filter(lambda row: row.supplier == supplier, self.suppliers)) rfq_supplier = rfq_suppliers[0] - update_password_link = self.update_supplier_contact(rfq_supplier, self.get_link()) + self.validate_email_id(rfq_supplier) + + update_password_link = "" + if not frappe.db.exists("User", rfq_supplier.email_id): + # user doesnt exist + # so (dummy) update password button, should be included for preview + # as it will be included in actual mail, on user creation + update_password_link = "#" message = self.supplier_rfq_mail(rfq_supplier, update_password_link, self.get_link(), True) return message def send_to_supplier(self): + '''Sends RFQ mail to involved suppliers''' for rfq_supplier in self.suppliers: if rfq_supplier.send_email: self.validate_email_id(rfq_supplier) # make new user if required - update_password_link = self.update_supplier_contact(rfq_supplier, self.get_link()) + update_password_link, contact = self.update_supplier_contact(rfq_supplier, self.get_link()) self.update_supplier_part_no(rfq_supplier) self.supplier_rfq_mail(rfq_supplier, update_password_link, self.get_link()) rfq_supplier.email_sent = 1 + rfq_supplier.contact = contact rfq_supplier.save() def get_link(self): @@ -98,18 +107,22 @@ class RequestforQuotation(BuyingController): def update_supplier_contact(self, rfq_supplier, link): '''Create a new user for the supplier if not set in contact''' - update_password_link = '' + update_password_link, contact = '', '' if frappe.db.exists("User", rfq_supplier.email_id): user = frappe.get_doc("User", rfq_supplier.email_id) else: user, update_password_link = self.create_user(rfq_supplier, link) - self.update_contact_of_supplier(rfq_supplier, user) + contact = self.link_supplier_contact(rfq_supplier, user) - return update_password_link + return update_password_link, contact - def update_contact_of_supplier(self, rfq_supplier, user): + def link_supplier_contact(self, rfq_supplier, user): + ''' + Create a new contact if no contact against Supplier. + If Contact exists, check if email and user id set + ''' if rfq_supplier.contact: contact = frappe.get_doc("Contact", rfq_supplier.contact) else: @@ -126,6 +139,10 @@ class RequestforQuotation(BuyingController): contact.save(ignore_permissions=True) + if not rfq_supplier.contact: + # return contact to later update, RFQ supplier row's contact + return contact.name + def create_user(self, rfq_supplier, link): user = frappe.get_doc({ 'doctype': 'User', diff --git a/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json b/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json index 61ad336574..ce9316f987 100644 --- a/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json +++ b/erpnext/buying/doctype/request_for_quotation_supplier/request_for_quotation_supplier.json @@ -1,362 +1,112 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-03-29 05:59:11.896885", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2016-03-29 05:59:11.896885", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "send_email", + "email_sent", + "supplier", + "contact", + "no_quote", + "quote_status", + "column_break_3", + "supplier_name", + "email_id", + "download_pdf" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "send_email", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Send Email", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "1", + "fieldname": "send_email", + "fieldtype": "Check", + "label": "Send Email" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:doc.docstatus >= 1", - "fieldname": "email_sent", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Email Sent", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "0", + "depends_on": "eval:doc.docstatus >= 1", + "fieldname": "email_sent", + "fieldtype": "Check", + "label": "Email Sent", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 4, - "fieldname": "supplier", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Supplier", - "length": 0, - "no_copy": 0, - "options": "Supplier", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 4, + "fieldname": "supplier", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Supplier", + "options": "Supplier", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "contact", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Contact", - "length": 0, - "no_copy": 1, - "options": "Contact", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "columns": 3, + "fieldname": "contact", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Contact", + "no_copy": 1, + "options": "Contact" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus >= 1 && doc.quote_status != 'Received'", - "fieldname": "no_quote", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "No Quote", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "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_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus >= 1 && !doc.no_quote", - "fieldname": "quote_status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quote Status", - "length": 0, - "no_copy": 0, - "options": "Pending\nReceived\nNo Quote", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "depends_on": "eval:doc.docstatus >= 1 && !doc.no_quote", + "fieldname": "quote_status", + "fieldtype": "Select", + "label": "Quote Status", + "options": "Pending\nReceived\nNo Quote", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, + "bold": 1, "fetch_from": "supplier.supplier_name", - "fieldname": "supplier_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Supplier Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "supplier_name", + "fieldtype": "Read Only", + "in_global_search": 1, + "label": "Supplier Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, + "columns": 3, "fetch_from": "contact.email_id", - "fieldname": "email_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Email Id", - "length": 0, - "no_copy": 1, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "email_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Email Id", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "download_pdf", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Download PDF", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "allow_on_submit": 1, + "fieldname": "download_pdf", + "fieldtype": "Button", + "label": "Download PDF" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-05-16 22:43:30.212408", - "modified_by": "Administrator", - "module": "Buying", - "name": "Request for Quotation Supplier", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-09-28 19:31:11.855588", + "modified_by": "Administrator", + "module": "Buying", + "name": "Request for Quotation Supplier", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From a6ea86432ed4854a99c2d52bdf3ac0b305bb2571 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 28 Sep 2020 21:05:11 +0530 Subject: [PATCH 4/7] fix: Skip Update Password in Preview - dont show update password button in preview, its for the suppliers mail only - modified docstrings - update supplier row contact only if None --- .../request_for_quotation.js | 4 ++-- .../request_for_quotation.py | 21 ++++++------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 9b9104d47e..14747b0abb 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -247,8 +247,8 @@ frappe.ui.form.on("Request for Quotation",{ } - dialog.fields_dict.note.$wrapper.append(`

This is a preview of the email to be sent. A PDF of this document will - automatically be attached with the mail.

`); + dialog.fields_dict.note.$wrapper.append(`

This is a preview of the email to be sent. A PDF of the document will + automatically be attached with the email.

`); dialog.set_value("subject", frm.doc.subject); dialog.show(); diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 84ddac4361..bef64b0e8d 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -63,25 +63,18 @@ class RequestforQuotation(BuyingController): frappe.db.set(self, 'status', 'Cancelled') def get_supplier_email_preview(self, supplier): - '''Returns formatted email preview as string''' + """Returns formatted email preview as string""" rfq_suppliers = list(filter(lambda row: row.supplier == supplier, self.suppliers)) rfq_supplier = rfq_suppliers[0] self.validate_email_id(rfq_supplier) - update_password_link = "" - if not frappe.db.exists("User", rfq_supplier.email_id): - # user doesnt exist - # so (dummy) update password button, should be included for preview - # as it will be included in actual mail, on user creation - update_password_link = "#" - - message = self.supplier_rfq_mail(rfq_supplier, update_password_link, self.get_link(), True) + message = self.supplier_rfq_mail(rfq_supplier, '', self.get_link(), True) return message def send_to_supplier(self): - '''Sends RFQ mail to involved suppliers''' + """Sends RFQ mail to involved suppliers""" for rfq_supplier in self.suppliers: if rfq_supplier.send_email: self.validate_email_id(rfq_supplier) @@ -92,7 +85,8 @@ class RequestforQuotation(BuyingController): self.update_supplier_part_no(rfq_supplier) self.supplier_rfq_mail(rfq_supplier, update_password_link, self.get_link()) rfq_supplier.email_sent = 1 - rfq_supplier.contact = contact + if not rfq_supplier.contact: + rfq_supplier.contact = contact rfq_supplier.save() def get_link(self): @@ -119,10 +113,7 @@ class RequestforQuotation(BuyingController): return update_password_link, contact def link_supplier_contact(self, rfq_supplier, user): - ''' - Create a new contact if no contact against Supplier. - If Contact exists, check if email and user id set - ''' + """If no Contact, create a new contact against Supplier. If Contact exists, check if email and user id set""" if rfq_supplier.contact: contact = frappe.get_doc("Contact", rfq_supplier.contact) else: From 629fa57f9d81d64051da7b799b3b2f601e9ecb76 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 28 Sep 2020 21:17:49 +0530 Subject: [PATCH 5/7] fix: Remove unneccessary div --- erpnext/templates/emails/request_for_quotation.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/templates/emails/request_for_quotation.html b/erpnext/templates/emails/request_for_quotation.html index 216bd81d35..812939a553 100644 --- a/erpnext/templates/emails/request_for_quotation.html +++ b/erpnext/templates/emails/request_for_quotation.html @@ -13,12 +13,12 @@ {{ user_fullname }}


{% if update_password_link %} -
+

{{_("Please click on the following button to set your new password")}}:

-
+ {% endif %} \ No newline at end of file From 65fc48da25439dc414c1cfbaeef625eea1b2e7a7 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 29 Sep 2020 12:46:19 +0530 Subject: [PATCH 6/7] fix: Email Template and Codacy - Email Preview works well with email template - Codacy for docstrings --- .../request_for_quotation.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index bef64b0e8d..8f946a7c51 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -63,7 +63,7 @@ class RequestforQuotation(BuyingController): frappe.db.set(self, 'status', 'Cancelled') def get_supplier_email_preview(self, supplier): - """Returns formatted email preview as string""" + """Returns formatted email preview as string.""" rfq_suppliers = list(filter(lambda row: row.supplier == supplier, self.suppliers)) rfq_supplier = rfq_suppliers[0] @@ -74,7 +74,7 @@ class RequestforQuotation(BuyingController): return message def send_to_supplier(self): - """Sends RFQ mail to involved suppliers""" + """Sends RFQ mail to involved suppliers.""" for rfq_supplier in self.suppliers: if rfq_supplier.send_email: self.validate_email_id(rfq_supplier) @@ -113,7 +113,7 @@ class RequestforQuotation(BuyingController): return update_password_link, contact def link_supplier_contact(self, rfq_supplier, user): - """If no Contact, create a new contact against Supplier. If Contact exists, check if email and user id set""" + """If no Contact, create a new contact against Supplier. If Contact exists, check if email and user id set.""" if rfq_supplier.contact: contact = frappe.get_doc("Contact", rfq_supplier.contact) else: @@ -153,9 +153,17 @@ class RequestforQuotation(BuyingController): if full_name == "Guest": full_name = "Administrator" + # send document dict and some important data from suppliers row + # to render message_for_supplier from any template + doc_args = self.as_dict() + doc_args.update({ + 'supplier': data.get('supplier'), + 'supplier_name': data.get('supplier_name') + }) + args = { 'update_password_link': update_password_link, - 'message': frappe.render_template(self.message_for_supplier, data.as_dict()), + 'message': frappe.render_template(self.message_for_supplier, doc_args), 'rfq_link': rfq_link, 'user_fullname': full_name, 'supplier_name' : data.get('supplier_name'), From 29d7528a956015070c710fa90cd42cd3c0828031 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 1 Oct 2020 14:55:21 +0530 Subject: [PATCH 7/7] fix: Hide Preview Email button on submitted doc --- .../doctype/request_for_quotation/request_for_quotation.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json index 7e5650f670..5f01f6e24c 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json @@ -262,6 +262,7 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:!doc.docstatus==1", "fieldname": "preview", "fieldtype": "Button", "label": "Preview Email" @@ -276,7 +277,7 @@ "icon": "fa fa-shopping-cart", "is_submittable": 1, "links": [], - "modified": "2020-09-28 17:37:10.313350", + "modified": "2020-10-01 14:54:50.888729", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation",