fix: mirror the magnitude comparison in the close dialog

The server started treating billing amounts as magnitudes so return rows stay
closable, but the client kept the signed comparison. An unbilled return row was
filtered out of the Close Items dialog and hid the button entirely, so the row
the server would accept could not be reached from the form.

The pending amount shown in the dialog had the same flaw and would have
displayed zero outstanding on a row with a full amount left to credit.

Verified on a return Delivery Note: the row now appears with amount -500 and
pending amount 500.
This commit is contained in:
Mihir Kandoi
2026-07-29 16:24:44 +05:30
parent 27dffd916d
commit 24b5a531bc
2 changed files with 20 additions and 4 deletions

View File

@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
import frappe
from frappe.utils import flt
from erpnext.controllers.item_close import update_closed_status
from erpnext.controllers.sales_and_purchase_return import make_return_doc
@@ -201,3 +202,16 @@ class TestDeliveryNoteItemClose(ERPNextTestSuite):
self.close_items(return_note, [row])
self.assertTrue(return_note.items[0].closed)
def test_return_row_pending_amount_is_a_magnitude(self):
"""The dialog shows what is outstanding, so a return row must not read as zero."""
note = self.make_delivery_note()
return_note = make_return_doc("Delivery Note", note.name)
return_note.insert()
return_note.submit()
row = return_note.items[0]
self.assertLess(row.amount, 0)
pending = abs(flt(row.amount)) - abs(flt(row.billed_amt))
self.assertEqual(pending, abs(flt(note.items[0].amount)))
self.assertGreater(pending, 0)

View File

@@ -80,7 +80,8 @@ erpnext.item_close = {
return {
is_closable: (item) =>
!item.closed &&
(flt(item[qty_field]) < flt(item.qty) || flt(item.billed_amt) < flt(item.amount)),
(flt(item[qty_field]) < flt(item.qty) ||
Math.abs(flt(item.billed_amt)) < Math.abs(flt(item.amount))),
help: help,
summarise: (item) => ({
item_code: item.item_code,
@@ -88,7 +89,7 @@ erpnext.item_close = {
qty: item.qty,
fulfilled_qty: item[qty_field] || 0,
pending_qty: Math.max(flt(item.qty) - flt(item[qty_field]), 0),
pending_amount: Math.max(flt(item.amount) - flt(item.billed_amt), 0),
pending_amount: Math.max(Math.abs(flt(item.amount)) - Math.abs(flt(item.billed_amt)), 0),
}),
columns: [
erpnext.item_close.column("item_code", __("Item Code"), "Data", 3),
@@ -103,7 +104,8 @@ erpnext.item_close = {
billing_config(invoice_label) {
return {
is_closable: (item) => !item.closed && flt(item.billed_amt) < flt(item.amount),
is_closable: (item) =>
!item.closed && Math.abs(flt(item.billed_amt)) < Math.abs(flt(item.amount)),
help: __(
"Closed rows stop being expected. Their unbilled amount is written off and they are skipped when creating a {0}.",
[invoice_label]
@@ -114,7 +116,7 @@ erpnext.item_close = {
qty: item.qty,
amount: item.amount,
billed_amt: item.billed_amt || 0,
pending_amount: Math.max(flt(item.amount) - flt(item.billed_amt), 0),
pending_amount: Math.max(Math.abs(flt(item.amount)) - Math.abs(flt(item.billed_amt)), 0),
}),
columns: [
erpnext.item_close.column("item_code", __("Item Code"), "Data", 3),