diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 8134e7e701..0394f74b71 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -1,4 +1,3 @@ - // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt @@ -16,6 +15,8 @@ frappe.ui.form.on("Purchase Order", { }, onload: function(frm) { + set_schedule_date(frm); + erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); @@ -34,6 +35,17 @@ frappe.ui.form.on("Purchase Order Item", { frm.trigger('calculate_taxes_and_totals'); } }) + }, + + schedule_date: function(frm, cdt, cdn) { + var row = locals[cdt][cdn]; + if (row.schedule_date) { + if(!frm.doc.schedule_date) { + erpnext.utils.copy_value_in_all_row(frm.doc, cdt, cdn, "items", "schedule_date"); + } else { + set_schedule_date(frm); + } + } } }); @@ -120,12 +132,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( }, validate: function() { - // set default schedule date as today if missing. - (this.frm.doc.items || []).forEach(function(d) { - if(!d.schedule_date) { - d.schedule_date = frappe.datetime.nowdate(); - } - }) + set_schedule_date(this.frm); }, make_stock_entry: function() { @@ -214,7 +221,12 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( items_add: function(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); - this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]); + if(doc.schedule_date) { + row.schedule_date = doc.schedule_date; + refresh_field("schedule_date", cdn, "items"); + } else { + this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]); + } }, unclose_purchase_order: function(){ @@ -227,8 +239,26 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( delivered_by_supplier: function(){ cur_frm.cscript.update_status('Deliver', 'Delivered') - } + }, + get_last_purchase_rate: function() { + frappe.call({ + "method": "get_last_purchase_rate", + "doc": cur_frm.doc, + callback: function(r, rt) { + cur_frm.dirty(); + cur_frm.cscript.calculate_taxes_and_totals(); + } + }) + }, + + items_on_form_rendered: function() { + set_schedule_date(this.frm); + }, + + schedule_date: function() { + set_schedule_date(this.frm); + } }); // for backward compatibility: combine new and previous states @@ -270,8 +300,10 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) { } } -cur_frm.cscript.schedule_date = function(doc, cdt, cdn) { - erpnext.utils.copy_value_in_all_row(doc, cdt, cdn, "items", "schedule_date"); +function set_schedule_date(frm) { + if(frm.doc.schedule_date){ + erpnext.utils.copy_value_in_all_row(frm.doc, frm.doc.doctype, frm.doc.name, "items", "schedule_date"); + } } frappe.provide("erpnext.buying"); diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index af3f9a6c11..28b8b475c2 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -292,6 +292,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "schedule_date", + "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": "Reqd By Date", + "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, @@ -3427,7 +3458,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-09-22 16:11:49.856808", + "modified": "2017-10-05 14:19:04.102534", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 36cef4396b..415099328b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -41,6 +41,7 @@ class PurchaseOrder(BuyingController): self.set_status() self.validate_supplier() + self.validate_schedule_date() validate_for_items(self) self.check_for_closed_status() diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 17786aa443..2af55825d4 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -118,6 +118,7 @@ class TestPurchaseOrder(unittest.TestCase): "company": "_Test Company", "supplier" : "_Test Supplier", "is_subcontracted" : "No", + "schedule_date": add_days(nowdate(), 1), "currency" : frappe.db.get_value("Company", "_Test Company", "default_currency"), "conversion_factor" : 1, "items" : get_same_items(), @@ -149,6 +150,7 @@ def create_purchase_order(**args): if args.transaction_date: po.transaction_date = args.transaction_date + po.schedule_date = add_days(nowdate(), 1) po.company = args.company or "_Test Company" po.supplier = args.customer or "_Test Supplier" po.is_subcontracted = args.is_subcontracted or "No" diff --git a/erpnext/buying/doctype/purchase_order/test_records.json b/erpnext/buying/doctype/purchase_order/test_records.json index 2c2e792176..74b8f1b429 100644 --- a/erpnext/buying/doctype/purchase_order/test_records.json +++ b/erpnext/buying/doctype/purchase_order/test_records.json @@ -30,7 +30,8 @@ ], "supplier": "_Test Supplier", "supplier_name": "_Test Supplier", - "transaction_date": "2013-02-12" + "transaction_date": "2013-02-12", + "schedule_date": "2013-02-13" }, { "advance_paid": 0.0, @@ -63,6 +64,7 @@ ], "supplier": "_Test Supplier", "supplier_name": "_Test Supplier", - "transaction_date": "2013-02-12" + "transaction_date": "2013-02-12", + "schedule_date": "2013-02-13" } ] diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js index e6529e60db..6605a65170 100644 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js @@ -13,12 +13,21 @@ QUnit.test("test: purchase order", function(assert) { {items: [ [ {"item_code": 'Test Product 4'}, - {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, + {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 2)}, {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, {"qty": 5}, {"uom": 'Unit'}, {"rate": 100}, {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} + ], + [ + {"item_code": 'Test Product 1'}, + {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, + {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, + {"qty": 2}, + {"uom": 'Unit'}, + {"rate": 100}, + {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} ] ]}, @@ -30,12 +39,19 @@ QUnit.test("test: purchase order", function(assert) { () => { // Get supplier details assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct"); + assert.ok(cur_frm.doc.schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 1), "Schedule Date correct"); assert.ok($('div.control-value.like-disabled-input.for-description').text().includes('Contact 3'), "Contact display correct"); assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Contact email correct"); // Get item details assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item name correct"); assert.ok(cur_frm.doc.items[0].description == 'Test Product 4', "Description correct"); assert.ok(cur_frm.doc.items[0].qty == 5, "Quantity correct"); + assert.ok(cur_frm.doc.items[0].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 2), "Schedule Date correct"); + + assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item name correct"); + assert.ok(cur_frm.doc.items[1].description == 'Test Product 1', "Description correct"); + assert.ok(cur_frm.doc.items[1].qty == 2, "Quantity correct"); + assert.ok(cur_frm.doc.items[1].schedule_date == cur_frm.doc.schedule_date, "Schedule Date correct"); // Calculate total assert.ok(cur_frm.doc.total == 500, "Total correct"); // Get terms diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js index 9d87af2342..27129fb3b8 100644 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js @@ -11,6 +11,7 @@ QUnit.test("test: purchase order with taxes and charges", function(assert) { {is_subcontracted: 'No'}, {buying_price_list: 'Test-Buying-USD'}, {currency: 'USD'}, + {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, {items: [ [ {"item_code": 'Test Product 4'}, diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index d56c3bd5c3..ca1a0a7677 100755 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -140,7 +140,7 @@ "allow_on_submit": 1, "bold": 1, "collapsible": 0, - "columns": 0, + "columns": 2, "fieldname": "schedule_date", "fieldtype": "Date", "hidden": 0, @@ -148,7 +148,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Reqd By Date", "length": 0, @@ -382,7 +382,7 @@ "allow_on_submit": 0, "bold": 1, "collapsible": 0, - "columns": 2, + "columns": 1, "fieldname": "qty", "fieldtype": "Float", "hidden": 0, @@ -749,7 +749,7 @@ "allow_on_submit": 0, "bold": 1, "collapsible": 0, - "columns": 3, + "columns": 2, "fieldname": "rate", "fieldtype": "Currency", "hidden": 0, @@ -1745,7 +1745,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-09-22 16:47:08.783546", + "modified": "2017-10-05 19:47:12.433095", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py index b3d92be4b6..c395d0cfd7 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt +from frappe.utils import flt, nowdate, add_days from frappe.model.mapper import get_mapped_doc from erpnext.controllers.buying_controller import BuyingController @@ -151,5 +151,4 @@ def make_quotation(source_name, target_doc=None): } }, target_doc) - return doclist - + return doclist \ No newline at end of file diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 1f9051d433..d3010818e5 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _, msgprint -from frappe.utils import flt,cint, cstr +from frappe.utils import flt,cint, cstr, getdate from erpnext.accounts.party import get_party_details from erpnext.stock.get_item_details import get_conversion_factor @@ -408,3 +408,16 @@ class BuyingController(StockController): "actual_qty": -1*flt(d.consumed_qty), })) + def validate_schedule_date(self): + if not self.schedule_date: + self.schedule_date = min([d.schedule_date for d in self.get("items")]) + + if self.schedule_date: + for d in self.get('items'): + if not d.schedule_date: + d.schedule_date = self.schedule_date + + if d.schedule_date and getdate(d.schedule_date) < getdate(self.transaction_date): + frappe.throw(_("Expected Date cannot be before Transaction Date")) + else: + frappe.throw(_("Please enter Schedule Date")) \ No newline at end of file diff --git a/erpnext/demo/user/purchase.py b/erpnext/demo/user/purchase.py index 82983f0a71..8769b09156 100644 --- a/erpnext/demo/user/purchase.py +++ b/erpnext/demo/user/purchase.py @@ -129,6 +129,7 @@ def make_subcontract(): po = frappe.new_doc("Purchase Order") po.is_subcontracted = "Yes" po.supplier = get_random("Supplier") + po.schedule_date = frappe.utils.add_days(frappe.flags.current_date, 7) item_code = get_random("Item", {"is_sub_contracted_item": 1}) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 70122ba220..8b0f714dcb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -449,4 +449,4 @@ erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts erpnext.patches.v8_9.set_default_fields_in_variant_settings erpnext.patches.v8_9.update_billing_gstin_for_indian_account erpnext.patches.v9_0.fix_subscription_next_date -erpnext.patches.v9_0.set_schedule_date_for_material_request +erpnext.patches.v9_0.set_schedule_date_for_material_request_and_purchase_order diff --git a/erpnext/patches/v9_0/set_schedule_date_for_material_request.py b/erpnext/patches/v9_0/set_schedule_date_for_material_request.py deleted file mode 100644 index 39d4d413a1..0000000000 --- a/erpnext/patches/v9_0/set_schedule_date_for_material_request.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2017, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doctype("Material Request") - frappe.reload_doctype("Material Request Item") - - if not frappe.db.has_column("Material Request", "schedule_date"): - return - - #Update only submitted MR - for mr in frappe.get_all("Material Request", filters= [["docstatus", "=", 1]], fields=["name"]): - material_request = frappe.get_doc("Material Request", mr) - if material_request.items: - if not material_request.schedule_date: - max_schedule_date = max([d.schedule_date for d in material_request.items]) - frappe.db.set_value("Material Request", mr, - "schedule_date", max_schedule_date, update_modified=False) \ No newline at end of file diff --git a/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py b/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py new file mode 100644 index 0000000000..7ab0e2ca83 --- /dev/null +++ b/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py @@ -0,0 +1,22 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + for doctype in ("Material Request", "Purchase Order"): + frappe.reload_doctype(doctype) + frappe.reload_doctype(doctype + " Item") + + if not frappe.db.has_column(doctype, "schedule_date"): + continue + + #Update only submitted MR + for record in frappe.get_all(doctype, filters= [["docstatus", "=", 1]], fields=["name"]): + doc = frappe.get_doc(doctype, record) + if doc.items: + if not doc.schedule_date: + min_schedule_date = min([d.schedule_date for d in doc.items]) + frappe.db.set_value(doctype, record, + "schedule_date", min_schedule_date, update_modified=False) \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 98333c495e..c3e28d21c7 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import json import frappe.utils -from frappe.utils import cstr, flt, getdate, comma_and, cint +from frappe.utils import cstr, flt, getdate, comma_and, cint, nowdate, add_days from frappe import _ from frappe.model.utils import get_fetch_values from frappe.model.mapper import get_mapped_doc diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 4e4b0f7303..9eb2313bb2 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -53,7 +53,7 @@ frappe.ui.form.on("Material Request Item", { set_schedule_date(frm); } } - } + } }); erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.extend({ diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 4e9d290e80..59075c588b 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -7,7 +7,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cstr, flt, getdate, new_line_sep +from frappe.utils import cstr, flt, getdate, new_line_sep, nowdate, add_days from frappe import msgprint, _ from frappe.model.mapper import get_mapped_doc from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty @@ -53,20 +53,6 @@ class MaterialRequest(BuyingController): if actual_so_qty and (flt(so_items[so_no][item]) + already_indented > actual_so_qty): frappe.throw(_("Material Request of maximum {0} can be made for Item {1} against Sales Order {2}").format(actual_so_qty - already_indented, item, so_no)) - def validate_schedule_date(self): - if not self.schedule_date: - self.schedule_date = max([d.schedule_date for d in self.get("items")]) - - if self.schedule_date: - for d in self.get('items'): - if not d.schedule_date: - d.schedule_date = self.schedule_date - - if d.schedule_date and getdate(d.schedule_date) < getdate(self.transaction_date): - frappe.throw(_("Expected Date cannot be before Material Request Date")) - else: - frappe.throw(_("Please enter Schedule Date")) - # Validate # --------------------- def validate(self): @@ -296,7 +282,7 @@ def make_purchase_order_based_on_supplier(source_name, target_doc=None): def postprocess(source, target_doc): target_doc.supplier = source_name - + target_doc.schedule_date = add_days(nowdate(), 1) target_doc.set("items", [d for d in target_doc.get("items") if d.get("item_code") in supplier_items and d.get("qty") > 0]) @@ -329,12 +315,12 @@ def get_material_requests_based_on_supplier(supplier): material_requests = frappe.db.sql_list("""select distinct mr.name from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item where mr.name = mr_item.parent - and mr_item.item_code in (%s) - and mr.material_request_type = 'Purchase' - and mr.per_ordered < 99.99 - and mr.docstatus = 1 - and mr.status != 'Stopped' - order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)), + and mr_item.item_code in (%s) + and mr.material_request_type = 'Purchase' + and mr.per_ordered < 99.99 + and mr.docstatus = 1 + and mr.status != 'Stopped' + order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)), tuple(supplier_items)) else: material_requests = [] diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 1531f4096e..709b8c720c 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -206,6 +206,7 @@ class TestMaterialRequest(unittest.TestCase): po_doc = make_purchase_order(mr.name) po_doc.supplier = "_Test Supplier" po_doc.transaction_date = "2013-07-07" + po_doc.schedule_date = "2013-07-09" po_doc.get("items")[0].qty = 27.0 po_doc.get("items")[1].qty = 1.5 po_doc.get("items")[0].schedule_date = "2013-07-09"