Skip to content

Commit

Permalink
fix: do not consider rejected warehouses in pick list (#39539)
Browse files Browse the repository at this point in the history
* fix: do not picked rejected materials

* test: test case for pick list without rejected materials

(cherry picked from commit f6725e4)

# Conflicts:
#	erpnext/stock/doctype/pick_list/pick_list.json
#	erpnext/stock/doctype/pick_list/pick_list.py
  • Loading branch information
rohitwaghchaure authored and mergify[bot] committed Feb 6, 2024
1 parent 789aee4 commit 4c76f41
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 5 deletions.
78 changes: 78 additions & 0 deletions erpnext/selling/doctype/sales_order/test_sales_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
from erpnext.selling.doctype.sales_order.sales_order import (
WarehouseRequired,
create_pick_list,
make_delivery_note,
make_material_request,
make_raw_material_request,
Expand Down Expand Up @@ -1973,6 +1974,83 @@ def test_expired_rate_for_packed_item(self):
self.assertEqual(so.items[0].rate, scenario.get("expected_rate"))
self.assertEqual(so.packed_items[0].rate, scenario.get("expected_rate"))

def test_pick_list_without_rejected_materials(self):
serial_and_batch_item = make_item(
"_Test Serial and Batch Item for Rejected Materials",
properties={
"has_serial_no": 1,
"has_batch_no": 1,
"create_new_batch": 1,
"batch_number_series": "BAT-TSBIFRM-.#####",
"serial_no_series": "SN-TSBIFRM-.#####",
},
).name

serial_item = make_item(
"_Test Serial Item for Rejected Materials",
properties={
"has_serial_no": 1,
"serial_no_series": "SN-TSIFRM-.#####",
},
).name

batch_item = make_item(
"_Test Batch Item for Rejected Materials",
properties={
"has_batch_no": 1,
"create_new_batch": 1,
"batch_number_series": "BAT-TBIFRM-.#####",
},
).name

normal_item = make_item("_Test Normal Item for Rejected Materials").name

warehouse = "_Test Warehouse - _TC"
rejected_warehouse = "_Test Dummy Rejected Warehouse - _TC"

if not frappe.db.exists("Warehouse", rejected_warehouse):
frappe.get_doc(
{
"doctype": "Warehouse",
"warehouse_name": rejected_warehouse,
"company": "_Test Company",
"warehouse_group": "_Test Warehouse Group",
"is_rejected_warehouse": 1,
}
).insert()

se = make_stock_entry(item_code=normal_item, qty=1, to_warehouse=warehouse, do_not_submit=True)
for item in [serial_and_batch_item, serial_item, batch_item]:
se.append("items", {"item_code": item, "qty": 1, "t_warehouse": warehouse})

se.save()
se.submit()

se = make_stock_entry(
item_code=normal_item, qty=1, to_warehouse=rejected_warehouse, do_not_submit=True
)
for item in [serial_and_batch_item, serial_item, batch_item]:
se.append("items", {"item_code": item, "qty": 1, "t_warehouse": rejected_warehouse})

se.save()
se.submit()

so = make_sales_order(item_code=normal_item, qty=2, do_not_submit=True)

for item in [serial_and_batch_item, serial_item, batch_item]:
so.append("items", {"item_code": item, "qty": 2, "warehouse": warehouse})

so.save()
so.submit()

pick_list = create_pick_list(so.name)

pick_list.save()
for row in pick_list.locations:
self.assertEqual(row.qty, 1.0)
self.assertFalse(row.warehouse == rejected_warehouse)
self.assertTrue(row.warehouse == warehouse)


def automatically_fetch_payment_terms(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
Expand Down
12 changes: 12 additions & 0 deletions erpnext/stock/doctype/pick_list/pick_list.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"for_qty",
"column_break_4",
"parent_warehouse",
"consider_rejected_warehouses",
"get_item_locations",
"section_break_6",
"scan_barcode",
Expand Down Expand Up @@ -184,11 +185,22 @@
"report_hide": 1,
"reqd": 1,
"search_index": 1
},
{
"default": "0",
"description": "Enable it if users want to consider rejected materials to dispatch.",
"fieldname": "consider_rejected_warehouses",
"fieldtype": "Check",
"label": "Consider Rejected Warehouses"
}
],
"is_submittable": 1,
"links": [],
<<<<<<< HEAD
"modified": "2024-02-01 16:17:44.877426",
=======
"modified": "2024-01-24 17:05:20.317180",
>>>>>>> f6725e4342 (fix: do not consider rejected warehouses in pick list (#39539))
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List",
Expand Down
139 changes: 135 additions & 4 deletions erpnext/stock/doctype/pick_list/pick_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ def set_item_locations(self, save=False):
self.item_count_map.get(item_code),
self.company,
picked_item_details=picked_items_details.get(item_code),
consider_rejected_warehouses=self.consider_rejected_warehouses,
),
)

Expand Down Expand Up @@ -710,6 +711,7 @@ def get_available_item_locations(
company,
ignore_validation=False,
picked_item_details=None,
consider_rejected_warehouses=False,
):
locations = []
total_picked_qty = (
Expand All @@ -725,18 +727,37 @@ def get_available_item_locations(
required_qty,
company,
total_picked_qty,
<<<<<<< HEAD
=======
consider_rejected_warehouses=consider_rejected_warehouses,
>>>>>>> f6725e4342 (fix: do not consider rejected warehouses in pick list (#39539))
)
elif has_serial_no:
locations = get_available_item_locations_for_serialized_item(
item_code, from_warehouses, required_qty, company, total_picked_qty
item_code,
from_warehouses,
required_qty,
company,
total_picked_qty,
consider_rejected_warehouses=consider_rejected_warehouses,
)
elif has_batch_no:
locations = get_available_item_locations_for_batched_item(
item_code, from_warehouses, required_qty, company, total_picked_qty
item_code,
from_warehouses,
required_qty,
company,
total_picked_qty,
consider_rejected_warehouses=consider_rejected_warehouses,
)
else:
locations = get_available_item_locations_for_other_item(
item_code, from_warehouses, required_qty, company, total_picked_qty
item_code,
from_warehouses,
required_qty,
company,
total_picked_qty,
consider_rejected_warehouses=consider_rejected_warehouses,
)

total_qty_available = sum(location.get("qty") for location in locations)
Expand Down Expand Up @@ -769,19 +790,108 @@ def get_available_item_locations(
return locations


<<<<<<< HEAD
=======
def get_available_item_locations_for_serialized_item(
item_code,
from_warehouses,
required_qty,
company,
total_picked_qty=0,
consider_rejected_warehouses=False,
):
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(ceil(required_qty + total_picked_qty))
)

if from_warehouses:
query = query.where(sn.warehouse.isin(from_warehouses))
else:
query = query.where(Coalesce(sn.warehouse, "") != "")

if not consider_rejected_warehouses:
if rejected_warehouses := get_rejected_warehouses():
query = query.where(sn.warehouse.notin(rejected_warehouses))

serial_nos = query.run(as_list=True)

warehouse_serial_nos_map = frappe._dict()
for serial_no, warehouse in serial_nos:
warehouse_serial_nos_map.setdefault(warehouse, []).append(serial_no)

locations = []
for warehouse, serial_nos in warehouse_serial_nos_map.items():
locations.append({"qty": len(serial_nos), "warehouse": warehouse, "serial_no": serial_nos})

return locations


def get_available_item_locations_for_batched_item(
item_code,
from_warehouses,
required_qty,
company,
total_picked_qty=0,
consider_rejected_warehouses=False,
):
sle = frappe.qb.DocType("Stock Ledger Entry")
batch = frappe.qb.DocType("Batch")

query = (
frappe.qb.from_(sle)
.from_(batch)
.select(sle.warehouse, sle.batch_no, Sum(sle.actual_qty).as_("qty"))
.where(
(sle.batch_no == batch.name)
& (sle.item_code == item_code)
& (sle.company == company)
& (batch.disabled == 0)
& (sle.is_cancelled == 0)
& (IfNull(batch.expiry_date, "2200-01-01") > today())
)
.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(ceil(required_qty + total_picked_qty))
)

if from_warehouses:
query = query.where(sle.warehouse.isin(from_warehouses))

if not consider_rejected_warehouses:
if rejected_warehouses := get_rejected_warehouses():
query = query.where(sle.warehouse.notin(rejected_warehouses))

return query.run(as_dict=True)


>>>>>>> f6725e4342 (fix: do not consider rejected warehouses in pick list (#39539))
def get_available_item_locations_for_serial_and_batched_item(
item_code,
from_warehouses,
required_qty,
company,
total_picked_qty=0,
<<<<<<< HEAD
=======
consider_rejected_warehouses=False,
>>>>>>> f6725e4342 (fix: do not consider rejected warehouses in pick list (#39539))
):
# Get batch nos by FIFO
locations = get_available_item_locations_for_batched_item(
item_code,
from_warehouses,
required_qty,
company,
<<<<<<< HEAD
=======
consider_rejected_warehouses=consider_rejected_warehouses,
>>>>>>> f6725e4342 (fix: do not consider rejected warehouses in pick list (#39539))
)

if locations:
Expand Down Expand Up @@ -898,7 +1008,12 @@ def get_available_item_locations_for_batched_item(


def get_available_item_locations_for_other_item(
item_code, from_warehouses, required_qty, company, total_picked_qty=0
item_code,
from_warehouses,
required_qty,
company,
total_picked_qty=0,
consider_rejected_warehouses=False,
):
bin = frappe.qb.DocType("Bin")
query = (
Expand All @@ -915,6 +1030,10 @@ def get_available_item_locations_for_other_item(
wh = frappe.qb.DocType("Warehouse")
query = query.from_(wh).where((bin.warehouse == wh.name) & (wh.company == company))

if not consider_rejected_warehouses:
if rejected_warehouses := get_rejected_warehouses():
query = query.where(bin.warehouse.notin(rejected_warehouses))

item_locations = query.run(as_dict=True)

return item_locations
Expand Down Expand Up @@ -1236,3 +1355,15 @@ def update_common_item_properties(item, location):
item.serial_no = location.serial_no
item.batch_no = location.batch_no
item.material_request_item = location.material_request_item


def get_rejected_warehouses():
if not hasattr(frappe.local, "rejected_warehouses"):
frappe.local.rejected_warehouses = []

if not frappe.local.rejected_warehouses:
frappe.local.rejected_warehouses = frappe.get_all(
"Warehouse", filters={"is_rejected_warehouse": 1}, pluck="name"
)

return frappe.local.rejected_warehouses
10 changes: 9 additions & 1 deletion erpnext/stock/doctype/warehouse/warehouse.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"column_break_3",
"is_group",
"parent_warehouse",
"is_rejected_warehouse",
"column_break_4",
"account",
"company",
Expand Down Expand Up @@ -249,13 +250,20 @@
{
"fieldname": "column_break_qajx",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "If yes, then this warehouse will be used to store rejected materials",
"fieldname": "is_rejected_warehouse",
"fieldtype": "Check",
"label": "Is Rejected Warehouse"
}
],
"icon": "fa fa-building",
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2023-05-29 13:10:43.333160",
"modified": "2024-01-24 16:27:28.299520",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",
Expand Down

0 comments on commit 4c76f41

Please sign in to comment.