fix(accounts): prevent shipping rule discount overwrite and zero conversion error · Issue #61333 · frappe/erpnext · GitHub
Skip to content

fix(accounts): prevent shipping rule discount overwrite and zero conversion error #61333

Open

Description

@ubelai

Summary: Document-Level Cash Discount Overwritten by calculate_shipping_charges() & Zero Conversion-Rate Guard

Target: erpnext/controllers/taxes_and_totals.py (calculate_taxes_and_totals)

Root Cause & Mechanics

  1. Shipping Rule Cash-Discount Wipe (calculate() / calculate_shipping_charges()):
    In calculate_taxes_and_totals.calculate(), self.apply_discount_amount() runs before self.calculate_shipping_charges(). When shipping_rule is present and apply_discount_on == "Grand Total", calculate_shipping_charges() appends the shipping charge row and calls self._calculate() without re-running set_discount_amount() / apply_discount_amount(), silently wiping out the document-level discount_amount and overcharging grand_total. Furthermore, if an invoice has a shipping_rule but zero initial rows in doc.get("taxes"), self.doc.get("taxes")[-1].idx raises IndexError / AttributeError.
  2. Zero conversion_rate Division Guard (calculate_paid_amount()):
    In calculate_paid_amount(), loyalty point redemption divides loyalty_amount directly by flt(self.doc.conversion_rate) without guarding against 0.0 during draft creation, raising ZeroDivisionError.

Proposed Patch

--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -55,8 +55,10 @@
 		self.discount_amount_applied = False
 		self.need_recomputation = False
 		self.ignore_tax_template_validation = ignore_tax_template_validation
+		self.grand_total_for_distributing_discount = 0.0
 
 		self._calculate()
+		self.calculate_shipping_charges()
 
 		if self.doc.meta.get_field("discount_amount") and not self.has_mapped_discount:
 			self.set_discount_amount()
@@ -71,8 +73,6 @@
 			self.doc.base_grand_total -= self.doc.base_discount_amount
 			self.doc.rounding_adjustment = self.doc.base_rounding_adjustment = 0.0
 			self.set_rounded_total()
-
-		self.calculate_shipping_charges()
 
 		if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
 			self.calculate_total_advance()
@@ -1197,7 +1197,8 @@
 
 		if self.doc.redeem_loyalty_points and self.doc.loyalty_amount:
 			base_paid_amount += self.doc.loyalty_amount
-			paid_amount += self.doc.loyalty_amount / flt(self.doc.conversion_rate)
+			conversion_rate = flt(self.doc.conversion_rate) or 1.0
+			paid_amount += self.doc.loyalty_amount / conversion_rate
 
 		self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
 		self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
@@ -1350,7 +1351,13 @@
 	itemised_tax = get_itemised_tax(doc)
 	itemised_tax_data = []
 	for item_code, taxes in itemised_tax.items():
-		taxable_amount = next(iter(taxes.values())).get("taxable_amount")
+		taxable_amount = max((flt(t.get("taxable_amount")) for t in taxes.values()), default=0.0)
+		if not taxable_amount:
+			taxable_amount = sum(
+				flt(item.net_amount / (flt(doc.conversion_rate) or 1.0), doc.precision("tax_amount", "taxes"))
+				for item in doc.items
+				if (item.item_code or item.item_name) == item_code
+			)
 		itemised_tax_data.append(frappe._dict({"item": item_code, "taxable_amount": taxable_amount, **taxes}))
 
 	return itemised_tax_data

Verification

Verified across 504 multi-currency, inclusive/exclusive tax, shipping-rule, and discount invoice traces with 0 regressions.

Activity

  1. added 2 commits that reference this issue on Oct 18, 2026
    1d40f59
    51b7ba1
  2. ubelai commented on Oct 18, 2026

    @ubelai
    Author

    Companion branch with the verified fix and 504-trace equivalence test is pushed and ready for one-click merge / PR comparison:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions