From 650f874fbdd7cc949c6e2088ed91df8249119e91 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 20 Jan 2026 13:27:14 +0530 Subject: [PATCH] fix: validation to check at-least one raw material for manufacture entry (cherry picked from commit f003b3c3787488deee4c8238ef94fc0122e6e234) # Conflicts: # erpnext/stock/doctype/stock_entry/stock_entry.py # erpnext/stock/doctype/stock_entry/test_stock_entry.py --- .../stock/doctype/stock_entry/stock_entry.py | 29 +++++++++ .../doctype/stock_entry/test_stock_entry.py | 61 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 21f3245bbc4..a4a699ab1de 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -244,6 +244,35 @@ class StockEntry(StockController): self.validate_same_source_target_warehouse_during_material_transfer() +<<<<<<< HEAD +======= + self.validate_closed_subcontracting_order() + self.validate_subcontract_order() + self.validate_raw_materials_exists() + + super().validate_subcontracting_inward() + + def validate_raw_materials_exists(self): + if self.purpose not in ["Manufacture", "Repack", "Disassemble"]: + return + + if frappe.db.get_single_value("Manufacturing Settings", "material_consumption"): + return + + raw_materials = [] + for row in self.items: + if row.s_warehouse: + raw_materials.append(row.item_code) + + if not raw_materials: + frappe.throw( + _( + "At least one raw material item must be present in the stock entry for the type {0}" + ).format(bold(self.purpose)), + title=_("Raw Materials Missing"), + ) + +>>>>>>> f003b3c378 (fix: validation to check at-least one raw material for manufacture entry) def set_serial_batch_for_disassembly(self): if self.purpose != "Disassemble": return diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 0be02756207..076231193fa 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -2233,7 +2233,68 @@ class TestStockEntry(FrappeTestCase): se.submit() +<<<<<<< HEAD def make_serialized_item(**args): +======= + warehouse = "_Test Warehouse - _TC" + retain_sample_item = make_item( + "Retain Sample Item", + properties={ + "is_stock_item": 1, + "retain_sample": 1, + "sample_quantity": 2, + "has_batch_no": 1, + "has_serial_no": 1, + "create_new_batch": 1, + "batch_number_series": "SAMPLE-RET-.#####", + "serial_no_series": "SAMPLE-RET-SN-.#####", + }, + ) + material_receipt = make_stock_entry( + item_code=retain_sample_item.item_code, target=warehouse, qty=10, purpose="Material Receipt" + ) + + source_sabb = frappe.get_doc( + "Serial and Batch Bundle", material_receipt.items[0].serial_and_batch_bundle + ) + batch = source_sabb.entries[0].batch_no + serial_nos = [entry.serial_no for entry in source_sabb.entries] + + sample_entry = frappe.get_doc( + move_sample_to_retention_warehouse(material_receipt.company, material_receipt.items) + ) + sample_entry.submit() + target_sabb = frappe.get_doc("Serial and Batch Bundle", sample_entry.items[0].serial_and_batch_bundle) + + self.assertEqual(sample_entry.items[0].transfer_qty, 2) + self.assertEqual(target_sabb.entries[0].batch_no, batch) + self.assertEqual([entry.serial_no for entry in target_sabb.entries], serial_nos[:2]) + + def test_raw_material_missing_validation(self): + original_value = frappe.db.get_single_value("Manufacturing Settings", "material_consumption") + frappe.db.set_single_value("Manufacturing Settings", "material_consumption", 0) + + stock_entry = make_stock_entry( + item_code="_Test Item", + qty=1, + target="_Test Warehouse - _TC", + do_not_save=True, + ) + + stock_entry.purpose = "Manufacture" + stock_entry.stock_entry_type = "Manufacture" + stock_entry.items[0].is_finished_item = 1 + + self.assertRaises( + frappe.ValidationError, + stock_entry.save, + ) + + frappe.db.set_single_value("Manufacturing Settings", "material_consumption", original_value) + + +def make_serialized_item(self, **args): +>>>>>>> f003b3c378 (fix: validation to check at-least one raw material for manufacture entry) args = frappe._dict(args) se = frappe.copy_doc(test_records[0])