mirror of
https://github.com/frappe/erpnext.git
synced 2026-03-24 13:42:10 +01:00
fix: manual pick allow to pick more than available stock (#42155)
(cherry picked from commit 938dd4b2aa)
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
This commit is contained in:
@@ -6,7 +6,7 @@ from collections import OrderedDict, defaultdict
|
|||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _, bold
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.mapper import map_child_doc
|
from frappe.model.mapper import map_child_doc
|
||||||
from frappe.query_builder import Case
|
from frappe.query_builder import Case
|
||||||
@@ -73,6 +73,9 @@ class PickList(Document):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_for_qty()
|
self.validate_for_qty()
|
||||||
|
if self.pick_manually and self.get("locations"):
|
||||||
|
self.validate_stock_qty()
|
||||||
|
self.check_serial_no_status()
|
||||||
|
|
||||||
def before_save(self):
|
def before_save(self):
|
||||||
self.update_status()
|
self.update_status()
|
||||||
@@ -82,6 +85,60 @@ class PickList(Document):
|
|||||||
if self.get("locations"):
|
if self.get("locations"):
|
||||||
self.validate_sales_order_percentage()
|
self.validate_sales_order_percentage()
|
||||||
|
|
||||||
|
def validate_stock_qty(self):
|
||||||
|
from erpnext.stock.doctype.batch.batch import get_batch_qty
|
||||||
|
|
||||||
|
for row in self.get("locations"):
|
||||||
|
if row.batch_no and not row.qty:
|
||||||
|
batch_qty = get_batch_qty(row.batch_no, row.warehouse, row.item_code)
|
||||||
|
|
||||||
|
if row.qty > batch_qty:
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} for the batch {4} in the warehouse {5}."
|
||||||
|
).format(row.idx, row.item_code, batch_qty, row.batch_no, bold(row.warehouse)),
|
||||||
|
title=_("Insufficient Stock"),
|
||||||
|
)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
bin_qty = frappe.db.get_value(
|
||||||
|
"Bin",
|
||||||
|
{"item_code": row.item_code, "warehouse": row.warehouse},
|
||||||
|
"actual_qty",
|
||||||
|
)
|
||||||
|
|
||||||
|
if row.qty > bin_qty:
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} in the warehouse {4}."
|
||||||
|
).format(row.idx, row.qty, bold(row.item_code), bin_qty, bold(row.warehouse)),
|
||||||
|
title=_("Insufficient Stock"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_serial_no_status(self):
|
||||||
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
|
for row in self.get("locations"):
|
||||||
|
if not row.serial_no:
|
||||||
|
continue
|
||||||
|
|
||||||
|
picked_serial_nos = get_serial_nos(row.serial_no)
|
||||||
|
validated_serial_nos = frappe.get_all(
|
||||||
|
"Serial No",
|
||||||
|
filters={"name": ("in", picked_serial_nos), "warehouse": row.warehouse},
|
||||||
|
pluck="name",
|
||||||
|
)
|
||||||
|
|
||||||
|
incorrect_serial_nos = set(picked_serial_nos) - set(validated_serial_nos)
|
||||||
|
if incorrect_serial_nos:
|
||||||
|
frappe.throw(
|
||||||
|
_("The Serial No at Row #{0}: {1} is not available in warehouse {2}.").format(
|
||||||
|
row.idx, ", ".join(incorrect_serial_nos), row.warehouse
|
||||||
|
),
|
||||||
|
title=_("Incorrect Warehouse"),
|
||||||
|
)
|
||||||
|
|
||||||
def validate_sales_order_percentage(self):
|
def validate_sales_order_percentage(self):
|
||||||
# set percentage picked in SO
|
# set percentage picked in SO
|
||||||
for location in self.get("locations"):
|
for location in self.get("locations"):
|
||||||
|
|||||||
@@ -1132,3 +1132,45 @@ class TestPickList(FrappeTestCase):
|
|||||||
pl.save()
|
pl.save()
|
||||||
|
|
||||||
self.assertEqual(pl.locations[0].qty, 80.0)
|
self.assertEqual(pl.locations[0].qty, 80.0)
|
||||||
|
|
||||||
|
def test_validate_picked_qty_with_manual_option(self):
|
||||||
|
warehouse = "_Test Warehouse - _TC"
|
||||||
|
non_serialized_item = make_item(
|
||||||
|
"Test Non Serialized Pick List Item For Manual Option", properties={"is_stock_item": 1}
|
||||||
|
).name
|
||||||
|
|
||||||
|
serialized_item = make_item(
|
||||||
|
"Test Serialized Pick List Item For Manual Option",
|
||||||
|
properties={"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SN-HSNMSPLI-.####"},
|
||||||
|
).name
|
||||||
|
|
||||||
|
batched_item = make_item(
|
||||||
|
"Test Batched Pick List Item For Manual Option",
|
||||||
|
properties={
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"batch_number_series": "SN-HBNMSPLI-.####",
|
||||||
|
"create_new_batch": 1,
|
||||||
|
},
|
||||||
|
).name
|
||||||
|
|
||||||
|
make_stock_entry(item=non_serialized_item, to_warehouse=warehouse, qty=10, basic_rate=100)
|
||||||
|
make_stock_entry(item=serialized_item, to_warehouse=warehouse, qty=10, basic_rate=100)
|
||||||
|
make_stock_entry(item=batched_item, to_warehouse=warehouse, qty=10, basic_rate=100)
|
||||||
|
|
||||||
|
so = make_sales_order(
|
||||||
|
item_code=non_serialized_item, qty=10, rate=100, do_not_save=True, warehouse=warehouse
|
||||||
|
)
|
||||||
|
so.append("items", {"item_code": serialized_item, "qty": 10, "rate": 100, "warehouse": warehouse})
|
||||||
|
so.append("items", {"item_code": batched_item, "qty": 10, "rate": 100, "warehouse": warehouse})
|
||||||
|
so.set_missing_values()
|
||||||
|
so.save()
|
||||||
|
so.submit()
|
||||||
|
|
||||||
|
pl = create_pick_list(so.name)
|
||||||
|
pl.pick_manually = 1
|
||||||
|
|
||||||
|
for row in pl.locations:
|
||||||
|
row.qty = row.qty + 10
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, pl.save)
|
||||||
|
|||||||
Reference in New Issue
Block a user