From 5bd394278d2f08cd1971120160ebbd66a75be62d Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 3 Aug 2015 15:09:48 +0530 Subject: [PATCH] [fix] test-case, warehouse mandatory for mix type product bundle --- .../doctype/product_bundle/product_bundle.py | 8 +++++- .../product_bundle/test_product_bundle.py | 1 - .../doctype/sales_order/sales_order.py | 15 +++++++++-- .../doctype/sales_order/test_sales_order.py | 23 ++++++++++++++--- erpnext/setup/doctype/company/company.py | 25 ++++++++++--------- erpnext/setup/utils.py | 3 +++ erpnext/stock/doctype/item/test_item.py | 8 +++++- 7 files changed, 63 insertions(+), 20 deletions(-) diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.py b/erpnext/selling/doctype/product_bundle/product_bundle.py index fdf6b76db0..2949c5cddf 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/product_bundle.py @@ -13,9 +13,15 @@ class ProductBundle(Document): self.name = self.new_item_code def validate(self): + self.validate_main_item() from erpnext.utilities.transaction_base import validate_uom_is_integer validate_uom_is_integer(self, "uom", "qty") + def validate_main_item(self): + """Validates, main Item is not a stock item""" + if frappe.db.get_value("Item", self.new_item_code, "is_stock_item"): + frappe.throw(_("Parent Item {0} must not be a Stock Item").format(self.new_item_code)) + def get_item_details(self, name): det = frappe.db.sql("""select description, stock_uom from `tabItem` where name = %s""", name) @@ -28,7 +34,7 @@ def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond return frappe.db.sql("""select name, item_name, description from tabItem - where name not in (select name from `tabProduct Bundle`) + where is_stock_item=0 and name not in (select name from `tabProduct Bundle`) and %s like %s %s limit %s, %s""" % (searchfield, "%s", get_match_cond(doctype),"%s", "%s"), ("%%%s%%" % txt, start, page_len)) diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py index 39b17f368d..85a2b209f6 100644 --- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py @@ -13,7 +13,6 @@ def make_product_bundle(parent, items): product_bundle = frappe.get_doc({ "doctype": "Product Bundle", - "parent_item": parent, "new_item_code": parent }) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 109034d14c..1c05715179 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -15,6 +15,8 @@ form_grid_templates = { "items": "templates/form_grid/item_grid.html" } +class WarehouseRequired(frappe.ValidationError): pass + class SalesOrder(SellingController): def validate_mandatory(self): # validate transaction date v/s delivery date @@ -39,8 +41,11 @@ class SalesOrder(SellingController): for d in self.get('items'): check_list.append(cstr(d.item_code)) - if frappe.db.get_value("Item", d.item_code, "is_stock_item") and not d.warehouse: - frappe.throw(_("Delivery warehouse required for stock item {0}").format(d.item_code)) + if (frappe.db.get_value("Item", d.item_code, "is_stock_item")==1 or + (self.has_product_bundle(d.item_code) and self.product_bundle_has_stock_item(d.item_code))) \ + and not d.warehouse: + frappe.throw(_("Delivery warehouse required for stock item {0}").format(d.item_code), + WarehouseRequired) # used for production plan d.transaction_date = self.transaction_date @@ -52,6 +57,12 @@ class SalesOrder(SellingController): if len(unique_chk_list) != len(check_list): frappe.msgprint(_("Warning: Same item has been entered multiple times.")) + def product_bundle_has_stock_item(self, product_bundle): + """Returns true if product bundle has stock item""" + ret = len(frappe.db.sql("""select i.name from tabItem i, `tabProduct Bundle Item` pbi + where pbi.parent = %s and pbi.item_code = i.name and i.is_stock_item = 1""", product_bundle)) + return ret + def validate_sales_mntc_quotation(self): for d in self.get('items'): if d.prevdoc_docname: diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 1ceda1246e..8841202ab3 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -6,7 +6,7 @@ from frappe.utils import flt, add_days import frappe.permissions import unittest from erpnext.selling.doctype.sales_order.sales_order \ - import make_material_request, make_delivery_note, make_sales_invoice + import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired class TestSalesOrder(unittest.TestCase): def tearDown(self): @@ -235,11 +235,24 @@ class TestSalesOrder(unittest.TestCase): make_product_bundle("_Test Service Product Bundle", ["_Test Service Product Bundle Item 1", "_Test Service Product Bundle Item 2"]) - so = make_sales_order(item_code = "_Test Service Product Bundle") + so = make_sales_order(item_code = "_Test Service Product Bundle", warehouse=None) self.assertTrue("_Test Service Product Bundle Item 1" in [d.item_code for d in so.packed_items]) self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items]) + def test_mix_type_product_bundle(self): + from erpnext.stock.doctype.item.test_item import make_item + from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle + + make_item("_Test Mix Product Bundle", {"is_stock_item": 0, "is_sales_item": 1}) + make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1, "is_sales_item": 1}) + make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0, "is_sales_item": 1}) + + make_product_bundle("_Test Mix Product Bundle", + ["_Test Mix Product Bundle Item 1", "_Test Mix Product Bundle Item 2"]) + + self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="") + def test_auto_insert_price(self): from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Item for Auto Price List", {"is_stock_item": 0, "is_sales_item": 1}) @@ -284,13 +297,17 @@ def make_sales_order(**args): if args.selling_price_list: so.selling_price_list = args.selling_price_list + if "warehouse" not in args: + args.warehouse = "_Test Warehouse - _TC" + so.append("items", { "item_code": args.item or args.item_code or "_Test Item", - "warehouse": args.warehouse or "_Test Warehouse - _TC", + "warehouse": args.warehouse, "qty": args.qty or 10, "rate": args.rate or 100, "conversion_factor": 1.0, }) + if not args.do_not_save: so.insert() if not args.do_not_submit: diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index f1f7cc4a5c..5660d892cc 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -30,7 +30,7 @@ class Company(Document): self.abbr = self.abbr.strip() if self.get('__islocal') and len(self.abbr) > 5: frappe.throw(_("Abbreviation cannot have more than 5 characters")) - + if not self.abbr.strip(): frappe.throw(_("Abbr can not be blank or space")) @@ -70,7 +70,8 @@ class Company(Document): frappe.clear_cache() def install_country_fixtures(self): - if os.path.exists(os.path.join(os.path.dirname(__file__), "fixtures", self.country.lower())): + path = os.path.join(os.path.dirname(__file__), "fixtures", self.country.lower()) + if os.path.exists(path.encode("utf-8")): frappe.get_attr("erpnext.setup.doctype.company.fixtures.{0}.install".format(self.country.lower()))(self) def create_default_warehouses(self): @@ -183,7 +184,7 @@ class Company(Document): accounts = frappe.db.sql_list("select name from tabAccount where company=%s", self.name) cost_centers = frappe.db.sql_list("select name from `tabCost Center` where company=%s", self.name) warehouses = frappe.db.sql_list("select name from tabWarehouse where company=%s", self.name) - + rec = frappe.db.sql("SELECT name from `tabGL Entry` where company = %s", self.name) if not rec: # delete Account @@ -202,21 +203,21 @@ class Company(Document): frappe.db.sql("""delete from `tabWarehouse` where company=%s""", self.name) frappe.defaults.clear_default("company", value=self.name) - + # clear default accounts, warehouses from item for f in ["default_warehouse", "website_warehouse"]: - frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)""" + frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)""" % (f, f, ', '.join(['%s']*len(warehouses))), tuple(warehouses)) - - frappe.db.sql("""delete from `tabItem Reorder` where warehouse in (%s)""" + + frappe.db.sql("""delete from `tabItem Reorder` where warehouse in (%s)""" % ', '.join(['%s']*len(warehouses)), tuple(warehouses)) - + for f in ["income_account", "expense_account"]: - frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)""" + frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)""" % (f, f, ', '.join(['%s']*len(accounts))), tuple(accounts)) - + for f in ["selling_cost_center", "buying_cost_center"]: - frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)""" + frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)""" % (f, f, ', '.join(['%s']*len(cost_centers))), tuple(cost_centers)) # reset default company @@ -229,7 +230,7 @@ def replace_abbr(company, old, new): new = new.strip() if not new: frappe.throw(_("Abbr can not be blank or space")) - + frappe.only_for("System Manager") frappe.db.set_value("Company", company, "abbr", new) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index f661edb2b6..5480613b4e 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -56,6 +56,9 @@ def before_tests(): frappe.db.sql("delete from `tabLeave Application`") frappe.db.sql("delete from `tabSalary Slip`") frappe.db.sql("delete from `tabItem Price`") + + frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 0) + frappe.db.commit() @frappe.whitelist() diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 0fb01ac117..510c0d1b48 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -26,7 +26,13 @@ def make_item(item_code, properties=None): if properties: item.update(properties) - item.insert() + + + if item.is_stock_item and not item.default_warehouse: + item.default_warehouse = "_Test Warehouse - _TC" + + item.insert() + return item class TestItem(unittest.TestCase):