From f214bfcfc3ab83b351eb48bd13d2352948c446ba Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 16 Jul 2014 17:52:08 +0530 Subject: [PATCH 1/3] Fixes in load defaults of transaction.js --- erpnext/public/js/transaction.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/public/js/transaction.js b/erpnext/public/js/transaction.js index 1790a47252..ae5864d491 100644 --- a/erpnext/public/js/transaction.js +++ b/erpnext/public/js/transaction.js @@ -20,16 +20,13 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ currency: currency, price_list_currency: currency, status: "Draft", - fiscal_year: frappe.defaults.get_user_default("fiscal_year"), is_subcontracted: "No", }, function(fieldname, value) { if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) me.frm.set_value(fieldname, value); }); - if(!this.frm.doc.company) { - this.frm.set_value("company", frappe.defaults.get_user_default("company")); - } else { + if(this.frm.doc.company) { cur_frm.script_manager.trigger("company"); } } @@ -332,7 +329,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule", args: { args: this._get_args(item) }, callback: function(r) { - if (!r.exc) { + if (!r.exc && r.message) { me._set_values_for_item_list(r.message); if(calculate_taxes_and_totals) me.calculate_taxes_and_totals(); } From 4b5e89a067646648f4827bd586cf12e6669b52e9 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 16 Jul 2014 19:23:58 +0530 Subject: [PATCH 2/3] Fixes in Stock Entry test cases --- .../doctype/stock_entry/test_stock_entry.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 260223d4b8..1fdc016d7d 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -21,7 +21,7 @@ class TestStockEntry(unittest.TestCase): frappe.db.sql("""delete from `tabMaterial Request`""") self._clear_stock_account_balance() - frappe.db.set_value("Stock Settings", None, "auto_indent", True) + frappe.db.set_value("Stock Settings", None, "auto_indent", 1) st1 = frappe.copy_doc(test_records[0]) st1.insert() @@ -664,15 +664,17 @@ class TestStockEntry(unittest.TestCase): def test_serial_no_not_exists(self): self._clear_stock_account_balance() frappe.db.sql("delete from `tabSerial No` where name in ('ABCD', 'EFGH')") + make_serialized_item() se = frappe.copy_doc(test_records[0]) se.purpose = "Material Issue" - se.get("mtn_details")[0].item_code = "_Test Serialized Item" + se.get("mtn_details")[0].item_code = "_Test Serialized Item With Series" se.get("mtn_details")[0].qty = 2 se.get("mtn_details")[0].s_warehouse = "_Test Warehouse 1 - _TC" se.get("mtn_details")[0].t_warehouse = None se.get("mtn_details")[0].serial_no = "ABCD\nEFGH" se.get("mtn_details")[0].transfer_qty = 2 se.insert() + self.assertRaises(SerialNoNotExistsError, se.submit) def test_serial_duplicate(self): @@ -699,8 +701,8 @@ class TestStockEntry(unittest.TestCase): return se, serial_nos def test_serial_item_error(self): - self._clear_stock_account_balance() se, serial_nos = self.test_serial_by_series() + make_serialized_item("_Test Serialized Item", "ABCD\nEFGH") se = frappe.copy_doc(test_records[0]) se.purpose = "Material Transfer" @@ -735,6 +737,8 @@ class TestStockEntry(unittest.TestCase): def test_serial_warehouse_error(self): self._clear_stock_account_balance() + make_serialized_item(target_warehouse="_Test Warehouse 1 - _TC") + t = make_serialized_item() serial_nos = get_serial_nos(t.get("mtn_details")[0].serial_no) @@ -818,11 +822,16 @@ class TestStockEntry(unittest.TestCase): self.assertRaises (StockFreezeError, se.submit) frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0) -def make_serialized_item(): +def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None): se = frappe.copy_doc(test_records[0]) - se.get("mtn_details")[0].item_code = "_Test Serialized Item With Series" + se.get("mtn_details")[0].item_code = item_code or "_Test Serialized Item With Series" + se.get("mtn_details")[0].serial_no = serial_no se.get("mtn_details")[0].qty = 2 se.get("mtn_details")[0].transfer_qty = 2 + + if target_warehouse: + se.get("mtn_details")[0].t_warehouse = target_warehouse + se.insert() se.submit() return se From b8189d7d2337ecd33a7ee3cbbce78d204acf4e5a Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Wed, 16 Jul 2014 19:24:53 +0530 Subject: [PATCH 3/3] Auto Re-order Item for default warehouse only, if Warehouse wise re-order not specified --- erpnext/stock/utils.py | 95 ++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 340e5511ff..10f5cc2776 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -176,7 +176,6 @@ def get_buying_amount(voucher_type, voucher_no, item_row, stock_ledger_entries): def reorder_item(): """ Reorder item if stock reaches reorder level""" - # if initial setup not completed, return if not frappe.db.sql("select name from `tabFiscal Year` limit 1"): return @@ -185,46 +184,69 @@ def reorder_item(): frappe.local.auto_indent = cint(frappe.db.get_value('Stock Settings', None, 'auto_indent')) if frappe.local.auto_indent: - material_requests = {} - bin_list = frappe.db.sql("""select item_code, warehouse, projected_qty - from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != '' - and exists (select name from `tabItem` - where `tabItem`.name = `tabBin`.item_code and - is_stock_item='Yes' and (is_purchase_item='Yes' or is_sub_contracted_item='Yes') and - (ifnull(end_of_life, '0000-00-00')='0000-00-00' or end_of_life > curdate()))""", - as_dict=True) + _reorder_item() - for bin in bin_list: - #check if re-order is required - item_reorder = frappe.db.get("Item Reorder", - {"parent": bin.item_code, "warehouse": bin.warehouse}) - if item_reorder: - reorder_level = item_reorder.warehouse_reorder_level - reorder_qty = item_reorder.warehouse_reorder_qty - material_request_type = item_reorder.material_request_type or "Purchase" - else: - reorder_level, reorder_qty = frappe.db.get_value("Item", bin.item_code, - ["re_order_level", "re_order_qty"]) - material_request_type = "Purchase" +def _reorder_item(): + # {"Purchase": {"Company": [{"item_code": "", "warehouse": "", "reorder_qty": 0.0}]}, "Transfer": {...}} + material_requests = {"Purchase": {}, "Transfer": {}} - if flt(reorder_level) and flt(bin.projected_qty) < flt(reorder_level): - if flt(reorder_level) - flt(bin.projected_qty) > flt(reorder_qty): - reorder_qty = flt(reorder_level) - flt(bin.projected_qty) + item_warehouse_projected_qty = get_item_warehouse_projected_qty() + warehouse_company = frappe._dict(frappe.db.sql("""select name, company from `tabWarehouse`""")) + default_company = (frappe.defaults.get_defaults()["company"] or + frappe.db.sql("""select name from tabCompany limit 1""")[0][0]) - company = frappe.db.get_value("Warehouse", bin.warehouse, "company") or \ - frappe.defaults.get_defaults()["company"] or \ - frappe.db.sql("""select name from tabCompany limit 1""")[0][0] + def add_to_material_request(item_code, warehouse, reorder_level, reorder_qty, material_request_type): + if warehouse not in item_warehouse_projected_qty[item_code]: + # likely a disabled warehouse or a warehouse where BIN does not exist + return - material_requests.setdefault(material_request_type, frappe._dict()).setdefault( - company, []).append(frappe._dict({ - "item_code": bin.item_code, - "warehouse": bin.warehouse, - "reorder_qty": reorder_qty - }) - ) + reorder_level = flt(reorder_level) + reorder_qty = flt(reorder_qty) + projected_qty = item_warehouse_projected_qty[item_code][warehouse] - if material_requests: - create_material_request(material_requests) + if reorder_level and projected_qty < reorder_level: + deficiency = reorder_level - projected_qty + if deficiency > reorder_qty: + reorder_qty = deficiency + + company = warehouse_company.get(warehouse) or default_company + + material_requests[material_request_type].setdefault(company, []).append({ + "item_code": item_code, + "warehouse": warehouse, + "reorder_qty": reorder_qty + }) + + for item_code in item_warehouse_projected_qty: + item = frappe.get_doc("Item", item_code) + if item.get("item_reorder"): + for d in item.get("item_reorder"): + 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: + create_material_request(material_requests) + +def get_item_warehouse_projected_qty(): + item_warehouse_projected_qty = {} + + for item_code, warehouse, projected_qty in frappe.db.sql("""select item_code, warehouse, projected_qty + from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != '' + and exists (select name from `tabItem` + where `tabItem`.name = `tabBin`.item_code and + is_stock_item='Yes' and (is_purchase_item='Yes' or is_sub_contracted_item='Yes') and + (ifnull(end_of_life, '0000-00-00')='0000-00-00' or end_of_life > %s)) + and exists (select name from `tabWarehouse` + where `tabWarehouse`.name = `tabBin`.warehouse + and ifnull(disabled, 0)=0)""", nowdate()): + + item_warehouse_projected_qty.setdefault(item_code, {})[warehouse] = flt(projected_qty) + + return item_warehouse_projected_qty def create_material_request(material_requests): """ Create indent on reaching reorder level """ @@ -263,6 +285,7 @@ def create_material_request(material_requests): }) for d in items: + d = frappe._dict(d) item = frappe.get_doc("Item", d.item_code) mr.append("indent_details", { "doctype": "Material Request Item",