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 %}
+
+{% 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 %}
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 %}
-
+
{% 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",