diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 8c06a567e1f..d138ff85897 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -74,15 +74,21 @@ "discount_amount", "discount_percentage", "for_price_list", - "section_break_13", - "threshold_percentage", - "priority", + "dynamic_condition_tab", "condition", - "column_break_66", + "section_break_13", "apply_multiple_pricing_rules", "apply_discount_on_rate", + "column_break_66", + "threshold_percentage", + "validate_pricing_rule_section", "validate_applied_rule", + "column_break_texp", "rule_description", + "priority_section", + "has_priority", + "column_break_sayg", + "priority", "help_section", "pricing_rule_help", "reference_section", @@ -477,7 +483,7 @@ { "collapsible": 1, "fieldname": "section_break_13", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Advanced Settings" }, { @@ -487,6 +493,7 @@ "label": "Threshold for Suggestion (In Percentage)" }, { + "depends_on": "has_priority", "description": "Higher the number, higher the priority", "fieldname": "priority", "fieldtype": "Select", @@ -513,6 +520,7 @@ { "default": "0", "depends_on": "eval:doc.price_or_product_discount == 'Price'", + "description": "If enabled, then system will only validate the pricing rule and not apply automatically. User has to manually set the discount percentage / margin / free items to validate the pricing rule", "fieldname": "validate_applied_rule", "fieldtype": "Check", "label": "Validate Applied Rule" @@ -525,7 +533,8 @@ }, { "fieldname": "help_section", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", + "label": "Help Article", "options": "Simple" }, { @@ -603,12 +612,42 @@ "fieldname": "apply_recursion_over", "fieldtype": "Float", "label": "Apply Recursion Over (As Per Transaction UOM)" + }, + { + "fieldname": "priority_section", + "fieldtype": "Section Break", + "label": "Priority" + }, + { + "fieldname": "dynamic_condition_tab", + "fieldtype": "Tab Break", + "label": "Dynamic Condition" + }, + { + "fieldname": "validate_pricing_rule_section", + "fieldtype": "Section Break", + "label": "Validate Pricing Rule" + }, + { + "fieldname": "column_break_texp", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_sayg", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "Enable this checkbox even if you want to set the zero priority", + "fieldname": "has_priority", + "fieldtype": "Check", + "label": "Has Priority" } ], "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2024-03-27 13:10:17.521896", + "modified": "2024-05-17 13:16:34.496704", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 36c059f2f5d..ac690ceea27 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -27,9 +27,7 @@ class PricingRule(Document): from frappe.types import DF from erpnext.accounts.doctype.pricing_rule_brand.pricing_rule_brand import PricingRuleBrand - from erpnext.accounts.doctype.pricing_rule_item_code.pricing_rule_item_code import ( - PricingRuleItemCode, - ) + from erpnext.accounts.doctype.pricing_rule_item_code.pricing_rule_item_code import PricingRuleItemCode from erpnext.accounts.doctype.pricing_rule_item_group.pricing_rule_item_group import ( PricingRuleItemGroup, ) @@ -67,6 +65,7 @@ class PricingRule(Document): free_item_rate: DF.Currency free_item_uom: DF.Link | None free_qty: DF.Float + has_priority: DF.Check is_cumulative: DF.Check is_recursive: DF.Check item_groups: DF.Table[PricingRuleItemGroup] @@ -156,6 +155,12 @@ class PricingRule(Document): frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on)) def validate_mandatory(self): + if self.has_priority and not self.priority: + throw(_("Priority is mandatory"), frappe.MandatoryError, _("Please Set Priority")) + + if self.priority and not self.has_priority: + self.has_priority = 1 + for apply_on, field in apply_on_dict.items(): if self.apply_on == apply_on and len(self.get(field) or []) < 1: throw(_("{0} is not added in the table").format(apply_on), frappe.MandatoryError) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 5df689e5a2b..b047898b771 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -1157,6 +1157,62 @@ class TestPricingRule(unittest.TestCase): frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + def test_priority_of_multiple_pricing_rules(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule 1", + "name": "_Test Pricing Rule 1", + "apply_on": "Item Code", + "currency": "USD", + "items": [ + { + "item_code": "_Test Item", + } + ], + "selling": 1, + "price_or_product_discount": "Price", + "rate_or_discount": "Discount Percentage", + "discount_percentage": 10, + "has_priority": 1, + "priority": 1, + "company": "_Test Company", + } + + frappe.get_doc(test_record.copy()).insert() + + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule 2", + "name": "_Test Pricing Rule 2", + "apply_on": "Item Code", + "currency": "USD", + "items": [ + { + "item_code": "_Test Item", + } + ], + "selling": 1, + "price_or_product_discount": "Price", + "rate_or_discount": "Discount Percentage", + "discount_percentage": 20, + "has_priority": 1, + "priority": 3, + "company": "_Test Company", + } + + frappe.get_doc(test_record.copy()).insert() + + so = make_sales_order(item_code="_Test Item", qty=1, price_list_rate=1000, do_not_submit=True) + self.assertEqual(so.items[0].discount_percentage, 20) + self.assertEqual(so.items[0].rate, 800) + + frappe.delete_doc_if_exists("Sales Order", so.name) + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + test_dependencies = ["Campaign"] @@ -1185,6 +1241,7 @@ def make_pricing_rule(**args): "priority": args.priority or 1, "discount_amount": args.discount_amount or 0.0, "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0, + "has_priority": args.has_priority or 0, } ) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 6429f29a388..32221b6d9a8 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -33,6 +33,9 @@ def get_pricing_rules(args, doc=None): for apply_on in ["Item Code", "Item Group", "Brand"]: pricing_rules.extend(_get_pricing_rules(apply_on, args, values)) + if pricing_rules and pricing_rules[0].has_priority: + continue + if pricing_rules and not apply_multiple_pricing_rules(pricing_rules): break diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 09e7c7c109b..f58737712f8 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -366,3 +366,4 @@ erpnext.patches.v15_0.delete_orphaned_asset_movement_item_records erpnext.patches.v15_0.remove_cancelled_asset_capitalization_from_asset erpnext.patches.v15_0.fix_debit_credit_in_transaction_currency erpnext.patches.v15_0.rename_purchase_receipt_amount_to_purchase_amount +erpnext.patches.v14_0.enable_set_priority_for_pricing_rules #1 diff --git a/erpnext/patches/v14_0/enable_set_priority_for_pricing_rules.py b/erpnext/patches/v14_0/enable_set_priority_for_pricing_rules.py new file mode 100644 index 00000000000..af87eeb2727 --- /dev/null +++ b/erpnext/patches/v14_0/enable_set_priority_for_pricing_rules.py @@ -0,0 +1,10 @@ +import frappe + + +def execute(): + pr_table = frappe.qb.DocType("Pricing Rule") + ( + frappe.qb.update(pr_table) + .set(pr_table.has_priority, 1) + .where((pr_table.priority.isnotnull()) & (pr_table.priority != "")) + ).run()