Merge pull request #33715 from s-aga-r/fix-pick-list

fix: consider existing pick-list
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 6744d16..698ffac 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -325,3 +325,4 @@
 erpnext.patches.v14_0.create_accounting_dimensions_for_payment_request
 erpnext.patches.v14_0.update_entry_type_for_journal_entry
 erpnext.patches.v14_0.change_autoname_for_tax_withheld_vouchers
+erpnext.patches.v14_0.set_pick_list_status
diff --git a/erpnext/patches/v14_0/set_pick_list_status.py b/erpnext/patches/v14_0/set_pick_list_status.py
new file mode 100644
index 0000000..eea5745
--- /dev/null
+++ b/erpnext/patches/v14_0/set_pick_list_status.py
@@ -0,0 +1,40 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# License: MIT. See LICENSE
+
+
+import frappe
+from pypika.terms import ExistsCriterion
+
+
+def execute():
+	pl = frappe.qb.DocType("Pick List")
+	se = frappe.qb.DocType("Stock Entry")
+	dn = frappe.qb.DocType("Delivery Note")
+
+	(
+		frappe.qb.update(pl).set(
+			pl.status,
+			(
+				frappe.qb.terms.Case()
+				.when(pl.docstatus == 0, "Draft")
+				.when(pl.docstatus == 2, "Cancelled")
+				.else_("Completed")
+			),
+		)
+	).run()
+
+	(
+		frappe.qb.update(pl)
+		.set(pl.status, "Open")
+		.where(
+			(
+				ExistsCriterion(
+					frappe.qb.from_(se).select(se.name).where((se.docstatus == 1) & (se.pick_list == pl.name))
+				)
+				| ExistsCriterion(
+					frappe.qb.from_(dn).select(dn.name).where((dn.docstatus == 1) & (dn.pick_list == pl.name))
+				)
+			).negate()
+			& (pl.docstatus == 1)
+		)
+	).run()
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index a1df764..9f9f5cb 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -228,6 +228,7 @@
 
 	def on_submit(self):
 		self.validate_packed_qty()
+		self.update_pick_list_status()
 
 		# Check for Approving Authority
 		frappe.get_doc("Authorization Control").validate_approving_authority(
@@ -313,6 +314,11 @@
 		if has_error:
 			raise frappe.ValidationError
 
+	def update_pick_list_status(self):
+		from erpnext.stock.doctype.pick_list.pick_list import update_pick_list_status
+
+		update_pick_list_status(self.pick_list)
+
 	def check_next_docstatus(self):
 		submit_rv = frappe.db.sql(
 			"""select t1.name
diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json
index e1c3f0f..7259dc0 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.json
+++ b/erpnext/stock/doctype/pick_list/pick_list.json
@@ -26,7 +26,8 @@
   "locations",
   "amended_from",
   "print_settings_section",
-  "group_same_items"
+  "group_same_items",
+  "status"
  ],
  "fields": [
   {
@@ -168,11 +169,26 @@
    "fieldtype": "Data",
    "label": "Customer Name",
    "read_only": 1
+  },
+  {
+   "default": "Draft",
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "in_standard_filter": 1,
+   "label": "Status",
+   "no_copy": 1,
+   "options": "Draft\nOpen\nCompleted\nCancelled",
+   "print_hide": 1,
+   "read_only": 1,
+   "report_hide": 1,
+   "reqd": 1,
+   "search_index": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2022-07-19 11:03:04.442174",
+ "modified": "2023-01-24 10:33:43.244476",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Pick List",
@@ -244,4 +260,4 @@
  "sort_order": "DESC",
  "states": [],
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 808f19e..bf3b5dd 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -11,7 +11,8 @@
 from frappe.model.document import Document
 from frappe.model.mapper import map_child_doc
 from frappe.query_builder import Case
-from frappe.query_builder.functions import IfNull, Locate, Sum
+from frappe.query_builder.custom import GROUP_CONCAT
+from frappe.query_builder.functions import Coalesce, IfNull, Locate, Replace, Sum
 from frappe.utils import cint, floor, flt, today
 from frappe.utils.nestedset import get_descendants_of
 
@@ -77,15 +78,32 @@
 				)
 
 	def on_submit(self):
+		self.update_status()
 		self.update_bundle_picked_qty()
 		self.update_reference_qty()
 		self.update_sales_order_picking_status()
 
 	def on_cancel(self):
+		self.update_status()
 		self.update_bundle_picked_qty()
 		self.update_reference_qty()
 		self.update_sales_order_picking_status()
 
+	def update_status(self, status=None, update_modified=True):
+		if not status:
+			if self.docstatus == 0:
+				status = "Draft"
+			elif self.docstatus == 1:
+				if self.status == "Draft":
+					status = "Open"
+				elif target_document_exists(self.name, self.purpose):
+					status = "Completed"
+			elif self.docstatus == 2:
+				status = "Cancelled"
+
+		if status:
+			frappe.db.set_value("Pick List", self.name, "status", status, update_modified=update_modified)
+
 	def update_reference_qty(self):
 		packed_items = []
 		so_items = []
@@ -162,6 +180,7 @@
 	def set_item_locations(self, save=False):
 		self.validate_for_qty()
 		items = self.aggregate_item_qty()
+		picked_items_details = self.get_picked_items_details(items)
 		self.item_location_map = frappe._dict()
 
 		from_warehouses = None
@@ -180,7 +199,11 @@
 			self.item_location_map.setdefault(
 				item_code,
 				get_available_item_locations(
-					item_code, from_warehouses, self.item_count_map.get(item_code), self.company
+					item_code,
+					from_warehouses,
+					self.item_count_map.get(item_code),
+					self.company,
+					picked_item_details=picked_items_details.get(item_code),
 				),
 			)
 
@@ -309,6 +332,56 @@
 				already_picked + (picked_qty * (1 if self.docstatus == 1 else -1)),
 			)
 
+	def get_picked_items_details(self, items):
+		picked_items = frappe._dict()
+
+		if items:
+			pi = frappe.qb.DocType("Pick List")
+			pi_item = frappe.qb.DocType("Pick List Item")
+			query = (
+				frappe.qb.from_(pi)
+				.inner_join(pi_item)
+				.on(pi.name == pi_item.parent)
+				.select(
+					pi_item.item_code,
+					pi_item.warehouse,
+					pi_item.batch_no,
+					Sum(Case().when(pi_item.picked_qty > 0, pi_item.picked_qty).else_(pi_item.stock_qty)).as_(
+						"picked_qty"
+					),
+					Replace(GROUP_CONCAT(pi_item.serial_no), ",", "\n").as_("serial_no"),
+				)
+				.where(
+					(pi_item.item_code.isin([x.item_code for x in items]))
+					& ((pi_item.picked_qty > 0) | (pi_item.stock_qty > 0))
+					& (pi.status != "Completed")
+					& (pi_item.docstatus != 2)
+				)
+				.groupby(
+					pi_item.item_code,
+					pi_item.warehouse,
+					pi_item.batch_no,
+				)
+			)
+
+			if self.name:
+				query = query.where(pi_item.parent != self.name)
+
+			items_data = query.run(as_dict=True)
+
+			for item_data in items_data:
+				key = (item_data.warehouse, item_data.batch_no) if item_data.batch_no else item_data.warehouse
+				serial_no = [x for x in item_data.serial_no.split("\n") if x] if item_data.serial_no else None
+				data = {"picked_qty": item_data.picked_qty}
+				if serial_no:
+					data["serial_no"] = serial_no
+				if item_data.item_code not in picked_items:
+					picked_items[item_data.item_code] = {key: data}
+				else:
+					picked_items[item_data.item_code][key] = data
+
+		return picked_items
+
 	def _get_product_bundles(self) -> Dict[str, str]:
 		# Dict[so_item_row: item_code]
 		product_bundles = {}
@@ -346,29 +419,30 @@
 		return int(flt(min(possible_bundles), precision or 6))
 
 
+def update_pick_list_status(pick_list):
+	if pick_list:
+		doc = frappe.get_doc("Pick List", pick_list)
+		doc.run_method("update_status")
+
+
 def get_picked_items_qty(items) -> List[Dict]:
-	return frappe.db.sql(
-		f"""
-		SELECT
-			sales_order_item,
-			item_code,
-			sales_order,
-			SUM(stock_qty) AS stock_qty,
-			SUM(picked_qty) AS picked_qty
-		FROM
-			`tabPick List Item`
-		WHERE
-			sales_order_item IN (
-				{", ".join(frappe.db.escape(d) for d in items)}
-			)
-			AND docstatus = 1
-		GROUP BY
-			sales_order_item,
-			sales_order
-		FOR UPDATE
-	""",
-		as_dict=1,
-	)
+	pi_item = frappe.qb.DocType("Pick List Item")
+	return (
+		frappe.qb.from_(pi_item)
+		.select(
+			pi_item.sales_order_item,
+			pi_item.item_code,
+			pi_item.sales_order,
+			Sum(pi_item.stock_qty).as_("stock_qty"),
+			Sum(pi_item.picked_qty).as_("picked_qty"),
+		)
+		.where((pi_item.docstatus == 1) & (pi_item.sales_order_item.isin(items)))
+		.groupby(
+			pi_item.sales_order_item,
+			pi_item.sales_order,
+		)
+		.for_update()
+	).run(as_dict=True)
 
 
 def validate_item_locations(pick_list):
@@ -434,31 +508,38 @@
 
 
 def get_available_item_locations(
-	item_code, from_warehouses, required_qty, company, ignore_validation=False
+	item_code,
+	from_warehouses,
+	required_qty,
+	company,
+	ignore_validation=False,
+	picked_item_details=None,
 ):
 	locations = []
+	total_picked_qty = (
+		sum([v.get("picked_qty") for k, v in picked_item_details.items()]) if picked_item_details else 0
+	)
 	has_serial_no = frappe.get_cached_value("Item", item_code, "has_serial_no")
 	has_batch_no = frappe.get_cached_value("Item", item_code, "has_batch_no")
 
 	if has_batch_no and has_serial_no:
 		locations = get_available_item_locations_for_serial_and_batched_item(
-			item_code, from_warehouses, required_qty, company
+			item_code, from_warehouses, required_qty, company, total_picked_qty
 		)
 	elif has_serial_no:
 		locations = get_available_item_locations_for_serialized_item(
-			item_code, from_warehouses, required_qty, company
+			item_code, from_warehouses, required_qty, company, total_picked_qty
 		)
 	elif has_batch_no:
 		locations = get_available_item_locations_for_batched_item(
-			item_code, from_warehouses, required_qty, company
+			item_code, from_warehouses, required_qty, company, total_picked_qty
 		)
 	else:
 		locations = get_available_item_locations_for_other_item(
-			item_code, from_warehouses, required_qty, company
+			item_code, from_warehouses, required_qty, company, total_picked_qty
 		)
 
 	total_qty_available = sum(location.get("qty") for location in locations)
-
 	remaining_qty = required_qty - total_qty_available
 
 	if remaining_qty > 0 and not ignore_validation:
@@ -469,25 +550,60 @@
 			title=_("Insufficient Stock"),
 		)
 
+	if picked_item_details:
+		for location in list(locations):
+			key = (
+				(location["warehouse"], location["batch_no"])
+				if location.get("batch_no")
+				else location["warehouse"]
+			)
+
+			if key in picked_item_details:
+				picked_detail = picked_item_details[key]
+
+				if picked_detail.get("serial_no") and location.get("serial_no"):
+					location["serial_no"] = list(
+						set(location["serial_no"]).difference(set(picked_detail["serial_no"]))
+					)
+					location["qty"] = len(location["serial_no"])
+				else:
+					location["qty"] -= picked_detail.get("picked_qty")
+
+			if location["qty"] < 1:
+				locations.remove(location)
+
+		total_qty_available = sum(location.get("qty") for location in locations)
+		remaining_qty = required_qty - total_qty_available
+
+		if remaining_qty > 0 and not ignore_validation:
+			frappe.msgprint(
+				_("{0} units of Item {1} is picked in another Pick List.").format(
+					remaining_qty, frappe.get_desk_link("Item", item_code)
+				),
+				title=_("Already Picked"),
+			)
+
 	return locations
 
 
 def get_available_item_locations_for_serialized_item(
-	item_code, from_warehouses, required_qty, company
+	item_code, from_warehouses, required_qty, company, total_picked_qty=0
 ):
-	filters = frappe._dict({"item_code": item_code, "company": company, "warehouse": ["!=", ""]})
+	sn = frappe.qb.DocType("Serial No")
+	query = (
+		frappe.qb.from_(sn)
+		.select(sn.name, sn.warehouse)
+		.where((sn.item_code == item_code) & (sn.company == company))
+		.orderby(sn.purchase_date)
+		.limit(cint(required_qty + total_picked_qty))
+	)
 
 	if from_warehouses:
-		filters.warehouse = ["in", from_warehouses]
+		query = query.where(sn.warehouse.isin(from_warehouses))
+	else:
+		query = query.where(Coalesce(sn.warehouse, "") != "")
 
-	serial_nos = frappe.get_all(
-		"Serial No",
-		fields=["name", "warehouse"],
-		filters=filters,
-		limit=required_qty,
-		order_by="purchase_date",
-		as_list=1,
-	)
+	serial_nos = query.run(as_list=True)
 
 	warehouse_serial_nos_map = frappe._dict()
 	for serial_no, warehouse in serial_nos:
@@ -501,7 +617,7 @@
 
 
 def get_available_item_locations_for_batched_item(
-	item_code, from_warehouses, required_qty, company
+	item_code, from_warehouses, required_qty, company, total_picked_qty=0
 ):
 	sle = frappe.qb.DocType("Stock Ledger Entry")
 	batch = frappe.qb.DocType("Batch")
@@ -521,6 +637,7 @@
 		.groupby(sle.warehouse, sle.batch_no, sle.item_code)
 		.having(Sum(sle.actual_qty) > 0)
 		.orderby(IfNull(batch.expiry_date, "2200-01-01"), batch.creation, sle.batch_no, sle.warehouse)
+		.limit(cint(required_qty + total_picked_qty))
 	)
 
 	if from_warehouses:
@@ -530,53 +647,58 @@
 
 
 def get_available_item_locations_for_serial_and_batched_item(
-	item_code, from_warehouses, required_qty, company
+	item_code, from_warehouses, required_qty, company, total_picked_qty=0
 ):
 	# Get batch nos by FIFO
 	locations = get_available_item_locations_for_batched_item(
 		item_code, from_warehouses, required_qty, company
 	)
 
-	filters = frappe._dict(
-		{"item_code": item_code, "company": company, "warehouse": ["!=", ""], "batch_no": ""}
-	)
+	if locations:
+		sn = frappe.qb.DocType("Serial No")
+		conditions = (sn.item_code == item_code) & (sn.company == company)
 
-	# Get Serial Nos by FIFO for Batch No
-	for location in locations:
-		filters.batch_no = location.batch_no
-		filters.warehouse = location.warehouse
-		location.qty = (
-			required_qty if location.qty > required_qty else location.qty
-		)  # if extra qty in batch
+		for location in locations:
+			location.qty = (
+				required_qty if location.qty > required_qty else location.qty
+			)  # if extra qty in batch
 
-		serial_nos = frappe.get_list(
-			"Serial No", fields=["name"], filters=filters, limit=location.qty, order_by="purchase_date"
-		)
+			serial_nos = (
+				frappe.qb.from_(sn)
+				.select(sn.name)
+				.where(
+					(conditions) & (sn.batch_no == location.batch_no) & (sn.warehouse == location.warehouse)
+				)
+				.orderby(sn.purchase_date)
+				.limit(cint(location.qty + total_picked_qty))
+			).run(as_dict=True)
 
-		serial_nos = [sn.name for sn in serial_nos]
-		location.serial_no = serial_nos
+			serial_nos = [sn.name for sn in serial_nos]
+			location.serial_no = serial_nos
+			location.qty = len(serial_nos)
 
 	return locations
 
 
-def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty, company):
-	# gets all items available in different warehouses
-	warehouses = [x.get("name") for x in frappe.get_list("Warehouse", {"company": company}, "name")]
-
-	filters = frappe._dict(
-		{"item_code": item_code, "warehouse": ["in", warehouses], "actual_qty": [">", 0]}
+def get_available_item_locations_for_other_item(
+	item_code, from_warehouses, required_qty, company, total_picked_qty=0
+):
+	bin = frappe.qb.DocType("Bin")
+	query = (
+		frappe.qb.from_(bin)
+		.select(bin.warehouse, bin.actual_qty.as_("qty"))
+		.where((bin.item_code == item_code) & (bin.actual_qty > 0))
+		.orderby(bin.creation)
+		.limit(cint(required_qty + total_picked_qty))
 	)
 
 	if from_warehouses:
-		filters.warehouse = ["in", from_warehouses]
+		query = query.where(bin.warehouse.isin(from_warehouses))
+	else:
+		wh = frappe.qb.DocType("Warehouse")
+		query = query.from_(wh).where((bin.warehouse == wh.name) & (wh.company == company))
 
-	item_locations = frappe.get_all(
-		"Bin",
-		fields=["warehouse", "actual_qty as qty"],
-		filters=filters,
-		limit=required_qty,
-		order_by="creation",
-	)
+	item_locations = query.run(as_dict=True)
 
 	return item_locations
 
diff --git a/erpnext/stock/doctype/pick_list/pick_list_list.js b/erpnext/stock/doctype/pick_list/pick_list_list.js
new file mode 100644
index 0000000..ad88b0a
--- /dev/null
+++ b/erpnext/stock/doctype/pick_list/pick_list_list.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.listview_settings['Pick List'] = {
+	get_indicator: function (doc) {
+		const status_colors = {
+			"Draft": "grey",
+			"Open": "orange",
+			"Completed": "green",
+			"Cancelled": "red",
+		};
+		return [__(doc.status), status_colors[doc.status], "status,=," + doc.status];
+	},
+};
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index 43acdf0..1254fe3 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -414,6 +414,7 @@
 		pick_list.submit()
 
 		delivery_note = create_delivery_note(pick_list.name)
+		pick_list.load_from_db()
 
 		self.assertEqual(pick_list.locations[0].qty, delivery_note.items[0].qty)
 		self.assertEqual(pick_list.locations[1].qty, delivery_note.items[1].qty)
@@ -663,3 +664,147 @@
 		self.assertEqual(dn.items[0].rate, 42)
 		so.reload()
 		self.assertEqual(so.per_delivered, 100)
+
+	def test_pick_list_status(self):
+		warehouse = "_Test Warehouse - _TC"
+		item = make_item(properties={"maintain_stock": 1}).name
+		make_stock_entry(item=item, to_warehouse=warehouse, qty=10)
+
+		so = make_sales_order(item_code=item, qty=10, rate=100)
+
+		pl = create_pick_list(so.name)
+		pl.save()
+		pl.reload()
+		self.assertEqual(pl.status, "Draft")
+
+		pl.submit()
+		pl.reload()
+		self.assertEqual(pl.status, "Open")
+
+		dn = create_delivery_note(pl.name)
+		dn.save()
+		pl.reload()
+		self.assertEqual(pl.status, "Open")
+
+		dn.submit()
+		pl.reload()
+		self.assertEqual(pl.status, "Completed")
+
+		dn.cancel()
+		pl.reload()
+		self.assertEqual(pl.status, "Completed")
+
+		pl.cancel()
+		pl.reload()
+		self.assertEqual(pl.status, "Cancelled")
+
+	def test_consider_existing_pick_list(self):
+		def create_items(items_properties):
+			items = []
+
+			for properties in items_properties:
+				properties.update({"maintain_stock": 1})
+				item_code = make_item(properties=properties).name
+				properties.update({"item_code": item_code})
+				items.append(properties)
+
+			return items
+
+		def create_stock_entries(items):
+			warehouses = ["Stores - _TC", "Finished Goods - _TC"]
+
+			for item in items:
+				for warehouse in warehouses:
+					se = make_stock_entry(
+						item=item.get("item_code"),
+						to_warehouse=warehouse,
+						qty=5,
+					)
+
+		def get_item_list(items, qty, warehouse="All Warehouses - _TC"):
+			return [
+				{
+					"item_code": item.get("item_code"),
+					"qty": qty,
+					"warehouse": warehouse,
+				}
+				for item in items
+			]
+
+		def get_picked_items_details(pick_list_doc):
+			items_data = {}
+
+			for location in pick_list_doc.locations:
+				key = (location.warehouse, location.batch_no) if location.batch_no else location.warehouse
+				serial_no = [x for x in location.serial_no.split("\n") if x] if location.serial_no else None
+				data = {"picked_qty": location.picked_qty}
+				if serial_no:
+					data["serial_no"] = serial_no
+				if location.item_code not in items_data:
+					items_data[location.item_code] = {key: data}
+				else:
+					items_data[location.item_code][key] = data
+
+			return items_data
+
+		# Step - 1: Setup - Create Items and Stock Entries
+		items_properties = [
+			{
+				"valuation_rate": 100,
+			},
+			{
+				"valuation_rate": 200,
+				"has_batch_no": 1,
+				"create_new_batch": 1,
+			},
+			{
+				"valuation_rate": 300,
+				"has_serial_no": 1,
+				"serial_no_series": "SNO.###",
+			},
+			{
+				"valuation_rate": 400,
+				"has_batch_no": 1,
+				"create_new_batch": 1,
+				"has_serial_no": 1,
+				"serial_no_series": "SNO.###",
+			},
+		]
+
+		items = create_items(items_properties)
+		create_stock_entries(items)
+
+		# Step - 2: Create Sales Order [1]
+		so1 = make_sales_order(item_list=get_item_list(items, qty=6))
+
+		# Step - 3: Create and Submit Pick List [1] for Sales Order [1]
+		pl1 = create_pick_list(so1.name)
+		pl1.submit()
+
+		# Step - 4: Create Sales Order [2] with same Item(s) as Sales Order [1]
+		so2 = make_sales_order(item_list=get_item_list(items, qty=4))
+
+		# Step - 5: Create Pick List [2] for Sales Order [2]
+		pl2 = create_pick_list(so2.name)
+		pl2.save()
+
+		# Step - 6: Assert
+		picked_items_details = get_picked_items_details(pl1)
+
+		for location in pl2.locations:
+			key = (location.warehouse, location.batch_no) if location.batch_no else location.warehouse
+			item_data = picked_items_details.get(location.item_code, {}).get(key, {})
+			picked_qty = item_data.get("picked_qty", 0)
+			picked_serial_no = picked_items_details.get("serial_no", [])
+			bin_actual_qty = frappe.db.get_value(
+				"Bin", {"item_code": location.item_code, "warehouse": location.warehouse}, "actual_qty"
+			)
+
+			# Available Qty to pick should be equal to [Actual Qty - Picked Qty]
+			self.assertEqual(location.stock_qty, bin_actual_qty - picked_qty)
+
+			# Serial No should not be in the Picked Serial No list
+			if location.serial_no:
+				a = set(picked_serial_no)
+				b = set([x for x in location.serial_no.split("\n") if x])
+				self.assertSetEqual(b, b.difference(a))
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 1755f28..8c20ca0 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -158,6 +158,7 @@
 		self.validate_subcontract_order()
 		self.update_subcontract_order_supplied_items()
 		self.update_subcontracting_order_status()
+		self.update_pick_list_status()
 
 		self.make_gl_entries()
 
@@ -2276,6 +2277,11 @@
 
 			update_subcontracting_order_status(self.subcontracting_order)
 
+	def update_pick_list_status(self):
+		from erpnext.stock.doctype.pick_list.pick_list import update_pick_list_status
+
+		update_pick_list_status(self.pick_list)
+
 	def set_missing_values(self):
 		"Updates rate and availability of all the items of mapped doc."
 		self.set_transfer_qty()