From fee24c65d47da56b2cd91179b9e2426ad3aca260 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 13:54:08 +0530 Subject: [PATCH 1/2] fix: do not consider rejected warehouses in pick list (backport #39539) (#39804) * fix: do not consider rejected warehouses in pick list (#39539) * fix: do not picked rejected materials * test: test case for pick list without rejected materials (cherry picked from commit f6725e43425043eaba7dcdd3cf3768a857a39ee6) # Conflicts: # erpnext/selling/doctype/sales_order/test_sales_order.py # erpnext/stock/doctype/pick_list/pick_list.json # erpnext/stock/doctype/pick_list/pick_list.py * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts * chore: fixed test case --------- Co-authored-by: rohitwaghchaure (cherry picked from commit 2c8e4c1ab3bde6a9ab37a41d2801c447d76903ef) # Conflicts: # erpnext/selling/doctype/sales_order/test_sales_order.py --- .../doctype/sales_order/test_sales_order.py | 108 ++++++++++++++++++ .../stock/doctype/pick_list/pick_list.json | 12 +- erpnext/stock/doctype/pick_list/pick_list.py | 74 +++++++++++- .../stock/doctype/warehouse/warehouse.json | 10 +- 4 files changed, 195 insertions(+), 9 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index a518597aa6f6..72065d18e5f1 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -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, @@ -1973,6 +1974,113 @@ 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")) +<<<<<<< HEAD +======= + def test_sales_order_advance_payment_status(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request + + so = make_sales_order(qty=1, rate=100) + self.assertEqual( + frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested" + ) + + pr = make_payment_request(dt=so.doctype, dn=so.name, submit_doc=True, return_doc=True) + self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested") + + pe = get_payment_entry(so.doctype, so.name).save().submit() + self.assertEqual( + frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Fully Paid" + ) + + pe.reload() + pe.cancel() + self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested") + + pr.reload() + pr.cancel() + self.assertEqual( + frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested" + ) + + 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) + +>>>>>>> 2c8e4c1ab3 (fix: do not consider rejected warehouses in pick list (backport #39539) (#39804)) def automatically_fetch_payment_terms(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index bd84aadef74b..0c474342a973 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -16,6 +16,7 @@ "for_qty", "column_break_4", "parent_warehouse", + "consider_rejected_warehouses", "get_item_locations", "section_break_6", "scan_barcode", @@ -184,11 +185,18 @@ "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": [], - "modified": "2024-02-01 16:17:44.877426", + "modified": "2024-02-02 16:17:44.877426", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", @@ -260,4 +268,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 e2edb20510cf..98ed569af1eb 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -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, ), ) @@ -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 = ( @@ -725,18 +727,34 @@ def get_available_item_locations( required_qty, company, total_picked_qty, + consider_rejected_warehouses=consider_rejected_warehouses, ) 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) @@ -775,6 +793,7 @@ def get_available_item_locations_for_serial_and_batched_item( required_qty, company, total_picked_qty=0, + consider_rejected_warehouses=False, ): # Get batch nos by FIFO locations = get_available_item_locations_for_batched_item( @@ -782,6 +801,7 @@ def get_available_item_locations_for_serial_and_batched_item( from_warehouses, required_qty, company, + consider_rejected_warehouses=consider_rejected_warehouses, ) if locations: @@ -811,7 +831,12 @@ def get_available_item_locations_for_serial_and_batched_item( def get_available_item_locations_for_serialized_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, ): picked_serial_nos = get_picked_serial_nos(item_code, from_warehouses) @@ -828,6 +853,10 @@ def get_available_item_locations_for_serialized_item( 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() @@ -860,7 +889,12 @@ def get_available_item_locations_for_serialized_item( def get_available_item_locations_for_batched_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, ): locations = [] data = get_auto_batch_nos( @@ -875,7 +909,14 @@ def get_available_item_locations_for_batched_item( ) warehouse_wise_batches = frappe._dict() + rejected_warehouses = get_rejected_warehouses() + for d in data: + if ( + not consider_rejected_warehouses and rejected_warehouses and d.warehouse in rejected_warehouses + ): + continue + if d.warehouse not in warehouse_wise_batches: warehouse_wise_batches.setdefault(d.warehouse, defaultdict(float)) @@ -898,7 +939,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 = ( @@ -915,6 +961,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 @@ -1236,3 +1286,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 diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 43b2ad2a69b0..7b0cade3ca46 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -13,6 +13,7 @@ "column_break_3", "is_group", "parent_warehouse", + "is_rejected_warehouse", "column_break_4", "account", "company", @@ -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", From 4f6e06acb2998cc0a9c56d378540a9366243555f Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 8 Feb 2024 14:23:20 +0530 Subject: [PATCH 2/2] chore: fix conflicts --- .../doctype/sales_order/test_sales_order.py | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 72065d18e5f1..681c2bdd0a8f 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1974,35 +1974,6 @@ 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")) -<<<<<<< HEAD -======= - def test_sales_order_advance_payment_status(self): - from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry - from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request - - so = make_sales_order(qty=1, rate=100) - self.assertEqual( - frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested" - ) - - pr = make_payment_request(dt=so.doctype, dn=so.name, submit_doc=True, return_doc=True) - self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested") - - pe = get_payment_entry(so.doctype, so.name).save().submit() - self.assertEqual( - frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Fully Paid" - ) - - pe.reload() - pe.cancel() - self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested") - - pr.reload() - pr.cancel() - self.assertEqual( - frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested" - ) - def test_pick_list_without_rejected_materials(self): serial_and_batch_item = make_item( "_Test Serial and Batch Item for Rejected Materials", @@ -2080,7 +2051,6 @@ def test_pick_list_without_rejected_materials(self): self.assertFalse(row.warehouse == rejected_warehouse) self.assertTrue(row.warehouse == warehouse) ->>>>>>> 2c8e4c1ab3 (fix: do not consider rejected warehouses in pick list (backport #39539) (#39804)) def automatically_fetch_payment_terms(enable=1): accounts_settings = frappe.get_doc("Accounts Settings")