Repository: frappe/erpnext
Pull request: Stocking out items with expired batches
![stockout](https://user-images.githubusercontent.com/17617465/40606864-5e4f5c7a-6284-11e8-86cf-20b5ff528bd5.gif)

Closes: #12843

Problem statement:
Stocking Out Expired Batched Items
As per the current design, once item is expired, then no stock transaction is allowed for it. We will have to explicitly provide an option for the expired batched items to be stocked-out, so that expense could be booked for them. 

May be allow selection of Expired batches only in the Stock Entry should help?

Hints from issue discussion:

Commit message: Add the fetched item details to stock entry details

Patch to review:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 7452da8ea1b1..be66388c9d44 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -156,6 +156,28 @@ frappe.ui.form.on('Stock Entry', {
 				})
 			}, __("Get items from"));
 		}
+		if (frm.doc.docstatus===0 && frm.doc.purpose == "Material Issue") {
+			frm.add_custom_button(__('Expired Batches'), function() {
+				frappe.call({
+					method: "erpnext.stock.doctype.stock_entry.stock_entry.get_expired_batch_items",
+					callback: function(r) {
+						if (!r.exc && r.message) {
+							frm.set_value("items", []);
+							r.message.forEach(function(element) {
+								let d = frm.add_child("items");
+								d.item_code = element.item;
+								d.s_warehouse = element.warehouse;
+								d.qty = element.qty;
+								d.uom = element.stock_uom;
+								d.conversion_factor = 1;
+								d.transfer_qty = element.qty;
+								frm.refresh_fields();
+							});
+						}
+					}
+				});
+			}, __("Get items from"));
+		}
 
 		if (frm.doc.company) {
 			frm.trigger("toggle_display_account_head");
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 38628548ea01..0f6f432b04c9 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1153,6 +1153,15 @@ def get_uom_details(item_code, uom, qty):
 		}
 	return ret
 
+@frappe.whitelist()
+def get_expired_batch_items():
+	return frappe.db.sql("""select b.item, sum(sle.actual_qty) as qty, sle.warehouse, sle.stock_uom\
+	from `tabBatch` b, `tabStock Ledger Entry` sle
+	where b.expiry_date <= %s
+	and b.expiry_date is not NULL
+	and b.batch_id = sle.batch_no
+	group by sle.warehouse""",(nowdate()), as_dict=1)
+
 @frappe.whitelist()
 def get_warehouse_details(args):
 	if isinstance(args, string_types):


Code review comments:
diff_hunk: @@ -1153,6 +1153,15 @@ def get_uom_details(item_code, uom, qty):
 		}
 	return ret
 
+@frappe.whitelist()
+def get_expired_batch_items():
+	return frappe.db.sql("""select b.item, sum(sle.actual_qty) as qty, sle.warehouse, sle.stock_uom\
path: erpnext/stock/doctype/stock_entry/stock_entry.py
text: @user1:
select batch_no as well, it is required to set in stock_entry

diff_hunk: @@ -1153,6 +1153,15 @@ def get_uom_details(item_code, uom, qty):
 		}
 	return ret
 
+@frappe.whitelist()
+def get_expired_batch_items():
+	return frappe.db.sql("""select b.item, sum(sle.actual_qty) as qty, sle.warehouse, sle.stock_uom\
+	from `tabBatch` b, `tabStock Ledger Entry` sle
+	where b.expiry_date <= %s
+	and b.expiry_date is not NULL
+	and b.batch_id = sle.batch_no
+	group by sle.warehouse""",(nowdate()), as_dict=1)
path: erpnext/stock/doctype/stock_entry/stock_entry.py
text: @user1:
group by sle.warehouse, sle.item_code, sle.batch_no