mirror of
https://github.com/frappe/erpnext.git
synced 2026-02-17 16:45:02 +00:00
fix: handle returns as well
This commit is contained in:
@@ -2965,29 +2965,46 @@ class AccountsController(TransactionBase):
|
||||
return
|
||||
|
||||
item_doctype = self.meta.get_field("items").options
|
||||
item_meta = frappe.get_meta(item_doctype)
|
||||
|
||||
reference_fieldname = next(
|
||||
(
|
||||
row.fieldname
|
||||
for row in item_meta.fields
|
||||
if row.fieldtype == "Link"
|
||||
and row.options == source_doc.doctype
|
||||
and not row.get("is_custom_field")
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if not reference_fieldname:
|
||||
return
|
||||
|
||||
doctype_table = frappe.qb.DocType(self.doctype)
|
||||
item_table = frappe.qb.DocType(item_doctype)
|
||||
discount_already_applied = (
|
||||
|
||||
is_same_doctype = self.doctype == source_doc.doctype
|
||||
is_return = self.get("is_return") and is_same_doctype
|
||||
|
||||
if is_same_doctype and not is_return:
|
||||
# should never happen
|
||||
# you don't map to the same doctype without it being a return
|
||||
return
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(doctype_table)
|
||||
.where(doctype_table.docstatus == 1)
|
||||
.where(doctype_table.discount_amount != 0)
|
||||
.where(
|
||||
.select(Sum(doctype_table.discount_amount))
|
||||
)
|
||||
|
||||
if is_return:
|
||||
query = query.where(doctype_table.is_return == 1).where(
|
||||
doctype_table.return_against == source_doc.name
|
||||
)
|
||||
|
||||
else:
|
||||
item_meta = frappe.get_meta(item_doctype)
|
||||
reference_fieldname = next(
|
||||
(
|
||||
row.fieldname
|
||||
for row in item_meta.fields
|
||||
if row.fieldtype == "Link"
|
||||
and row.options == source_doc.doctype
|
||||
and not row.get("is_custom_field")
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if not reference_fieldname:
|
||||
return
|
||||
|
||||
query = query.where(
|
||||
doctype_table.name.isin(
|
||||
frappe.qb.from_(item_table)
|
||||
.select(item_table.parent)
|
||||
@@ -2995,20 +3012,29 @@ class AccountsController(TransactionBase):
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
.select(Sum(doctype_table.discount_amount))
|
||||
).run()
|
||||
|
||||
result = query.run()
|
||||
if not result:
|
||||
return
|
||||
|
||||
discount_already_applied = result[0][0]
|
||||
if not discount_already_applied:
|
||||
return
|
||||
|
||||
discount_already_applied = flt(discount_already_applied[0][0], self.precision("discount_amount"))
|
||||
if is_return:
|
||||
# returns have negative discount
|
||||
discount_already_applied *= -1
|
||||
|
||||
if (source_doc.discount_amount * (discount_already_applied - source_doc.discount_amount)) >= 0:
|
||||
# full discount already applied or exceeded
|
||||
self.discount_amount = 0
|
||||
else:
|
||||
self.discount_amount = flt(
|
||||
self.discount_amount - discount_already_applied, self.precision("discount_amount")
|
||||
)
|
||||
discount_amount = source_doc.discount_amount - discount_already_applied
|
||||
if is_return:
|
||||
# returns have negative discount
|
||||
discount_amount *= -1
|
||||
|
||||
self.discount_amount = flt(discount_amount, self.precision("discount_amount"))
|
||||
|
||||
self.calculate_taxes_and_totals()
|
||||
|
||||
|
||||
@@ -2400,3 +2400,35 @@ class TestAccountsController(IntegrationTestCase):
|
||||
# and not affected by the repeated mapping logic
|
||||
self.assertEqual(dn.additional_discount_percentage, 10)
|
||||
self.assertEqual(dn.discount_amount, 50) # 10% of 500
|
||||
|
||||
def test_discount_amount_for_multiple_returns(self):
|
||||
"""
|
||||
Test that discount amount is correctly adjusted when multiple return invoices
|
||||
are created against the same original invoice to prevent over-returning discount
|
||||
"""
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
|
||||
|
||||
# Create original sales invoice with discount
|
||||
si = create_sales_invoice(qty=10, rate=100, do_not_submit=True)
|
||||
si.apply_discount_on = "Net Total"
|
||||
si.discount_amount = 100
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
# Create first return - Frappe will copy full discount by default, we need to adjust it
|
||||
return_si_1 = make_sales_return(si.name)
|
||||
return_si_1.items[0].qty = -6 # Return 6 out of 10 items
|
||||
# Manually set discount to match the proportion (60% of discount)
|
||||
return_si_1.discount_amount = -60
|
||||
return_si_1.save()
|
||||
return_si_1.submit()
|
||||
|
||||
self.assertEqual(return_si_1.discount_amount, -60)
|
||||
|
||||
# Create second return for remaining items
|
||||
return_si_2 = make_sales_return(si.name)
|
||||
return_si_2.items[0].qty = -4 # Return remaining 4 out of 10 items
|
||||
return_si_2.save()
|
||||
|
||||
# Second return should only get remaining discount (100 - 60 = 40)
|
||||
self.assertEqual(return_si_2.discount_amount, -40)
|
||||
|
||||
Reference in New Issue
Block a user