diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 39172d7d11..bb1910720d 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -9,6 +9,7 @@ from frappe.utils.nestedset import NestedSet from frappe.website.website_generator import WebsiteGenerator from frappe.website.render import clear_cache from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow +from erpnext.utilities.product import get_qty_in_stock class ItemGroup(NestedSet, WebsiteGenerator): @@ -83,7 +84,8 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non # base query query = """select I.name, I.item_name, I.item_code, I.route, I.image, I.website_image, I.thumbnail, I.item_group, I.description, I.web_long_description as website_description, I.is_stock_item, - case when (S.actual_qty - S.reserved_qty) > 0 then 1 else 0 end as in_stock + case when (S.actual_qty - S.reserved_qty) > 0 then 1 else 0 end as in_stock, I.website_warehouse, + I.has_batch_no from `tabItem` I left join tabBin S on I.item_code = S.item_code and I.website_warehouse = S.warehouse where I.show_in_website = 1 @@ -104,8 +106,26 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1) + data = adjust_for_expired_items(data) + return [get_item_for_list_in_html(r) for r in data] + +def adjust_for_expired_items(data): + adjusted_data = [] + + for item in data: + if item.get('has_batch_no') and item.get('website_warehouse'): + stock_qty_dict = get_qty_in_stock( + item.get('name'), 'website_warehouse', item.get('website_warehouse')) + qty = stock_qty_dict.stock_qty[0][0] + item['in_stock'] = 1 if qty else 0 + adjusted_data.append(item) + + return adjusted_data + + + def get_child_groups(item_group_name): item_group = frappe.get_doc("Item Group", item_group_name) return frappe.db.sql("""select name diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 9d5c056ba7..f088e3267d 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -4,14 +4,17 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint, fmt_money, flt +from frappe.utils import cint, fmt_money, flt, nowdate, getdate from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item +from erpnext.stock.doctype.batch.batch import get_batch_qty -def get_qty_in_stock(item_code, item_warehouse_field): +def get_qty_in_stock(item_code, item_warehouse_field, warehouse=None): in_stock, stock_qty = 0, '' template_item_code, is_stock_item = frappe.db.get_value("Item", item_code, ["variant_of", "is_stock_item"]) - warehouse = frappe.db.get_value("Item", item_code, item_warehouse_field) + if not warehouse: + warehouse = frappe.db.get_value("Item", item_code, item_warehouse_field) + if not warehouse and template_item_code and template_item_code != item_code: warehouse = frappe.db.get_value("Item", template_item_code, item_warehouse_field) @@ -21,8 +24,49 @@ def get_qty_in_stock(item_code, item_warehouse_field): if stock_qty: in_stock = stock_qty[0][0] > 0 and 1 or 0 + if stock_qty[0][0]: + stock_qty = adjust_for_expired_items(item_code, stock_qty, warehouse) + + if stock_qty[0][0]: + in_stock = stock_qty[0][0] > 0 and 1 or 0 + return frappe._dict({"in_stock": in_stock, "stock_qty": stock_qty, "is_stock_item": is_stock_item}) + +def adjust_for_expired_items(item_code, stock_qty, warehouse): + batches = frappe.get_list('Batch', filters=[{'item': item_code}], fields=['expiry_date', 'name']) + expired_batches = get_expired_batches(batches) + stock_qty = [list(item) for item in stock_qty] + + if expired_batches: + for batch in expired_batches: + if warehouse: + stock_qty[0][0] = max(0, stock_qty[0][0] - get_batch_qty(batch, warehouse)) + else: + stock_qty[0][0] = max(0, stock_qty[0][0] - qty_from_all_warehouses(get_batch_qty(batch))) + + if not stock_qty[0][0]: + break + return stock_qty + + +def get_expired_batches(batches): + """ + :param batches: A list of dict in the form [{'expiry_date': datetime.date(20XX, 1, 1), 'name': 'batch_id'}, ...] + """ + return [b.name for b in batches if b.expiry_date and b.expiry_date <= getdate(nowdate())] + + +def qty_from_all_warehouses(batch_info): + """ + :param batch_info: A list of dict in the form [{u'warehouse': u'Stores - I', u'qty': 0.8}, ...] + """ + qty = 0 + for batch in batch_info: + qty = qty + batch.qty + + return qty + def get_price(item_code, price_list, customer_group, company, qty=1): template_item_code = frappe.db.get_value("Item", item_code, "variant_of")