diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index f8a9160aca8..63d2e80888c 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -241,6 +241,8 @@ class StockEntry(StockController): self.reset_default_field_value("from_warehouse", "items", "s_warehouse") self.reset_default_field_value("to_warehouse", "items", "t_warehouse") + self.validate_same_source_target_warehouse_during_material_transfer() + def on_submit(self): self.validate_closed_subcontracting_order() self.make_bundle_using_old_serial_batch_fields() @@ -796,6 +798,53 @@ class StockEntry(StockController): title=_("Missing Item"), ) + def validate_same_source_target_warehouse_during_material_transfer(self): + """ + Validate Material Transfer entries where source and target warehouses are identical. + + For Material Transfer purpose, if an item has the same source and target warehouse, + require that at least one inventory dimension (if configured) differs between source + and target to ensure a meaningful transfer is occurring. + + Raises: + frappe.ValidationError: If warehouses are same and no inventory dimensions differ + """ + from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions + + inventory_dimensions = get_inventory_dimensions() + if self.purpose == "Material Transfer": + for item in self.items: + if cstr(item.s_warehouse) == cstr(item.t_warehouse): + if not inventory_dimensions: + frappe.throw( + _( + "Row #{0}: Source and Target Warehouse cannot be the same for Material Transfer" + ).format(item.idx), + title=_("Invalid Source and Target Warehouse"), + ) + else: + difference_found = False + for dimension in inventory_dimensions: + fieldname = ( + dimension.source_fieldname + if dimension.source_fieldname.startswith("to_") + else f"to_{dimension.source_fieldname}" + ) + if ( + item.get(dimension.source_fieldname) + and item.get(fieldname) + and item.get(dimension.source_fieldname) != item.get(fieldname) + ): + difference_found = True + break + if not difference_found: + frappe.throw( + _( + "Row #{0}: Source, Target Warehouse and Inventory Dimensions cannot be the exact same for Material Transfer" + ).format(item.idx), + title=_("Invalid Source and Target Warehouse"), + ) + def get_matched_items(self, item_code): for row in self.items: if row.item_code == item_code or row.original_item == item_code: