fix: Discount and taxes in return document should follow the reference document (backport #41911) (#42574)
fix: Discount and taxes in return document should follow the reference document (#41911)
* fix: Discount and taxes in return document should follow the reference document
* fix: Ignore Pricing rule on debit/credit note if created against PI/SI with test cases
* fix: linter issue
(cherry picked from commit 281198456d)
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
This commit is contained in:
@@ -6,7 +6,9 @@ import unittest
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
@@ -1311,6 +1313,69 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
pricing_rule.is_recursive = True
|
pricing_rule.is_recursive = True
|
||||||
self.assertRaises(frappe.ValidationError, pricing_rule.save)
|
self.assertRaises(frappe.ValidationError, pricing_rule.save)
|
||||||
|
|
||||||
|
def test_ignore_pricing_rule_for_credit_note(self):
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
|
||||||
|
pricing_rule = make_pricing_rule(
|
||||||
|
discount_percentage=20,
|
||||||
|
selling=1,
|
||||||
|
buying=1,
|
||||||
|
priority=1,
|
||||||
|
title="_Test Pricing Rule",
|
||||||
|
)
|
||||||
|
|
||||||
|
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
|
||||||
|
item = si.items[0]
|
||||||
|
si.submit()
|
||||||
|
self.assertEqual(item.discount_percentage, 20)
|
||||||
|
self.assertEqual(item.rate, 80)
|
||||||
|
|
||||||
|
# change discount on pricing rule
|
||||||
|
pricing_rule.discount_percentage = 30
|
||||||
|
pricing_rule.save()
|
||||||
|
|
||||||
|
credit_note = make_return_doc(si.doctype, si.name)
|
||||||
|
credit_note.save()
|
||||||
|
self.assertEqual(credit_note.ignore_pricing_rule, 1)
|
||||||
|
self.assertEqual(credit_note.pricing_rules, [])
|
||||||
|
self.assertEqual(credit_note.items[0].discount_percentage, 20)
|
||||||
|
self.assertEqual(credit_note.items[0].rate, 80)
|
||||||
|
self.assertEqual(credit_note.items[0].pricing_rules, None)
|
||||||
|
|
||||||
|
credit_note.delete()
|
||||||
|
si.cancel()
|
||||||
|
|
||||||
|
def test_ignore_pricing_rule_for_debit_note(self):
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
|
||||||
|
pricing_rule = make_pricing_rule(
|
||||||
|
discount_percentage=20,
|
||||||
|
buying=1,
|
||||||
|
priority=1,
|
||||||
|
title="_Test Pricing Rule",
|
||||||
|
)
|
||||||
|
|
||||||
|
pi = make_purchase_invoice(do_not_submit=True, supplier="_Test Supplier 1", qty=1)
|
||||||
|
item = pi.items[0]
|
||||||
|
pi.submit()
|
||||||
|
self.assertEqual(item.discount_percentage, 20)
|
||||||
|
self.assertEqual(item.rate, 40)
|
||||||
|
|
||||||
|
# change discount on pricing rule
|
||||||
|
pricing_rule.discount_percentage = 30
|
||||||
|
pricing_rule.save()
|
||||||
|
|
||||||
|
# create debit note from purchase invoice
|
||||||
|
debit_note = make_return_doc(pi.doctype, pi.name)
|
||||||
|
debit_note.save()
|
||||||
|
|
||||||
|
self.assertEqual(debit_note.ignore_pricing_rule, 1)
|
||||||
|
self.assertEqual(debit_note.pricing_rules, [])
|
||||||
|
self.assertEqual(debit_note.items[0].discount_percentage, 20)
|
||||||
|
self.assertEqual(debit_note.items[0].rate, 40)
|
||||||
|
self.assertEqual(debit_note.items[0].pricing_rules, None)
|
||||||
|
|
||||||
|
debit_note.delete()
|
||||||
|
pi.cancel()
|
||||||
|
|
||||||
|
|
||||||
test_dependencies = ["Campaign"]
|
test_dependencies = ["Campaign"]
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ force_item_fields = (
|
|||||||
"brand",
|
"brand",
|
||||||
"stock_uom",
|
"stock_uom",
|
||||||
"is_fixed_asset",
|
"is_fixed_asset",
|
||||||
"item_tax_rate",
|
|
||||||
"pricing_rules",
|
"pricing_rules",
|
||||||
"weight_per_unit",
|
"weight_per_unit",
|
||||||
"weight_uom",
|
"weight_uom",
|
||||||
@@ -743,7 +742,6 @@ class AccountsController(TransactionBase):
|
|||||||
args["is_subcontracted"] = self.is_subcontracted
|
args["is_subcontracted"] = self.is_subcontracted
|
||||||
|
|
||||||
ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False)
|
ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False)
|
||||||
|
|
||||||
for fieldname, value in ret.items():
|
for fieldname, value in ret.items():
|
||||||
if item.meta.get_field(fieldname) and value is not None:
|
if item.meta.get_field(fieldname) and value is not None:
|
||||||
if item.get(fieldname) is None or fieldname in force_item_fields:
|
if item.get(fieldname) is None or fieldname in force_item_fields:
|
||||||
@@ -753,7 +751,10 @@ class AccountsController(TransactionBase):
|
|||||||
fieldname
|
fieldname
|
||||||
):
|
):
|
||||||
item.set(fieldname, value)
|
item.set(fieldname, value)
|
||||||
|
elif fieldname == "item_tax_rate" and not (
|
||||||
|
self.get("is_return") and self.get("return_against")
|
||||||
|
):
|
||||||
|
item.set(fieldname, value)
|
||||||
elif fieldname == "serial_no":
|
elif fieldname == "serial_no":
|
||||||
# Ensure that serial numbers are matched against Stock UOM
|
# Ensure that serial numbers are matched against Stock UOM
|
||||||
item_conversion_factor = item.get("conversion_factor") or 1.0
|
item_conversion_factor = item.get("conversion_factor") or 1.0
|
||||||
|
|||||||
@@ -319,6 +319,8 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
|
|||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
doc = frappe.get_doc(target)
|
doc = frappe.get_doc(target)
|
||||||
doc.is_return = 1
|
doc.is_return = 1
|
||||||
|
doc.ignore_pricing_rule = 1
|
||||||
|
doc.pricing_rules = []
|
||||||
doc.return_against = source.name
|
doc.return_against = source.name
|
||||||
doc.set_warehouse = ""
|
doc.set_warehouse = ""
|
||||||
if doctype == "Sales Invoice" or doctype == "POS Invoice":
|
if doctype == "Sales Invoice" or doctype == "POS Invoice":
|
||||||
@@ -478,6 +480,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
|
|||||||
|
|
||||||
def update_item(source_doc, target_doc, source_parent):
|
def update_item(source_doc, target_doc, source_parent):
|
||||||
target_doc.qty = -1 * source_doc.qty
|
target_doc.qty = -1 * source_doc.qty
|
||||||
|
target_doc.pricing_rules = None
|
||||||
if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
|
if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
|
||||||
returned_qty_map = get_returned_qty_map_for_row(
|
returned_qty_map = get_returned_qty_map_for_row(
|
||||||
source_parent.name, source_parent.supplier, source_doc.name, doctype
|
source_parent.name, source_parent.supplier, source_doc.name, doctype
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ class calculate_taxes_and_totals:
|
|||||||
self.doc.base_tax_withholding_net_total = sum_base_net_amount
|
self.doc.base_tax_withholding_net_total = sum_base_net_amount
|
||||||
|
|
||||||
def validate_item_tax_template(self):
|
def validate_item_tax_template(self):
|
||||||
|
if self.doc.get("is_return") and self.doc.get("return_against"):
|
||||||
|
return
|
||||||
|
|
||||||
for item in self._items:
|
for item in self._items:
|
||||||
if item.item_code and item.get("item_tax_template"):
|
if item.item_code and item.get("item_tax_template"):
|
||||||
item_doc = frappe.get_cached_doc("Item", item.item_code)
|
item_doc = frappe.get_cached_doc("Item", item.item_code)
|
||||||
@@ -241,7 +244,6 @@ class calculate_taxes_and_totals:
|
|||||||
"tax_fraction_for_current_item",
|
"tax_fraction_for_current_item",
|
||||||
"grand_total_fraction_for_current_item",
|
"grand_total_fraction_for_current_item",
|
||||||
]
|
]
|
||||||
|
|
||||||
if tax.charge_type != "Actual" and not (
|
if tax.charge_type != "Actual" and not (
|
||||||
self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
|
self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -1978,6 +1978,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
let item_rates = {};
|
let item_rates = {};
|
||||||
let item_tax_templates = {};
|
let item_tax_templates = {};
|
||||||
|
|
||||||
|
if (me.frm.doc.is_return && me.frm.doc.return_against) return;
|
||||||
|
|
||||||
$.each(this.frm.doc.items || [], function(i, item) {
|
$.each(this.frm.doc.items || [], function(i, item) {
|
||||||
if (item.item_code) {
|
if (item.item_code) {
|
||||||
// Use combination of name and item code in case same item is added multiple times
|
// Use combination of name and item code in case same item is added multiple times
|
||||||
|
|||||||
Reference in New Issue
Block a user