From 8c03029f2847b11846b6bb40f4b5c254e35e77a1 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 29 Jun 2026 22:25:19 +0530 Subject: [PATCH] fix(controllers): guard return-rate division against a zero stock qty (Postgres) get_rate_for_return builds Abs(stock_value_difference / actual_qty) for Sales/Delivery returns and passes it to get_value with no actual_qty filter. A matched Stock Ledger Entry with actual_qty=0 (a zero-qty repost / serial-batch row) makes Postgres raise 'division by zero' while MariaDB returns NULL. Wrap the divisor in NullIf(actual_qty, 0) so both engines return NULL. MariaDB output unchanged. Sibling of the already-fixed /actual_qty sites in stock_ledger.py and incorrect_serial_no_valuation.py. --- erpnext/controllers/sales_and_purchase_return.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index db1227e29b2..9cdc0a07cd5 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -7,7 +7,7 @@ import frappe from frappe import _, bold from frappe.model.meta import get_field_precision from frappe.query_builder import DocType -from frappe.query_builder.functions import Abs, Sum +from frappe.query_builder.functions import Abs, NullIf, Sum from frappe.utils import cint, flt, format_datetime, get_datetime import erpnext @@ -766,7 +766,7 @@ def get_rate_for_return( select_field = "incoming_rate" else: StockLedgerEntry = frappe.qb.DocType("Stock Ledger Entry") - select_field = Abs(StockLedgerEntry.stock_value_difference / StockLedgerEntry.actual_qty) + select_field = Abs(StockLedgerEntry.stock_value_difference / NullIf(StockLedgerEntry.actual_qty, 0)) item_details = frappe.get_cached_value("Item", item_code, ["has_batch_no", "has_expiry_date"], as_dict=1) set_zero_rate_for_expired_batch = frappe.db.get_single_value(