diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 41e10afa26f8e07d69167cd1f8e18cf7b9f5491e..d08a45f0c3e6669f529cdf80835c4662dfbf0292 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 828a46dd41c61e9e459a9c46a3deb969074bf543..c002c322d1c0cd9bbcb904bebbbc3c2fdda541f4 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -17,6 +17,114 @@ other_fields = ["other_item_code", "other_item_group", "other_brand"] class PricingRule(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + 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_group.pricing_rule_item_group import ( + PricingRuleItemGroup, + ) + + applicable_for: DF.Literal[ + "", + "Customer", + "Customer Group", + "Territory", + "Sales Partner", + "Campaign", + "Supplier", + "Supplier Group", + ] + apply_discount_on: DF.Literal["Grand Total", "Net Total"] + apply_discount_on_rate: DF.Check + apply_multiple_pricing_rules: DF.Check + apply_on: DF.Literal["", "Item Code", "Item Group", "Brand", "Transaction"] + apply_recursion_over: DF.Float + apply_rule_on_other: DF.Literal["", "Item Code", "Item Group", "Brand"] + brands: DF.Table[PricingRuleBrand] + buying: DF.Check + campaign: DF.Link | None + company: DF.Link | None + condition: DF.Code | None + coupon_code_based: DF.Check + currency: DF.Link + customer: DF.Link | None + customer_group: DF.Link | None + disable: DF.Check + discount_amount: DF.Currency + discount_percentage: DF.Float + for_price_list: DF.Link | None + free_item: DF.Link | None + 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] + items: DF.Table[PricingRuleItemCode] + margin_rate_or_amount: DF.Float + margin_type: DF.Literal["", "Percentage", "Amount"] + max_amt: DF.Currency + max_qty: DF.Float + min_amt: DF.Currency + min_qty: DF.Float + mixed_conditions: DF.Check + naming_series: DF.Literal["PRLE-.####"] + other_brand: DF.Link | None + other_item_code: DF.Link | None + other_item_group: DF.Link | None + price_or_product_discount: DF.Literal["Price", "Product"] + priority: DF.Literal[ + "", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + ] + promotional_scheme: DF.Link | None + promotional_scheme_id: DF.Data | None + rate: DF.Currency + rate_or_discount: DF.Literal["", "Rate", "Discount Percentage", "Discount Amount"] + recurse_for: DF.Float + round_free_qty: DF.Check + rule_description: DF.SmallText | None + sales_partner: DF.Link | None + same_item: DF.Check + selling: DF.Check + supplier: DF.Link | None + supplier_group: DF.Link | None + territory: DF.Link | None + threshold_percentage: DF.Percent + title: DF.Data + valid_from: DF.Date | None + valid_upto: DF.Date | None + validate_applied_rule: DF.Check + warehouse: DF.Link | None + # end: auto-generated types + def validate(self): self.validate_mandatory() self.validate_duplicate_apply_on() @@ -46,6 +154,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 3778973bd63330ca971338d546c166fc2e2031a6..cc6dd49c4d329a875d73e0e86f7efbf9294d3f7d 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -1158,6 +1158,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"] @@ -1186,6 +1242,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 49320e3ef9df58fd3ff7956136b7c359ee71de65..471aa76b919c1db42ac2978f49ff1acbd67c0106 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -31,6 +31,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 9f561c956b5d354c0b36d2381a5d51ce89a10c76..be4e85826b338effadd6a571caffd4d40917519b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -396,3 +396,4 @@ erpnext.patches.v14_0.set_maintain_stock_for_bom_item 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 0000000000000000000000000000000000000000..af87eeb27274490c296982932707ba70d99649a0 --- /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()