diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index 6e1d02c128..d35711868f 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -55,7 +55,7 @@ class StockSettings(Document): self.cant_change_valuation_method() self.validate_clean_description_html() self.validate_pending_reposts() - self.cant_disable_stock_reservation() + self.validate_stock_reservation() def validate_warehouses(self): warehouse_fields = ["default_warehouse", "sample_retention_warehouse"] @@ -100,23 +100,64 @@ class StockSettings(Document): if self.stock_frozen_upto: check_pending_reposting(self.stock_frozen_upto) - def cant_disable_stock_reservation(self): - """Raises an exception if user tries to disable Stock Reservation and there are existing Stock Reservation Entries.""" + def validate_stock_reservation(self): + """Raises an exception if the user tries to enable/disable `Stock Reservation` with `Negative Stock` or `Open Stock Reservation Entries`.""" - if not self.enable_stock_reservation: - db_enable_stock_reservation = frappe.db.get_single_value( - "Stock Settings", "enable_stock_reservation" - ) + db_allow_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock") + db_enable_stock_reservation = frappe.db.get_single_value( + "Stock Settings", "enable_stock_reservation" + ) - if db_enable_stock_reservation and frappe.db.exists( - "Stock Reservation Entry", {"docstatus": 1, "status": ["!=", "Delivered"]} - ): + # Change in value of `Allow Negative Stock` + if db_allow_negative_stock != self.allow_negative_stock: + + # Disable -> Enable: Don't allow if `Stock Reservation` is enabled + if self.allow_negative_stock and self.enable_stock_reservation: frappe.throw( - _("As there are existing {0}, you can not change the value of {1}.").format( - frappe.bold("Stock Reservation Entries"), frappe.bold("Enable Stock Reservation") + _("As {0} is enabled, you can not enable {1}.").format( + frappe.bold("Stock Reservation"), frappe.bold("Allow Negative Stock") ) ) + # Change in value of `Enable Stock Reservation` + if db_enable_stock_reservation != self.enable_stock_reservation: + + # Disable -> Enable + if self.enable_stock_reservation: + + # Don't allow if `Allow Negative Stock` is enabled + if self.allow_negative_stock: + frappe.throw( + _("As {0} is enabled, you can not enable {1}.").format( + frappe.bold("Allow Negative Stock"), frappe.bold("Stock Reservation") + ) + ) + + else: + # Don't allow if there are negative stock + has_negative_stock = frappe.db.exists("Bin", {"actual_qty": ["<", 0]}) + + if has_negative_stock: + frappe.throw( + _("As there are negative stock, you can not enable {0}.").format( + frappe.bold("Stock Reservation") + ) + ) + + # Enable -> Disable + else: + # Don't allow if there are open Stock Reservation Entries + has_reserved_stock = frappe.db.exists( + "Stock Reservation Entry", {"docstatus": 1, "status": ["!=", "Delivered"]} + ) + + if has_reserved_stock: + frappe.throw( + _("As there are reserved stock, you cannot disable {0}.").format( + frappe.bold("Stock Reservation") + ) + ) + def on_update(self): self.toggle_warehouse_field_for_inter_warehouse_transfer()