From ab57ee9d1fb11e10cd0ceaae2af4baa11dca8f8e Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Tue, 7 Jan 2025 08:02:12 +0100 Subject: [PATCH 1/6] feat: changes in taxes and totals py for enabling multiple margin pricing rules --- erpnext/controllers/taxes_and_totals.py | 27 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index ca0b037ea5b0..cb6a262ab9d2 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -962,19 +962,28 @@ def calculate_margin(self, item): if item.price_list_rate: if item.pricing_rules and not self.doc.ignore_pricing_rule: has_margin = False + item.margin_rate_or_amount = 0 + for d in get_applied_pricing_rules(item.pricing_rules): pricing_rule = frappe.get_cached_doc("Pricing Rule", d) - if pricing_rule.margin_rate_or_amount and ( - ( - pricing_rule.currency == self.doc.currency - and pricing_rule.margin_type in ["Amount", "Percentage"] - ) - or pricing_rule.margin_type == "Percentage" + if ( + pricing_rule.margin_rate_or_amount + and pricing_rule.currency == self.doc.currency ): - item.margin_type = pricing_rule.margin_type - item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount - has_margin = True + if pricing_rule.margin_type == "Percentage": + item.margin_type = "Amount" + item.margin_rate_or_amount += item.price_list_rate * ( + pricing_rule.margin_rate_or_amount / 100 + ) + has_margin = True + + elif pricing_rule.margin_type == "Amount": + item.margin_type = "Amount" + item.margin_rate_or_amount += ( + pricing_rule.margin_rate_or_amount + ) + has_margin = True if not has_margin: item.margin_type = None From 9576ce0119227aaf3d7fe1ad25f44d76813e67d6 Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Tue, 7 Jan 2025 12:02:28 +0100 Subject: [PATCH 2/6] feat: changes in code for single and multiple pricing rules --- erpnext/controllers/taxes_and_totals.py | 55 +++++++++++++++++-------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index cb6a262ab9d2..d7b6fb672298 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -962,29 +962,50 @@ def calculate_margin(self, item): if item.price_list_rate: if item.pricing_rules and not self.doc.ignore_pricing_rule: has_margin = False - item.margin_rate_or_amount = 0 - for d in get_applied_pricing_rules(item.pricing_rules): - pricing_rule = frappe.get_cached_doc("Pricing Rule", d) + applied_pricing_rules = get_applied_pricing_rules(item.pricing_rules) - if ( - pricing_rule.margin_rate_or_amount - and pricing_rule.currency == self.doc.currency - ): - if pricing_rule.margin_type == "Percentage": - item.margin_type = "Amount" - item.margin_rate_or_amount += item.price_list_rate * ( - pricing_rule.margin_rate_or_amount / 100 - ) - has_margin = True + # If only one pricing rule is applied + if len(applied_pricing_rules) == 1: + + for d in applied_pricing_rules: + pricing_rule = frappe.get_cached_doc("Pricing Rule", d) - elif pricing_rule.margin_type == "Amount": - item.margin_type = "Amount" - item.margin_rate_or_amount += ( - pricing_rule.margin_rate_or_amount + if pricing_rule.margin_rate_or_amount and ( + ( + pricing_rule.currency == self.doc.currency + and pricing_rule.margin_type in ["Amount", "Percentage"] ) + or pricing_rule.margin_type == "Percentage" + ): + item.margin_type = pricing_rule.margin_type + item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount has_margin = True + # If multiple pricing rules are applied + if len(applied_pricing_rules) > 1: + + item.margin_rate_or_amount = 0 + for d in applied_pricing_rules: + pricing_rule = frappe.get_cached_doc("Pricing Rule", d) + + if ( + pricing_rule.margin_rate_or_amount + ): + if pricing_rule.margin_type == "Percentage": + item.margin_type = "Amount" + item.margin_rate_or_amount += item.price_list_rate * ( + pricing_rule.margin_rate_or_amount / 100 + ) + has_margin = True + + elif pricing_rule.margin_type == "Amount" and pricing_rule.currency == self.doc.currency: + item.margin_type = "Amount" + item.margin_rate_or_amount += ( + pricing_rule.margin_rate_or_amount + ) + has_margin = True + if not has_margin: item.margin_type = None item.margin_rate_or_amount = 0.0 From 2bc858d54ad1d0d78c7b81fd806042e9de77c2bb Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Tue, 7 Jan 2025 12:29:50 +0100 Subject: [PATCH 3/6] feat: pre commit code beautifier --- erpnext/controllers/taxes_and_totals.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index d7b6fb672298..3a980d316ec6 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -967,7 +967,6 @@ def calculate_margin(self, item): # If only one pricing rule is applied if len(applied_pricing_rules) == 1: - for d in applied_pricing_rules: pricing_rule = frappe.get_cached_doc("Pricing Rule", d) @@ -984,14 +983,11 @@ def calculate_margin(self, item): # If multiple pricing rules are applied if len(applied_pricing_rules) > 1: - item.margin_rate_or_amount = 0 for d in applied_pricing_rules: pricing_rule = frappe.get_cached_doc("Pricing Rule", d) - if ( - pricing_rule.margin_rate_or_amount - ): + if pricing_rule.margin_rate_or_amount: if pricing_rule.margin_type == "Percentage": item.margin_type = "Amount" item.margin_rate_or_amount += item.price_list_rate * ( @@ -999,11 +995,12 @@ def calculate_margin(self, item): ) has_margin = True - elif pricing_rule.margin_type == "Amount" and pricing_rule.currency == self.doc.currency: + elif ( + pricing_rule.margin_type == "Amount" + and pricing_rule.currency == self.doc.currency + ): item.margin_type = "Amount" - item.margin_rate_or_amount += ( - pricing_rule.margin_rate_or_amount - ) + item.margin_rate_or_amount += pricing_rule.margin_rate_or_amount has_margin = True if not has_margin: From 1e0681520508d877a97f0c7670e856338489d96e Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Tue, 7 Jan 2025 14:00:04 +0100 Subject: [PATCH 4/6] feat: push unit test --- .../doctype/pricing_rule/test_pricing_rule.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 9787fda86d3e..f7f02270a27a 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -375,6 +375,41 @@ def test_pricing_rule_with_margin_and_discount_amount(self): self.assertEqual(item.discount_amount, 110) self.assertEqual(item.rate, 990) + def test_pricing_rule_with_multiple_margins(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule") + make_pricing_rule( + selling=1, + margin_type="Percentage", + margin_rate_or_amount=10, + rate_or_discount="Discount Amount", + discount_amount=0, + ) + make_pricing_rule( + selling=1, + margin_type="Percentage", + margin_rate_or_amount=5, + rate_or_discount="Discount Amount", + discount_amount=0, + ) + make_pricing_rule( + selling=1, + margin_type="Amount", + margin_rate_or_amount=100, + rate_or_discount="Discount Amount", + discount_amount=0, + ) + + si = create_sales_invoice(do_not_save=True) + si.items[0].price_list_rate = 1000 + si.payment_schedule = [] + si.insert(ignore_permissions=True) + + item = si.items[0] + self.assertEqual(item.margin_type, "Amount") + self.assertEqual(item.margin_rate_or_amount, 250) + self.assertEqual(item.rate_with_margin, 1250) + self.assertEqual(item.rate, 1250) + def test_pricing_rule_for_product_discount_on_same_item(self): frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule") test_record = { From d62c97c609750e1c3f41fd7e3046c4e3f0d0c64d Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Tue, 7 Jan 2025 14:19:43 +0100 Subject: [PATCH 5/6] feat: apply multiple pricing rules --- erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index f7f02270a27a..a39d943cdd81 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -382,6 +382,7 @@ def test_pricing_rule_with_multiple_margins(self): margin_type="Percentage", margin_rate_or_amount=10, rate_or_discount="Discount Amount", + apply_multiple_pricing_rules=1, discount_amount=0, ) make_pricing_rule( @@ -389,6 +390,7 @@ def test_pricing_rule_with_multiple_margins(self): margin_type="Percentage", margin_rate_or_amount=5, rate_or_discount="Discount Amount", + apply_multiple_pricing_rules=1, discount_amount=0, ) make_pricing_rule( @@ -396,6 +398,7 @@ def test_pricing_rule_with_multiple_margins(self): margin_type="Amount", margin_rate_or_amount=100, rate_or_discount="Discount Amount", + apply_multiple_pricing_rules=1, discount_amount=0, ) From 1cc19b2873960a75f04e59dc2f6aad94458810fe Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Tue, 7 Jan 2025 14:20:44 +0100 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20apply=20multip=C3=BCle=20pricing=20?= =?UTF-8?q?rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index a39d943cdd81..7b73fca7bf94 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -390,16 +390,16 @@ def test_pricing_rule_with_multiple_margins(self): margin_type="Percentage", margin_rate_or_amount=5, rate_or_discount="Discount Amount", - apply_multiple_pricing_rules=1, discount_amount=0, + apply_multiple_pricing_rules=1, ) make_pricing_rule( selling=1, margin_type="Amount", margin_rate_or_amount=100, rate_or_discount="Discount Amount", - apply_multiple_pricing_rules=1, discount_amount=0, + apply_multiple_pricing_rules=1, ) si = create_sales_invoice(do_not_save=True)