diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 774d7fd5ea7..5f5a078bd4f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -586,6 +586,8 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( set_dynamic_labels() { super.set_dynamic_labels(); this.frm.events.hide_fields(this.frm); + const hide_update_stock = cint(this.frm.doc.is_debit_note) || cint(this.frm.doc.has_subcontracted); + this.frm.set_df_property("update_stock", "hidden", hide_update_stock); } items_on_form_rendered() { @@ -1174,13 +1176,20 @@ frappe.ui.form.on("Sales Invoice", { ); }, + is_debit_note: function (frm) { + if (frm.doc.is_debit_note) { + frm.set_value("update_stock", 0); + } + // visibility handled by set_dynamic_labels() + frm.cscript.set_dynamic_labels(); + }, + refresh: function (frm) { if (frm.doc.is_debit_note) { frm.set_df_property("return_against", "label", __("Adjustment Against")); } frm.set_df_property("update_stock", "read_only", frm.doc.has_subcontracted); - frm.toggle_display("update_stock", !frm.doc.has_subcontracted); }, }); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 29725897883..0e54d1553a1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -304,6 +304,7 @@ class SalesInvoice(SellingController): self.validate_uom_is_integer("uom", "qty") self.check_sales_order_on_hold_or_close("sales_order") self.validate_debit_to_acc() + self.validate_debit_note_with_update_stock() self.clear_unallocated_advances("Sales Invoice Advance", "advances") FixedAssetService(self).validate_fixed_asset() FixedAssetService(self).set_income_account_for_fixed_assets() @@ -960,6 +961,17 @@ class SalesInvoice(SellingController): if flt(self.change_amount) and not self.account_for_change_amount: msgprint(_("Please enter Account for Change Amount"), raise_exception=1) + def validate_debit_note_with_update_stock(self): + """Prevent stock update when Sales Invoice is marked as Debit Note.""" + if self.is_debit_note and cint(self.update_stock): + frappe.throw( + _( + "You cannot update stock for a Debit Note. A Debit Note is a financial " + "document that should not affect inventory. Please disable 'Update Stock'." + ), + title=_("Invalid Configuration"), + ) + def validate_dropship_item(self): """If items are drop shipped, stock cannot be updated.""" if not cint(self.update_stock): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 79283dd24bf..e168a9e9df1 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -5213,6 +5213,13 @@ class TestSalesInvoice(ERPNextTestSuite): frappe.db.set_value("Company", "_Test Company 1", "cost_center", cost_center) + def test_debit_note_with_update_stock_validation(self): + """Test that saving a Debit Note with Update Stock enabled raises ValidationError.""" + si = create_sales_invoice(do_not_save=True) + si.is_debit_note = 1 + si.update_stock = 1 + self.assertRaises(frappe.ValidationError, si.save) + def make_item_for_si(item_code, properties=None): from erpnext.stock.doctype.item.test_item import make_item