diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py index 0e2d6d0b4e..14738c5ff2 100644 --- a/erpnext/controllers/tests/test_mapper.py +++ b/erpnext/controllers/tests/test_mapper.py @@ -4,7 +4,7 @@ import frappe import random, json import frappe.utils -from frappe.utils import nowdate +from frappe.utils import nowdate, add_months from frappe.model import mapper from frappe.test_runner import make_test_records @@ -44,7 +44,9 @@ class TestMapper(unittest.TestCase): "doctype": "Quotation", "quotation_to": "Customer", "customer": customer, - "order_type": "Sales" + "order_type": "Sales", + "transaction_date" : nowdate(), + "valid_till" : add_months(nowdate(), 1) }) for item in item_list: qtn.append("items", {"qty": "2", "item_code": item.item_code}) diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 43fa20f8c1..b90f725bb2 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -27,9 +27,19 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ var me = this; + if (doc.valid_till && frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) < 0) { + this.frm.set_intro(__("Validity period of this quotation has ended")); + } + + if (doc.__islocal) { + this.frm.set_value('valid_till', frappe.datetime.add_months(doc.transaction_date, 1)) + } + if(doc.docstatus == 1 && doc.status!=='Lost') { - cur_frm.add_custom_button(__('Make Sales Order'), - cur_frm.cscript['Make Sales Order']); + if(!doc.valid_till || frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) > 0) { + cur_frm.add_custom_button(__('Make Sales Order'), + cur_frm.cscript['Make Sales Order']); + } if(doc.status!=="Ordered") { cur_frm.add_custom_button(__('Set as Lost'), diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 21090230dd..33eed19476 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -363,6 +363,36 @@ "unique": 0, "width": "100px" }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "valid_till", + "fieldtype": "Date", + "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": "Valid Till", + "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": 0, @@ -2572,7 +2602,7 @@ "istable": 0, "max_attachments": 1, "menu_index": 0, - "modified": "2017-07-19 13:49:33.388736", + "modified": "2017-07-25 08:14:35.355019", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 420b84a634..5eb8b06ed8 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe.model.mapper import get_mapped_doc -from frappe.utils import flt +from frappe.utils import flt, nowdate, getdate from frappe import _ from erpnext.controllers.selling_controller import SellingController @@ -21,9 +21,14 @@ class Quotation(SellingController): self.validate_order_type() self.validate_uom_is_integer("stock_uom", "qty") self.validate_quotation_to() + self.validate_valid_till() if self.items: self.with_items = 1 + def validate_valid_till(self): + if self.valid_till and self.valid_till < self.transaction_date: + frappe.throw(_("Valid till date cannot be before transaction date")) + def has_sales_order(self): return frappe.db.get_value("Sales Order Item", {"prevdoc_docname": self.name, "docstatus": 1}) @@ -100,6 +105,9 @@ def get_list_context(context=None): @frappe.whitelist() def make_sales_order(source_name, target_doc=None): + quotation = frappe.db.get_value("Quotation", source_name, ["transaction_date", "valid_till"], as_dict = 1) + if quotation.valid_till and (quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate())): + frappe.throw(_("Validity period of this quotation has ended.")) return _make_sales_order(source_name, target_doc) def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 7fb40748f0..8753376aeb 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt +from frappe.utils import flt, add_days, nowdate, add_months import unittest test_dependencies = ["Product Bundle"] @@ -13,10 +13,11 @@ class TestQuotation(unittest.TestCase): from erpnext.selling.doctype.quotation.quotation import make_sales_order quotation = frappe.copy_doc(test_records[0]) + quotation.transaction_date = nowdate() + quotation.valid_till = add_months(quotation.transaction_date, 1) quotation.insert() self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) - quotation.submit() sales_order = make_sales_order(quotation.name) @@ -28,11 +29,23 @@ class TestQuotation(unittest.TestCase): self.assertEquals(sales_order.customer, "_Test Customer") for d in sales_order.get("items"): - d.delivery_date = "2014-01-01" + d.delivery_date = add_months(quotation.transaction_date, 1) sales_order.naming_series = "_T-Quotation-" - sales_order.transaction_date = "2013-05-12" + sales_order.transaction_date = nowdate() sales_order.insert() + def test_valid_till(self): + from erpnext.selling.doctype.quotation.quotation import make_sales_order + + quotation = frappe.copy_doc(test_records[0]) + quotation.valid_till = add_days(quotation.transaction_date, -1) + self.assertRaises(frappe.ValidationError, quotation.validate) + + quotation.valid_till = add_days(nowdate(), -1) + quotation.insert() + quotation.submit() + self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) + def test_create_quotation_with_margin(self): from erpnext.selling.doctype.quotation.quotation import make_sales_order from erpnext.selling.doctype.sales_order.sales_order \ @@ -45,6 +58,8 @@ class TestQuotation(unittest.TestCase): test_records[0]['items'][0]['margin_rate_or_amount'] = 18.75 quotation = frappe.copy_doc(test_records[0]) + quotation.transaction_date = nowdate() + quotation.valid_till = add_months(quotation.transaction_date, 1) quotation.insert() self.assertEquals(quotation.get("items")[0].rate, rate_with_margin) diff --git a/erpnext/selling/doctype/quotation/test_records.json b/erpnext/selling/doctype/quotation/test_records.json index 5637fb906c..7a9d3eb1e2 100644 --- a/erpnext/selling/doctype/quotation/test_records.json +++ b/erpnext/selling/doctype/quotation/test_records.json @@ -23,14 +23,15 @@ "parentfield": "items", "qty": 10.0, "rate": 100.0, - "uom": "_Test UOM 1", + "uom": "_Test UOM 1", "stock_uom": "_Test UOM 1", - "conversion_factor": 1.0 + "conversion_factor": 1.0 } ], "quotation_to": "Customer", "selling_price_list": "_Test Price List", "territory": "_Test Territory", - "transaction_date": "2013-02-21" + "transaction_date": "2013-02-21", + "valid_till": "2013-03-21" } ] \ No newline at end of file diff --git a/erpnext/shopping_cart/test_shopping_cart.py b/erpnext/shopping_cart/test_shopping_cart.py index f221a8a7a1..22b2895043 100644 --- a/erpnext/shopping_cart/test_shopping_cart.py +++ b/erpnext/shopping_cart/test_shopping_cart.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import unittest import frappe +from frappe.utils import nowdate, add_months from erpnext.shopping_cart.cart import _get_cart_quotation, update_cart, get_party from erpnext.tests.utils import create_test_contact_and_address @@ -126,6 +127,8 @@ class TestShoppingCart(unittest.TestCase): "selling_price_list": "_Test Price List Rest of the World", "currency": "USD", "taxes_and_charges" : "_Test Tax 1", + "transaction_date" : nowdate(), + "valid_till" : add_months(nowdate(), 1), "items": [{ "item_code": "_Test Item", "qty": 1