From 3cdf3544fb30685cb3d41aae2265f0f353b89f02 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 8 Feb 2016 11:52:22 +0530 Subject: [PATCH] [enhancement] default warehouse not mandatory in time, re-order level and re-order qty must always be set per warehosue --- .../production_order/production_order.py | 6 +- erpnext/patches.txt | 1 + erpnext/patches/v6_21/__init__.py | 0 erpnext/patches/v6_21/fix_reorder_level.py | 24 +++ erpnext/startup/report_data_map.py | 3 +- erpnext/stock/doctype/item/item.js | 7 - erpnext/stock/doctype/item/item.json | 152 +++++------------- erpnext/stock/doctype/item/item.py | 20 +-- erpnext/stock/doctype/item/test_item.py | 13 -- .../material_request/material_request.js | 4 +- erpnext/stock/reorder_item.py | 11 +- .../stock_projected_qty.py | 10 +- 12 files changed, 82 insertions(+), 169 deletions(-) create mode 100644 erpnext/patches/v6_21/__init__.py create mode 100644 erpnext/patches/v6_21/fix_reorder_level.py diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index dbd3250386..59d371a7c1 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -423,6 +423,8 @@ def make_time_log(name, operation, from_time=None, to_time=None, qty=None, proj @frappe.whitelist() def get_default_warehouse(): - wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse") - fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse") + wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", + "default_wip_warehouse") + fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", + "default_fg_warehouse") return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse} diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b73a32e58f..2239f0a48d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -247,3 +247,4 @@ erpnext.patches.v6_16.create_manufacturer_records execute:frappe.db.sql("update `tabPricing Rule` set title=name where title='' or title is null") #2016-01-27 erpnext.patches.v6_20.set_party_account_currency_in_orders erpnext.patches.v6_19.comment_feed_communication +erpnext.patches.v6_21.fix_reorder_level diff --git a/erpnext/patches/v6_21/__init__.py b/erpnext/patches/v6_21/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/patches/v6_21/fix_reorder_level.py b/erpnext/patches/v6_21/fix_reorder_level.py new file mode 100644 index 0000000000..602978b161 --- /dev/null +++ b/erpnext/patches/v6_21/fix_reorder_level.py @@ -0,0 +1,24 @@ +from __future__ import unicode_literals + +import frappe +from erpnext.stock.doctype.item.item import DuplicateReorderRows + +def execute(): + if frappe.db.has_column("Item", "re_order_level"): + for item in frappe.db.sql("""select name, default_warehouse, re_order_level, re_order_qty + from tabItem + where ifnull(re_order_level, 0) != 0 + and ifnull(re_order_qty, 0) != 0""", as_dict=1): + + item_doc = frappe.get_doc("Item", item.name) + item_doc.append("reorder_levels", { + "warehouse": item.default_warehouse, + "warehouse_reorder_level": item.re_order_level, + "warehouse_reorder_qty": item.re_order_qty, + "material_request_type": "Purchase" if item_doc.is_purchase_item else "Transfer" + }) + + try: + item_doc.save() + except DuplicateReorderRows: + pass diff --git a/erpnext/startup/report_data_map.py b/erpnext/startup/report_data_map.py index 7e209b65d9..4dc326eebe 100644 --- a/erpnext/startup/report_data_map.py +++ b/erpnext/startup/report_data_map.py @@ -46,8 +46,7 @@ data_map = { # Stock "Item": { "columns": ["name", "if(item_name=name, '', item_name) as item_name", "description", - "item_group as parent_item_group", "stock_uom", "brand", "valuation_method", - "re_order_level", "re_order_qty"], + "item_group as parent_item_group", "stock_uom", "brand", "valuation_method"], # "conditions": ["docstatus < 2"], "order_by": "name", "links": { diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 9ce603cb4b..a691f2f9d2 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -72,8 +72,6 @@ frappe.ui.form.on("Item", { (frm.doc.__onload && frm.doc.__onload.sle_exists=="exists") ? false : true); } - erpnext.item.toggle_reqd(frm); - erpnext.item.toggle_attributes(frm); }, @@ -102,7 +100,6 @@ frappe.ui.form.on("Item", { }, is_stock_item: function(frm) { - erpnext.item.toggle_reqd(frm); if(frm.doc.is_pro_applicable && !frm.doc.is_stock_item) frm.set_value("is_pro_applicable", 0); }, @@ -180,10 +177,6 @@ $.extend(erpnext.item, { }, - toggle_reqd: function(frm) { - frm.toggle_reqd("default_warehouse", frm.doc.is_stock_item); - }, - make_dashboard: function(frm) { frm.dashboard.reset(); if(frm.doc.__islocal) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 79789b014a..96b4350918 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -790,108 +790,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "depends_on": "eval:(doc.is_stock_item && !doc.apply_warehouse_wise_reorder_level)", - "description": "Automatically create Material Request if quantity falls below this level", - "fieldname": "re_order_level", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Re-order Level", - "length": 0, - "no_copy": 0, - "oldfieldname": "re_order_level", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "depends_on": "eval:(doc.is_stock_item && !doc.apply_warehouse_wise_reorder_level)", - "fieldname": "re_order_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Re-order Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "depends_on": "is_stock_item", - "fieldname": "apply_warehouse_wise_reorder_level", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Apply Warehouse-wise Reorder Level", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "reorder_levels", - "depends_on": "eval:(doc.is_stock_item && doc.apply_warehouse_wise_reorder_level)", - "fieldname": "section_break_31", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Reorder level based on Warehouse", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "depends_on": "eval:(doc.is_stock_item && doc.apply_warehouse_wise_reorder_level)", + "depends_on": "", "description": "Will also apply for variants unless overrridden", "fieldname": "reorder_levels", "fieldtype": "Table", @@ -1463,6 +1362,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "default": "", + "depends_on": "eval:doc.is_sales_item", + "description": "Allow in Sales Order of type \"Service\"", + "fieldname": "is_service_item", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 1, + "in_list_view": 0, + "label": "Is Service Item", + "length": 0, + "no_copy": 0, + "oldfieldname": "is_service_item", + "oldfieldtype": "Select", + "options": "", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -2309,7 +2237,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2016-01-26 05:31:58.950718", + "modified": "2016-02-08 01:15:52.177625", "modified_by": "Administrator", "module": "Stock", "name": "Item", @@ -2329,7 +2257,7 @@ "print": 1, "read": 1, "report": 1, - "role": "Material Master Manager", + "role": "Item Manager", "set_user_permissions": 0, "share": 1, "submit": 0, @@ -2349,7 +2277,7 @@ "print": 1, "read": 1, "report": 1, - "role": "Material Manager", + "role": "Stock Manager", "set_user_permissions": 0, "share": 0, "submit": 0, @@ -2357,7 +2285,7 @@ }, { "amend": 0, - "apply_user_permissions": 1, + "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -2369,7 +2297,7 @@ "print": 1, "read": 1, "report": 1, - "role": "Material User", + "role": "Stock User", "set_user_permissions": 0, "share": 0, "submit": 0, @@ -2377,7 +2305,7 @@ }, { "amend": 0, - "apply_user_permissions": 1, + "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -2397,7 +2325,7 @@ }, { "amend": 0, - "apply_user_permissions": 1, + "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -2417,7 +2345,7 @@ }, { "amend": 0, - "apply_user_permissions": 1, + "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -2437,7 +2365,7 @@ }, { "amend": 0, - "apply_user_permissions": 1, + "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, @@ -2457,7 +2385,7 @@ }, { "amend": 0, - "apply_user_permissions": 1, + "apply_user_permissions": 0, "cancel": 0, "create": 0, "delete": 0, diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 3e3c13e5c0..f18bc8bbe9 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -14,7 +14,7 @@ from frappe.website.render import clear_cache from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow from erpnext.controllers.item_variant import get_variant, copy_attributes_to_variant, ItemVariantExistsError -class WarehouseNotSet(frappe.ValidationError): pass +class DuplicateReorderRows(frappe.ValidationError): pass class Item(WebsiteGenerator): website = frappe._dict( @@ -58,7 +58,6 @@ class Item(WebsiteGenerator): if not self.stock_uom: msgprint(_("Please enter default Unit of Measure"), raise_exception=1) - self.check_warehouse_is_set_for_stock_item() self.validate_uom() self.add_default_uom_in_conversion_factor_table() self.validate_conversion_factor() @@ -303,11 +302,6 @@ class Item(WebsiteGenerator): if not find_variant(combination): context.disabled_attributes.setdefault(attr.attribute, []).append(combination[-1]) - def check_warehouse_is_set_for_stock_item(self): - if self.is_stock_item==1 and not self.default_warehouse and frappe.get_all("Warehouse"): - frappe.msgprint(_("Default Warehouse is mandatory for stock Item."), - raise_exception=WarehouseNotSet) - def add_default_uom_in_conversion_factor_table(self): uom_conv_list = [d.uom for d in self.get("uoms")] if self.stock_uom not in uom_conv_list: @@ -409,22 +403,14 @@ class Item(WebsiteGenerator): you can not change the values of 'Has Serial No', 'Has Batch No', 'Is Stock Item' and 'Valuation Method'")) def validate_reorder_level(self): - if cint(self.apply_warehouse_wise_reorder_level): - self.re_order_level, self.re_order_qty = 0, 0 - else: - self.set("reorder_levels", []) - - if self.re_order_level or len(self.get("reorder_levels", {"material_request_type": "Purchase"})): + if len(self.get("reorder_levels", {"material_request_type": "Purchase"})): if not (self.is_purchase_item or self.is_pro_applicable): frappe.throw(_("""To set reorder level, item must be a Purchase Item or Manufacturing Item""")) - if self.re_order_level and not self.re_order_qty: - frappe.throw(_("Please set reorder quantity")) for d in self.get("reorder_levels"): if d.warehouse_reorder_level and not d.warehouse_reorder_qty: frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx)) - def validate_warehouse_for_reorder(self): warehouse = [] for i in self.get("reorder_levels"): @@ -432,7 +418,7 @@ class Item(WebsiteGenerator): warehouse += [i.get("warehouse")] else: frappe.throw(_("Row {0}: An Reorder entry already exists for this warehouse {1}") - .format(i.idx, i.warehouse)) + .format(i.idx, i.warehouse), DuplicateReorderRows) def check_if_sle_exists(self): sle = frappe.db.sql("""select name from `tabStock Ledger Entry` diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 51d10795a1..a66362df4f 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -6,7 +6,6 @@ import unittest import frappe from frappe.test_runner import make_test_records -from erpnext.stock.doctype.item.item import WarehouseNotSet from erpnext.controllers.item_variant import create_variant, ItemVariantExistsError, InvalidItemAttributeValueError test_ignore = ["BOM"] @@ -45,18 +44,6 @@ class TestItem(unittest.TestCase): item = frappe.get_doc("Item", item_code) return item - # def test_template_cannot_have_stock(self): - # item = self.get_item(10) - # make_stock_entry(item_code=item.name, target="Stores - _TC", qty=1, basic_rate=1) - # item.has_variants = 1 - # self.assertRaises(ItemTemplateCannotHaveStock, item.save) - - def test_default_warehouse(self): - item = frappe.copy_doc(test_records[0]) - item.is_stock_item = 1 - item.default_warehouse = None - self.assertRaises(WarehouseNotSet, item.insert) - def test_get_item_details(self): from erpnext.stock.get_item_details import get_item_details to_check = { diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 0bb4815fff..922eb5a435 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -102,6 +102,8 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten fields: [ {"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"), options:"BOM", reqd: 1}, + {"fieldname":"warehouse", "fieldtype":"Link", "label":__("Warehouse"), + options:"Warehouse", reqd: 1, label:"For Warehouse"}, {"fieldname":"fetch_exploded", "fieldtype":"Check", "label":__("Fetch exploded BOM (including sub-assemblies)"), "default":1}, {fieldname:"fetch", "label":__("Get Items from BOM"), "fieldtype":"Button"} @@ -119,7 +121,7 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten var d = frappe.model.add_child(cur_frm.doc, "Material Request Item", "items"); d.item_code = item.item_code; d.description = item.description; - d.warehouse = item.default_warehouse; + d.warehouse = d.warehouse; d.uom = item.stock_uom; d.qty = item.qty; }); diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index 9b51bfd131..cca2456d8f 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -2,7 +2,7 @@ # License: GNU General Public License v3. See license.txt import frappe -from frappe.utils import flt, cstr, nowdate, add_days, cint +from frappe.utils import flt, nowdate, add_days, cint from frappe import _ def reorder_item(): @@ -26,10 +26,9 @@ def _reorder_item(): and (is_purchase_item=1 or is_sub_contracted_item=1) and disabled=0 and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s) - and ((re_order_level is not null and re_order_level > 0) - or exists (select name from `tabItem Reorder` ir where ir.parent=item.name) + and (exists (select name from `tabItem Reorder` ir where ir.parent=item.name) or (variant_of is not null and variant_of != '' - and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of)) + and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of)) )""", {"today": nowdate()}) @@ -73,10 +72,6 @@ def _reorder_item(): add_to_material_request(item_code, d.warehouse, d.warehouse_reorder_level, d.warehouse_reorder_qty, d.material_request_type) - else: - # raise for default warehouse - add_to_material_request(item_code, item.default_warehouse, item.re_order_level, item.re_order_qty, "Purchase") - if material_requests: return create_material_request(material_requests) diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index aef6c99121..fd9ff5d585 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -42,19 +42,15 @@ def get_data(filters): re_order_level = re_order_qty = 0 - if bin.warehouse==item.default_warehouse: - re_order_level = item.re_order_level or 0 - re_order_qty = item.re_order_qty or 0 - for d in item.get("reorder_levels"): if d.warehouse == bin.warehouse: re_order_level = d.warehouse_reorder_level re_order_qty = d.warehouse_reorder_qty - + shortage_qty = re_order_level - flt(bin.projected_qty) if re_order_level else 0 - data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse, - item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty, + data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse, + item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty, bin.reserved_qty, bin.projected_qty, re_order_level, re_order_qty, shortage_qty]) return data