diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 518782d759b..6a6bb226a9e 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -2,6 +2,13 @@ // For license information, please see license.txt frappe.ui.form.on("Pick List", { + after_save(frm) { + setTimeout(() => { + // Added to fix the issue of locations table not getting updated after save + frm.reload_doc(); + }, 500); + }, + setup: (frm) => { frm.ignore_doctypes_on_cancel_all = ["Serial and Batch Bundle"]; diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 4550d71677a..998f15945d3 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -73,6 +73,7 @@ class PickList(Document): self.set_onload("has_reserved_stock", True) def validate(self): + self.validate_expired_batches() self.validate_for_qty() self.validate_stock_qty() self.check_serial_no_status() @@ -205,6 +206,33 @@ class PickList(Document): self.update_reference_qty() self.update_sales_order_picking_status() + def validate_expired_batches(self): + batches = [] + for row in self.get("locations"): + if row.get("batch_no") and row.get("picked_qty"): + batches.append(row.batch_no) + + if batches: + batch = frappe.qb.DocType("Batch") + query = ( + frappe.qb.from_(batch) + .select(batch.name) + .where( + (batch.name.isin(batches)) + & (batch.expiry_date <= frappe.utils.nowdate()) + & (batch.expiry_date.isnotnull()) + ) + ) + + expired_batches = query.run(as_dict=True) + if expired_batches: + msg = "" + + frappe.throw( + _("The following batches are expired, please restock them:
{0}").format(msg), + title=_("Expired Batches"), + ) + def make_bundle_using_old_serial_batch_fields(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -447,6 +475,7 @@ class PickList(Document): self.remove(row) updated_locations = frappe._dict() + len_idx = len(self.get("locations")) or 0 for item_doc in items: item_code = item_doc.item_code @@ -489,6 +518,8 @@ class PickList(Document): if location.picked_qty > location.stock_qty: location.picked_qty = location.stock_qty + len_idx += 1 + location.idx = len_idx self.append("locations", location) # If table is empty on update after submit, set stock_qty, picked_qty to 0 so that indicator is red @@ -497,7 +528,11 @@ class PickList(Document): for location in locations_replica: location.stock_qty = 0 location.picked_qty = 0 + + len_idx += 1 + location.idx = len_idx self.append("locations", location) + frappe.msgprint( _( "Please Restock Items and Update the Pick List to continue. To discontinue, cancel the Pick List." @@ -638,8 +673,31 @@ class PickList(Document): if serial_no: picked_items[item_data.item_code][key]["serial_no"].extend(serial_no) + self.update_picked_item_from_current_pick_list(picked_items) return picked_items + def update_picked_item_from_current_pick_list(self, picked_items): + for row in self.locations: + if flt(row.picked_qty) > 0: + key = (row.warehouse, row.batch_no) if row.batch_no else row.warehouse + serial_no = [x for x in row.serial_no.split("\n") if x] if row.serial_no else None + if row.item_code not in picked_items: + picked_items[row.item_code] = {} + + if key not in picked_items[row.item_code]: + picked_items[row.item_code][key] = frappe._dict( + { + "picked_qty": 0, + "serial_no": [], + "batch_no": row.batch_no or "", + "warehouse": row.warehouse, + } + ) + + picked_items[row.item_code][key]["picked_qty"] += flt(row.stock_qty) or flt(row.picked_qty) + if serial_no: + picked_items[row.item_code][key]["serial_no"].extend(serial_no) + def _get_pick_list_items(self, items): pi = frappe.qb.DocType("Pick List") pi_item = frappe.qb.DocType("Pick List Item") @@ -653,9 +711,11 @@ class PickList(Document): pi_item.batch_no, pi_item.serial_and_batch_bundle, pi_item.serial_no, - (Case().when(pi_item.picked_qty > 0, pi_item.picked_qty).else_(pi_item.stock_qty)).as_( - "picked_qty" - ), + ( + Case() + .when((pi_item.picked_qty > 0) & (pi_item.docstatus == 1), pi_item.picked_qty) + .else_(pi_item.stock_qty) + ).as_("picked_qty"), ) .where( (pi_item.item_code.isin([x.item_code for x in items]))