From 4602b2e6c85ebe990fea455abe60b552f65641ac Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 1 Aug 2024 14:51:01 +0530 Subject: [PATCH 1/2] 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 --- .../doctype/pricing_rule/test_pricing_rule.py | 65 +++++++++++++++++++ erpnext/controllers/accounts_controller.py | 7 +- .../controllers/sales_and_purchase_return.py | 3 + erpnext/controllers/taxes_and_totals.py | 4 +- erpnext/public/js/controllers/transaction.js | 2 + 5 files changed, 77 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index bcc55c82a5..e8e36b4673 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -6,7 +6,9 @@ import unittest 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.controllers.sales_and_purchase_return import make_return_doc 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.get_item_details import get_item_details @@ -1378,6 +1380,69 @@ class TestPricingRule(unittest.TestCase): pricing_rule.is_recursive = True 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"] diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index f2a2895ab0..73aa5eab8e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -85,7 +85,6 @@ force_item_fields = ( "brand", "stock_uom", "is_fixed_asset", - "item_tax_rate", "pricing_rules", "weight_per_unit", "weight_uom", @@ -729,7 +728,6 @@ class AccountsController(TransactionBase): args["is_subcontracted"] = self.is_subcontracted ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False) - for fieldname, value in ret.items(): if item.meta.get_field(fieldname) and value is not None: if item.get(fieldname) is None or fieldname in force_item_fields: @@ -739,7 +737,10 @@ class AccountsController(TransactionBase): fieldname ): 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": # Ensure that serial numbers are matched against Stock UOM item_conversion_factor = item.get("conversion_factor") or 1.0 diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index c7a72a0c78..b9492c1cc6 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -319,6 +319,8 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai def set_missing_values(source, target): doc = frappe.get_doc(target) doc.is_return = 1 + doc.ignore_pricing_rule = 1 + doc.pricing_rules = [] doc.return_against = source.name doc.set_warehouse = "" 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): target_doc.qty = -1 * source_doc.qty + target_doc.pricing_rules = None if doctype in ["Purchase Receipt", "Subcontracting Receipt"]: returned_qty_map = get_returned_qty_map_for_row( source_parent.name, source_parent.supplier, source_doc.name, doctype diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index a130ef4926..5e6dfe114d 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -93,6 +93,9 @@ class calculate_taxes_and_totals: self.doc.base_tax_withholding_net_total = sum_base_net_amount def validate_item_tax_template(self): + if self.doc.get("is_return") and self.doc.get("return_against"): + return + for item in self._items: if item.item_code and item.get("item_tax_template"): item_doc = frappe.get_cached_doc("Item", item.item_code) @@ -243,7 +246,6 @@ class calculate_taxes_and_totals: "tax_fraction_for_current_item", "grand_total_fraction_for_current_item", ] - if tax.charge_type != "Actual" and not ( self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total" ): diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 16f1853015..b5b6cb774c 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2039,6 +2039,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe let item_rates = {}; let item_tax_templates = {}; + if (me.frm.doc.is_return && me.frm.doc.return_against) return; + $.each(this.frm.doc.items || [], function(i, item) { if (item.item_code) { // Use combination of name and item code in case same item is added multiple times -- GitLab From 6cb392e284dd01a5e63487309b61c5e3e32ee07e Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Tue, 6 Aug 2024 07:59:17 +0000 Subject: [PATCH 2/2] fix: reload documents before cancel --- erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index e8e36b4673..4c1d615899 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -1409,6 +1409,7 @@ class TestPricingRule(unittest.TestCase): self.assertEqual(credit_note.items[0].pricing_rules, None) credit_note.delete() + si.reload() si.cancel() def test_ignore_pricing_rule_for_debit_note(self): @@ -1441,6 +1442,7 @@ class TestPricingRule(unittest.TestCase): self.assertEqual(debit_note.items[0].pricing_rules, None) debit_note.delete() + pi.reload() pi.cancel() -- GitLab