Merge branch 'bcornwellmott-no_quote' into develop

This commit is contained in:
Nabin Hait 2017-08-17 12:03:01 +05:30
commit 0650d8e155
10 changed files with 332 additions and 32 deletions

View File

@ -138,7 +138,6 @@ frappe.ui.form.on("Request for Quotation",{
dialog.show();
},
make_suppplier_quotation: function(frm) {
var doc = frm.doc;
var dialog = new frappe.ui.Dialog({
@ -207,6 +206,29 @@ frappe.ui.form.on("Request for Quotation Supplier",{
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");
}
});
}
}
})

View File

@ -54,6 +54,7 @@ class RequestforQuotation(BuyingController):
frappe.db.set(self, 'status', 'Submitted')
for supplier in self.suppliers:
supplier.email_sent = 0
supplier.quote_status = 'Pending'
def on_cancel(self):
frappe.db.set(self, 'status', 'Cancelled')
@ -157,6 +158,28 @@ class RequestforQuotation(BuyingController):
attachments.append(frappe.attach_print(self.doctype, self.name, doc=self))
return attachments
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("""
SELECT
COUNT(sqi.name) as count
FROM
`tabSupplier Quotation Item` as sqi,
`tabSupplier Quotation` as sq
WHERE sq.supplier = %(supplier)s
AND sqi.docstatus = 1
AND sqi.request_for_quotation_item = %(rqi)s
AND sqi.parent = sq.name""",
{"supplier": supplier.supplier, "rqi": item.name}, as_dict=1)[0]
if (sqi_count.count) == 0:
quote_status = _('Pending')
supplier.quote_status = quote_status
@frappe.whitelist()
def send_supplier_emails(rfq_name):
check_portal_enabled('Request for Quotation')

View File

@ -10,21 +10,41 @@ from erpnext.templates.pages.rfq import check_supplier_has_docname_access
from frappe.utils import nowdate
class TestRequestforQuotation(unittest.TestCase):
def test_quote_status(self):
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
rfq = make_request_for_quotation()
self.assertEquals(rfq.get('suppliers')[0].quote_status, 'Pending')
self.assertEquals(rfq.get('suppliers')[1].quote_status, 'Pending')
# Submit the first supplier quotation
sq = make_supplier_quotation(rfq.name, 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.assertEquals(rfq.get('suppliers')[0].quote_status, 'Received')
self.assertEquals(rfq.get('suppliers')[1].quote_status, 'No Quote')
def test_make_supplier_quotation(self):
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
rfq = make_request_for_quotation()
sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier)
sq.submit()
sq1 = make_supplier_quotation(rfq.name, rfq.get('suppliers')[1].supplier)
sq1.submit()
self.assertEquals(sq.supplier, rfq.get('suppliers')[0].supplier)
self.assertEquals(sq.get('items')[0].request_for_quotation, rfq.name)
self.assertEquals(sq.get('items')[0].item_code, "_Test Item")
self.assertEquals(sq.get('items')[0].qty, 5)
self.assertEquals(sq1.supplier, rfq.get('suppliers')[1].supplier)
self.assertEquals(sq1.get('items')[0].request_for_quotation, rfq.name)
self.assertEquals(sq1.get('items')[0].item_code, "_Test Item")
@ -61,15 +81,15 @@ class TestRequestforQuotation(unittest.TestCase):
rfq.get('items')[0].rate = 100
rfq.supplier = rfq.suppliers[0].supplier
supplier_quotation_name = create_supplier_quotation(rfq)
supplier_quotation_doc = frappe.get_doc('Supplier Quotation', supplier_quotation_name)
self.assertEquals(supplier_quotation_doc.supplier, rfq.get('suppliers')[0].supplier)
self.assertEquals(supplier_quotation_doc.get('items')[0].request_for_quotation, rfq.name)
self.assertEquals(supplier_quotation_doc.get('items')[0].item_code, "_Test Item")
self.assertEquals(supplier_quotation_doc.get('items')[0].qty, 5)
self.assertEquals(supplier_quotation_doc.get('items')[0].amount, 500)
def make_request_for_quotation(supplier_data=None):
"""
@ -81,10 +101,10 @@ def make_request_for_quotation(supplier_data=None):
rfq.status = 'Draft'
rfq.company = '_Test Company'
rfq.message_for_supplier = 'Please supply the specified items at the best possible rates.'
for data in supplier_data:
rfq.append('suppliers', data)
rfq.append("items", {
"item_code": "_Test Item",
"description": "_Test Item",
@ -93,11 +113,11 @@ def make_request_for_quotation(supplier_data=None):
"warehouse": "_Test Warehouse - _TC",
"schedule_date": nowdate()
})
rfq.submit()
return rfq
def get_supplier_data():
return [{
"supplier": "_Test Supplier",

View File

@ -0,0 +1,132 @@
QUnit.module('buying');
QUnit.test("Test: Request for Quotation", function (assert) {
assert.expect(5);
let done = assert.async();
let rfq_name = "";
frappe.run_serially([
// Go to RFQ list
() => frappe.set_route("List", "Request for Quotation"),
// Create a new RFQ
() => frappe.new_doc("Request for Quotation"),
() => frappe.timeout(1),
() => cur_frm.set_value("transaction_date", "04-04-2017"),
() => cur_frm.set_value("company", "_Test Company"),
// Add Suppliers
() => {
cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view();
},
() => frappe.timeout(1),
() => {
cur_frm.fields_dict.suppliers.grid.grid_rows[0].doc.supplier = "_Test Supplier";
frappe.click_check('Send Email');
cur_frm.cur_grid.frm.script_manager.trigger('supplier');
},
() => frappe.timeout(1),
() => {
cur_frm.cur_grid.toggle_view();
},
() => frappe.timeout(1),
() => frappe.click_button('Add Row',0),
() => frappe.timeout(1),
() => {
cur_frm.fields_dict.suppliers.grid.grid_rows[1].toggle_view();
},
() => frappe.timeout(1),
() => {
cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.supplier = "_Test Supplier 1";
frappe.click_check('Send Email');
cur_frm.cur_grid.frm.script_manager.trigger('supplier');
},
() => frappe.timeout(1),
() => {
cur_frm.cur_grid.toggle_view();
},
() => frappe.timeout(1),
// Add Item
() => {
cur_frm.fields_dict.items.grid.grid_rows[0].toggle_view();
},
() => frappe.timeout(1),
() => {
cur_frm.fields_dict.items.grid.grid_rows[0].doc.item_code = "_Test Item";
frappe.set_control('item_code',"_Test Item");
frappe.set_control('qty',5);
frappe.set_control('schedule_date', "05-05-2017");
cur_frm.cur_grid.frm.script_manager.trigger('supplier');
},
() => frappe.timeout(2),
() => {
cur_frm.cur_grid.toggle_view();
},
() => frappe.timeout(2),
() => {
cur_frm.fields_dict.items.grid.grid_rows[0].doc.warehouse = "_Test Warehouse - _TC";
},
() => frappe.click_button('Save'),
() => frappe.timeout(1),
() => frappe.click_button('Submit'),
() => frappe.timeout(1),
() => frappe.click_button('Yes'),
() => frappe.timeout(1),
() => frappe.click_button('Menu'),
() => frappe.timeout(1),
() => frappe.click_link('Reload'),
() => frappe.timeout(1),
() => {
assert.equal(cur_frm.doc.docstatus, 1);
rfq_name = cur_frm.doc.name;
assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[0].doc.quote_status == "Pending");
assert.ok(cur_frm.fields_dict.suppliers.grid.grid_rows[1].doc.quote_status == "Pending");
},
() => {
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();
},
() => frappe.click_button('Update'),
() => frappe.timeout(1),
() => frappe.click_button('Supplier Quotation'),
() => frappe.timeout(1),
() => frappe.click_link('Make'),
() => frappe.timeout(1),
() => {
frappe.set_control('supplier',"_Test Supplier 1");
},
() => frappe.timeout(1),
() => frappe.click_button('Make Supplier Quotation'),
() => frappe.timeout(1),
() => cur_frm.set_value("company", "_Test Company"),
() => cur_frm.fields_dict.items.grid.grid_rows[0].doc.rate = 4.99,
() => frappe.timeout(1),
() => frappe.click_button('Save'),
() => frappe.timeout(1),
() => frappe.click_button('Submit'),
() => frappe.timeout(1),
() => frappe.click_button('Yes'),
() => frappe.timeout(1),
() => frappe.set_route("List", "Request for Quotation"),
() => frappe.timeout(2),
() => frappe.set_route("List", "Request for Quotation"),
() => frappe.timeout(2),
() => frappe.click_link(rfq_name),
() => frappe.timeout(1),
() => frappe.click_button('Menu'),
() => frappe.timeout(1),
() => frappe.click_link('Reload'),
() => 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

@ -137,6 +137,69 @@
"set_only_once": 0,
"unique": 0
},
{
"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,
"unique": 0
},
{
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -269,7 +332,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-07-24 06:52:19.542717",
"modified": "2017-07-26 22:25:58.096584",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation Supplier",

View File

@ -31,9 +31,11 @@ class SupplierQuotation(BuyingController):
def on_submit(self):
frappe.db.set(self, "status", "Submitted")
self.update_rfq_supplier_status(1)
def on_cancel(self):
frappe.db.set(self, "status", "Cancelled")
self.update_rfq_supplier_status(0)
def on_trash(self):
pass
@ -50,6 +52,41 @@ class SupplierQuotation(BuyingController):
"is_child_table": True
}
})
def update_rfq_supplier_status(self, include_me):
rfq_list = set([])
for item in self.items:
if item.request_for_quotation:
rfq_list.add(item.request_for_quotation)
for rfq in rfq_list:
doc = frappe.get_doc('Request for Quotation', rfq)
doc_sup = frappe.get_all('Request for Quotation Supplier', filters=
{'parent': doc.name, 'supplier': self.supplier}, fields=['name', 'quote_status'])[0]
quote_status = _('Received')
for item in doc.items:
sqi_count = frappe.db.sql("""
SELECT
COUNT(sqi.name) as count
FROM
`tabSupplier Quotation Item` as sqi,
`tabSupplier Quotation` as sq
WHERE sq.supplier = %(supplier)s
AND sqi.docstatus = 1
AND sq.name != %(me)s
AND sqi.request_for_quotation_item = %(rqi)s
AND sqi.parent = sq.name""",
{"supplier": self.supplier, "rqi": item.name, 'me': self.name}, as_dict=1)[0]
self_count = sum(my_item.request_for_quotation_item == item.name
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):
from erpnext.controllers.website_list_for_contact import get_list_context

View File

@ -2,43 +2,40 @@
A Request for Quotation is a document that an organization submits to one or more suppliers eliciting quotation for items.
In ERPNext, You can create request for quotation directly by going to:
In ERPNext, You can create Request for Quotation directly by going to:
> Buying > Documents > Request for Quotation > New Request for Quotation
![Request For Quotation](/docs/assets/img/buying/request-for-quotation.png)
After creation of Request for Quotation, there are two ways to generate Supplier Quotation from Request for Quotation.
After creation of request for quotation, there are two ways to generate supplier quotation from request for quotation.
#### For User
__Step 1:__ Open request for quotation and click on make supplier quotation.
__Step 1:__ Open Request for Quotation and click on make Supplier Quotation.
![Request For Quotation](/docs/assets/img/buying/make-supplier-quotation-from-rfq.png)
__Step 2:__ Select supplier and click on make supplier quotation.
__Step 2:__ Select supplier and click on make Supplier Quotation.
![Request For Quotation](/docs/assets/img/buying/supplier-selection-from-rfq.png)
__Step 3:__ System will open the supplier quotation, user has to enter the rate and submit it.
__Step 3:__ System will open the Supplier Quotation, user has to enter the rate and submit it.
![Request For Quotation](/docs/assets/img/buying/supplier-quotation-from-rfq.png)
#### For Supplier
__Step 1:__ User has to create contact or enter Email Address against the supplier on request for quotation.
__Step 1:__ User has to create contact or enter Email Address against the supplier on Request for Quotation.
![Request For Quotation](/docs/assets/img/buying/set-email-id.png)
__Step 2:__ User has to click on send supplier emails button.
![Request For Quotation](/docs/assets/img/buying/send-supplier-emails.png)
* If supplier's user not available: system will create supplier's user and send details to the supplier, supplier will need to click on the link(Password Update) present in the email. After password update supplier can access his portal with the request for quotation form.
* If supplier's user not available: system will create supplier's user and send details to the supplier, supplier will need to click on the link(Password Update) present in the email. After password update supplier can access his portal with the Request for Quotation form.
![Request For Quotation](/docs/assets/img/buying/supplier-password-update-link.png)
* If supplier's user available: system will send request for quotation link to supplier, supplier has to login using his credentials to view request for quotation form on portal.
* If supplier's user available: system will send Request for Quotation link to supplier, supplier has to login using his credentials to view Request for Quotation form on portal.
![Request For Quotation](/docs/assets/img/buying/send-rfq-link.png)
@ -46,9 +43,12 @@ __Step 3:__ Supplier has to enter amount and notes(payment terms) on the form an
![Request For Quotation](/docs/assets/img/buying/supplier-portal-rfq.png)
__Step 4:__ On submission, system will create supplier quotation(draft mode) against the supplier. User has to review the supplier quotation
and submit it.
More details:-
__Step 4:__ On submission, system will create Supplier Quotation (draft mode) against the supplier. The user has to review the Supplier Quotation
and submit it. When all items from the Request for Quotation have been quoted by a supplier, the status is updated in the Supplier
table of the Request for Quotation.
![Request For Quotation](/docs/assets/img/buying/request-for-quotation.gif)
#### More details
If a supplier indicates that they will not provide a quotation for the item, this can be indicated in the RFQ document by checking the 'No Quote' box after the Request for Quotation has been submitted.
![Request For Quotation](/docs/assets/img/buying/request-for-quotation.gif)

View File

@ -431,4 +431,7 @@ erpnext.patches.v8_5.set_default_mode_of_payment
erpnext.patches.v8_5.update_customer_group_in_POS_profile
erpnext.patches.v8_6.update_timesheet_company_from_PO
erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager
erpnext.patches.v8_5.remove_project_type_property_setter
<<<<<<< HEAD
erpnext.patches.v8_5.remove_project_type_property_setter
=======
>>>>>>> Set write permission to sales manger for permlevel 1 in Quotation doctype

View File

@ -8,4 +8,4 @@ def execute():
# Set write permission to permlevel 1 for sales manager role in Quotation doctype
frappe.db.sql(""" update `tabCustom DocPerm` set `tabCustom DocPerm`.write = 1
where `tabCustom DocPerm`.parent = 'Quotation' and `tabCustom DocPerm`.role = 'Sales Manager'
and `tabCustom DocPerm`.permlevel = 1 """)
and `tabCustom DocPerm`.permlevel = 1 """)