fix: repost read stale sibling SLE rate for moving average returns

During repost, a return line with recalculate_rate resolved its moving
average rate through get_incoming_rate -> get_previous_sle, which matches
posting_datetime <= and orders by creation desc. For a multi-line return
of the same item, every line shares one posting_datetime, so the query
landed on a sibling line of the same voucher whose stored valuation_rate
was still the previous repost run's output, not the rate before the
voucher.

Each repost run therefore re-seeded the voucher from its own prior
output. The error gain per run is (qty returned at the stale rate) /
(qty remaining after the return), so whenever a return removes most of
the stock the loop diverges instead of converging, alternating sign and
growing until stock_value overflows decimal(21,9) and the repost dies
with 'Out of range value for column stock_value'.

Use the in-memory running valuation rate that update_entries_after
already tracks for the warehouse at this point in the repost. It is the
authoritative pre-entry state, is immune to sibling rows, and makes the
repost idempotent. The database lookup is kept only as a fallback for a
zero in-memory rate, preserving the existing zero-rate fallback chain.
This commit is contained in:
Mihir Kandoi
2026-08-08 10:48:24 +05:30
parent 0e26f9b1db
commit ffa65b0c48

View File

@@ -1301,23 +1301,25 @@ class update_entries_after:
and not sle.get("batch_no")
and not sle.get("serial_and_batch_bundle")
):
rate = get_incoming_rate(
{
"item_code": sle.item_code,
"warehouse": sle.warehouse,
"posting_date": sle.posting_date,
"posting_time": sle.posting_time,
"qty": sle.actual_qty,
"serial_no": sle.get("serial_no"),
"batch_no": sle.get("batch_no"),
"serial_and_batch_bundle": sle.get("serial_and_batch_bundle"),
"company": sle.company,
"voucher_type": sle.voucher_type,
"voucher_no": sle.voucher_no,
"allow_zero_valuation": self.allow_zero_rate,
"sle": sle.name,
}
)
rate = flt(self.wh_data.valuation_rate)
if not rate:
rate = get_incoming_rate(
{
"item_code": sle.item_code,
"warehouse": sle.warehouse,
"posting_date": sle.posting_date,
"posting_time": sle.posting_time,
"qty": sle.actual_qty,
"serial_no": sle.get("serial_no"),
"batch_no": sle.get("batch_no"),
"serial_and_batch_bundle": sle.get("serial_and_batch_bundle"),
"company": sle.company,
"voucher_type": sle.voucher_type,
"voucher_no": sle.voucher_no,
"allow_zero_valuation": self.allow_zero_rate,
"sle": sle.name,
}
)
if not rate and sle.voucher_type in ["Delivery Note", "Sales Invoice"]:
rate = get_rate_for_return(