Pricing Rule fixes and improvements. Fixes #1795

This commit is contained in:
Nabin Hait 2014-06-20 15:59:49 +05:30
parent b35d84156f
commit 444f956e7b
24 changed files with 695 additions and 439 deletions

View File

@ -61,4 +61,31 @@ frappe.ui.form.on("Pricing Rule", "refresh", function(frm) {
'</table>'].join("\n");
set_field_options("pricing_rule_help", help_content);
cur_frm.cscript.set_options_for_applicable_for();
});
cur_frm.cscript.set_options_for_applicable_for = function() {
var options = [""];
var applicable_for = cur_frm.doc.applicable_for;
if(cur_frm.doc.selling) {
options = $.merge(options, ["Customer", "Customer Group", "Territory", "Sales Partner", "Campaign"]);
}
if(cur_frm.doc.buying) {
$.merge(options, ["Supplier", "Supplier Type"]);
}
set_field_options("applicable_for", options.join("\n"));
if(!in_list(options, applicable_for)) applicable_for = null;
cur_frm.set_value("applicable_for", applicable_for)
}
cur_frm.cscript.selling = function() {
cur_frm.cscript.set_options_for_applicable_for();
}
cur_frm.cscript.buying = function() {
cur_frm.cscript.set_options_for_applicable_for();
}

View File

@ -1,288 +1,300 @@
{
"allow_import": 1,
"autoname": "PRULE.#####",
"creation": "2014-02-21 15:02:51",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"allow_import": 1,
"autoname": "PRULE.#####",
"creation": "2014-02-21 15:02:51",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"fields": [
{
"fieldname": "applicability_section",
"fieldtype": "Section Break",
"in_list_view": 0,
"label": "Applicability",
"fieldname": "applicability_section",
"fieldtype": "Section Break",
"in_list_view": 0,
"label": "Applicability",
"permlevel": 0
},
},
{
"default": "Item Code",
"fieldname": "apply_on",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Apply On",
"options": "\nItem Code\nItem Group\nBrand",
"permlevel": 0,
"default": "Item Code",
"fieldname": "apply_on",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Apply On",
"options": "\nItem Code\nItem Group\nBrand",
"permlevel": 0,
"reqd": 1
},
},
{
"depends_on": "eval:doc.apply_on==\"Item Code\"",
"fieldname": "item_code",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item Code",
"options": "Item",
"permlevel": 0,
"depends_on": "eval:doc.apply_on==\"Item Code\"",
"fieldname": "item_code",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item Code",
"options": "Item",
"permlevel": 0,
"reqd": 0
},
},
{
"depends_on": "eval:doc.apply_on==\"Item Group\"",
"fieldname": "item_group",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item Group",
"options": "Item Group",
"depends_on": "eval:doc.apply_on==\"Item Group\"",
"fieldname": "item_group",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item Group",
"options": "Item Group",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.apply_on==\"Brand\"",
"fieldname": "brand",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Brand",
"options": "Brand",
"depends_on": "eval:doc.apply_on==\"Brand\"",
"fieldname": "brand",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Brand",
"options": "Brand",
"permlevel": 0
},
},
{
"fieldname": "applicable_for",
"fieldtype": "Select",
"label": "Applicable For",
"options": "\nCustomer\nCustomer Group\nTerritory\nSales Partner\nCampaign\nSupplier\nSupplier Type",
"fieldname": "selling",
"fieldtype": "Check",
"label": "Selling",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.applicable_for==\"Customer\"",
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"options": "Customer",
"fieldname": "buying",
"fieldtype": "Check",
"label": "Buying",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.applicable_for==\"Customer Group\"",
"fieldname": "customer_group",
"fieldtype": "Link",
"label": "Customer Group",
"options": "Customer Group",
"fieldname": "applicable_for",
"fieldtype": "Select",
"label": "Applicable For",
"options": "\nCustomer\nCustomer Group\nTerritory\nSales Partner\nCampaign\nSupplier\nSupplier Type",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.applicable_for==\"Territory\"",
"fieldname": "territory",
"fieldtype": "Link",
"label": "Territory",
"options": "Territory",
"depends_on": "eval:doc.applicable_for==\"Customer\"",
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"options": "Customer",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.applicable_for==\"Sales Partner\"",
"fieldname": "sales_partner",
"fieldtype": "Link",
"label": "Sales Partner",
"options": "Sales Partner",
"depends_on": "eval:doc.applicable_for==\"Customer Group\"",
"fieldname": "customer_group",
"fieldtype": "Link",
"label": "Customer Group",
"options": "Customer Group",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.applicable_for==\"Campaign\"",
"fieldname": "campaign",
"fieldtype": "Link",
"label": "Campaign",
"options": "Campaign",
"depends_on": "eval:doc.applicable_for==\"Territory\"",
"fieldname": "territory",
"fieldtype": "Link",
"label": "Territory",
"options": "Territory",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.applicable_for==\"Supplier\"",
"fieldname": "supplier",
"fieldtype": "Link",
"label": "Supplier",
"options": "Supplier",
"depends_on": "eval:doc.applicable_for==\"Sales Partner\"",
"fieldname": "sales_partner",
"fieldtype": "Link",
"label": "Sales Partner",
"options": "Sales Partner",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.applicable_for==\"Supplier Type\"",
"fieldname": "supplier_type",
"fieldtype": "Link",
"label": "Supplier Type",
"options": "Supplier Type",
"depends_on": "eval:doc.applicable_for==\"Campaign\"",
"fieldname": "campaign",
"fieldtype": "Link",
"label": "Campaign",
"options": "Campaign",
"permlevel": 0
},
},
{
"fieldname": "min_qty",
"fieldtype": "Float",
"label": "Min Qty",
"depends_on": "eval:doc.applicable_for==\"Supplier\"",
"fieldname": "supplier",
"fieldtype": "Link",
"label": "Supplier",
"options": "Supplier",
"permlevel": 0
},
},
{
"fieldname": "max_qty",
"fieldtype": "Float",
"label": "Max Qty",
"depends_on": "eval:doc.applicable_for==\"Supplier Type\"",
"fieldname": "supplier_type",
"fieldtype": "Link",
"label": "Supplier Type",
"options": "Supplier Type",
"permlevel": 0
},
},
{
"fieldname": "col_break1",
"fieldtype": "Column Break",
"fieldname": "max_qty",
"fieldtype": "Float",
"label": "Max Qty",
"permlevel": 0
},
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"fieldname": "min_qty",
"fieldtype": "Float",
"label": "Min Qty",
"permlevel": 0
},
},
{
"default": "Today",
"fieldname": "valid_from",
"fieldtype": "Date",
"label": "Valid From",
"fieldname": "col_break1",
"fieldtype": "Column Break",
"permlevel": 0
},
},
{
"fieldname": "valid_upto",
"fieldtype": "Date",
"label": "Valid Upto",
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"permlevel": 0
},
},
{
"fieldname": "priority",
"fieldtype": "Select",
"label": "Priority",
"options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20",
"default": "Today",
"fieldname": "valid_from",
"fieldtype": "Date",
"label": "Valid From",
"permlevel": 0
},
},
{
"fieldname": "disable",
"fieldtype": "Check",
"label": "Disable",
"fieldname": "valid_upto",
"fieldtype": "Date",
"label": "Valid Upto",
"permlevel": 0
},
},
{
"fieldname": "price_discount_section",
"fieldtype": "Section Break",
"label": "Price / Discount",
"fieldname": "priority",
"fieldtype": "Select",
"label": "Priority",
"options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20",
"permlevel": 0
},
},
{
"default": "Discount Percentage",
"fieldname": "price_or_discount",
"fieldtype": "Select",
"label": "Price or Discount",
"options": "\nPrice\nDiscount Percentage",
"permlevel": 0,
"fieldname": "disable",
"fieldtype": "Check",
"label": "Disable",
"permlevel": 0
},
{
"fieldname": "price_discount_section",
"fieldtype": "Section Break",
"label": "Price / Discount",
"permlevel": 0
},
{
"default": "Discount Percentage",
"fieldname": "price_or_discount",
"fieldtype": "Select",
"label": "Price or Discount",
"options": "\nPrice\nDiscount Percentage",
"permlevel": 0,
"reqd": 1
},
},
{
"fieldname": "col_break2",
"fieldtype": "Column Break",
"fieldname": "col_break2",
"fieldtype": "Column Break",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.price_or_discount==\"Price\"",
"fieldname": "price",
"fieldtype": "Float",
"label": "Price",
"depends_on": "eval:doc.price_or_discount==\"Price\"",
"fieldname": "price",
"fieldtype": "Float",
"label": "Price",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.price_or_discount==\"Discount Percentage\"",
"fieldname": "discount_percentage",
"fieldtype": "Float",
"label": "Discount Percentage",
"depends_on": "eval:doc.price_or_discount==\"Discount Percentage\"",
"fieldname": "discount_percentage",
"fieldtype": "Float",
"label": "Discount Percentage",
"permlevel": 0
},
},
{
"depends_on": "eval:doc.price_or_discount==\"Discount Percentage\"",
"fieldname": "for_price_list",
"fieldtype": "Link",
"label": "For Price List",
"options": "Price List",
"depends_on": "eval:doc.price_or_discount==\"Discount Percentage\"",
"fieldname": "for_price_list",
"fieldtype": "Link",
"label": "For Price List",
"options": "Price List",
"permlevel": 0
},
},
{
"fieldname": "help_section",
"fieldtype": "Section Break",
"label": "",
"options": "Simple",
"fieldname": "help_section",
"fieldtype": "Section Break",
"label": "",
"options": "Simple",
"permlevel": 0
},
},
{
"fieldname": "pricing_rule_help",
"fieldtype": "HTML",
"label": "Pricing Rule Help",
"fieldname": "pricing_rule_help",
"fieldtype": "HTML",
"label": "Pricing Rule Help",
"permlevel": 0
}
],
"icon": "icon-gift",
"idx": 1,
"istable": 0,
"modified": "2014-05-28 15:36:29.403659",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",
"owner": "Administrator",
],
"icon": "icon-gift",
"idx": 1,
"istable": 0,
"modified": "2014-06-19 15:00:09.962572",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"export": 0,
"import": 0,
"permlevel": 0,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"create": 1,
"delete": 1,
"export": 0,
"import": 0,
"permlevel": 0,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"write": 1
},
},
{
"create": 1,
"delete": 1,
"export": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 1,
"role": "Sales Manager",
"create": 1,
"delete": 1,
"export": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 1,
"role": "Sales Manager",
"write": 1
},
},
{
"create": 1,
"delete": 1,
"permlevel": 0,
"read": 1,
"report": 1,
"role": "Purchase Manager",
"create": 1,
"delete": 1,
"permlevel": 0,
"read": 1,
"report": 1,
"role": "Purchase Manager",
"write": 1
},
},
{
"create": 1,
"delete": 1,
"permlevel": 0,
"read": 1,
"report": 1,
"role": "Website Manager",
"create": 1,
"delete": 1,
"permlevel": 0,
"read": 1,
"report": 1,
"role": "Website Manager",
"write": 1
},
},
{
"create": 1,
"delete": 1,
"export": 1,
"import": 1,
"permlevel": 0,
"read": 1,
"report": 1,
"set_user_permissions": 1,
"role": "System Manager",
"create": 1,
"delete": 1,
"export": 1,
"import": 1,
"permlevel": 0,
"read": 1,
"report": 1,
"restrict": 1,
"role": "System Manager",
"write": 1
}
],
"sort_field": "modified",
],
"sort_field": "modified",
"sort_order": "DESC"
}
}

View File

@ -5,13 +5,17 @@
from __future__ import unicode_literals
import frappe
import json
from frappe import throw, _
from frappe.utils import flt
from frappe.utils import flt, cint
from frappe.model.document import Document
class MultiplePricingRuleConflict(frappe.ValidationError): pass
class PricingRule(Document):
def validate(self):
self.validate_mandatory()
self.validate_applicable_for_selling_or_buying()
self.validate_min_max_qty()
self.cleanup_fields_value()
self.validate_price_or_discount()
@ -22,6 +26,18 @@ class PricingRule(Document):
if tocheck and not self.get(tocheck):
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
def validate_applicable_for_selling_or_buying(self):
if not self.selling and not self.buying:
throw(_("Atleast one of the Selling or Buying must be selected"))
if not self.selling and self.applicable_for in ["Customer", "Customer Group",
"Territory", "Sales Partner", "Campaign"]:
throw(_("Selling must be checked, if Applicable For is selected as {0}"
.format(self.applicable_for)))
if not self.buying and self.applicable_for in ["Supplier", "Supplier Type"]:
throw(_("Buying must be checked, if Applicable For is selected as {0}"
.format(self.applicable_for)))
def validate_min_max_qty(self):
if self.min_qty and self.max_qty and flt(self.min_qty) > flt(self.max_qty):
@ -44,3 +60,181 @@ class PricingRule(Document):
for field in ["Price", "Discount Percentage"]:
if flt(self.get(frappe.scrub(field))) < 0:
throw(_("{0} can not be negative").format(field))
#--------------------------------------------------------------------------------
@frappe.whitelist()
def apply_pricing_rule(args):
"""
args = {
"item_list": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
"customer": "something",
"customer_group": "something",
"territory": "something",
"supplier": "something",
"supplier_type": "something",
"currency": "something",
"conversion_rate": "something",
"price_list": "something",
"plc_conversion_rate": "something",
"company": "something",
"transaction_date": "something",
"campaign": "something",
"sales_partner": "something",
"ignore_pricing_rule": "something"
}
"""
if isinstance(args, basestring):
args = json.loads(args)
args = frappe._dict(args)
# list of dictionaries
out = []
if args.get("parenttype") == "Material Request": return out
if not args.transaction_type:
args.transaction_type = "buying" if frappe.get_meta(args.parenttype).get_field("supplier") \
else "selling"
for item in args.get("item_list"):
args_copy = args.copy()
args_copy.update(item)
out.append(get_pricing_rule_for_item(args_copy))
return out
def get_pricing_rule_for_item(args):
if args.get("parenttype") == "Material Request": return {}
item_details = frappe._dict({
"doctype": args.doctype,
"name": args.name,
"pricing_rule": None
})
if args.ignore_pricing_rule or not args.item_code:
return item_details
if not (args.item_group and args.brand):
args.item_group, args.brand = frappe.db.get_value("Item", args.item_code, ["item_group", "brand"])
if args.customer and not (args.customer_group and args.territory):
args.customer_group, args.territory = frappe.db.get_value("Customer", args.customer,
["customer_group", "territory"])
elif args.supplier and not args.supplier_type:
args.supplier_type = frappe.db.get_value("Supplier", args.supplier, "supplier_type")
pricing_rules = get_pricing_rules(args)
pricing_rule = filter_pricing_rules(args, pricing_rules)
if pricing_rule:
item_details.pricing_rule = pricing_rule.name
if pricing_rule.price_or_discount == "Price":
item_details.update({
"price_list_rate": pricing_rule.price*flt(args.plc_conversion_rate)/flt(args.conversion_rate),
"discount_percentage": 0.0
})
else:
item_details.discount_percentage = pricing_rule.discount_percentage
return item_details
def get_pricing_rules(args):
def _get_tree_conditions(parenttype, allow_blank=True):
field = frappe.scrub(parenttype)
condition = ""
if args.get(field):
lft, rgt = frappe.db.get_value(parenttype, args[field], ["lft", "rgt"])
parent_groups = frappe.db.sql_list("""select name from `tab%s`
where lft<=%s and rgt>=%s""" % (parenttype, '%s', '%s'), (lft, rgt))
if parent_groups:
if allow_blank: parent_groups.append('')
condition = " ifnull("+field+", '') in ('" + "', '".join(parent_groups)+"')"
return condition
conditions = ""
for field in ["company", "customer", "supplier", "supplier_type", "campaign", "sales_partner"]:
if args.get(field):
conditions += " and ifnull("+field+", '') in (%("+field+")s, '')"
else:
conditions += " and ifnull("+field+", '') = ''"
for parenttype in ["Customer Group", "Territory"]:
group_condition = _get_tree_conditions(parenttype)
if group_condition:
conditions += " and " + group_condition
conditions += " and ifnull(for_price_list, '') in (%(price_list)s, '')"
if args.get("transaction_date"):
conditions += """ and %(transaction_date)s between ifnull(valid_from, '2000-01-01')
and ifnull(valid_upto, '2500-12-31')"""
return frappe.db.sql("""select * from `tabPricing Rule`
where (item_code=%(item_code)s or {item_group_condition} or brand=%(brand)s)
and docstatus < 2 and ifnull(disable, 0) = 0
and ifnull({transaction_type}, 0) = 1 {conditions}
order by priority desc, name desc""".format(
item_group_condition=_get_tree_conditions("Item Group", False),
transaction_type=args.transaction_type, conditions=conditions), args, as_dict=1)
def filter_pricing_rules(args, pricing_rules):
# filter for qty
if pricing_rules and args.get("qty"):
pricing_rules = filter(lambda x: (args.qty>=flt(x.min_qty)
and (args.qty<=x.max_qty if x.max_qty else True)), pricing_rules)
# find pricing rule with highest priority
if pricing_rules:
max_priority = max([cint(p.priority) for p in pricing_rules])
if max_priority:
pricing_rules = filter(lambda x: cint(x.priority)==max_priority, pricing_rules)
# apply internal priority
all_fields = ["item_code", "item_group", "brand", "customer", "customer_group", "territory",
"supplier", "supplier_type", "campaign", "sales_partner"]
if len(pricing_rules) > 1:
for field_set in [["item_code", "item_group", "brand"],
["customer", "customer_group", "territory"], ["supplier", "supplier_type"]]:
remaining_fields = list(set(all_fields) - set(field_set))
if if_all_rules_same(pricing_rules, remaining_fields):
pricing_rules = apply_internal_priority(pricing_rules, field_set, args)
break
if len(pricing_rules) > 1:
price_or_discount = list(set([d.price_or_discount for d in pricing_rules]))
if len(price_or_discount) == 1 and price_or_discount[0] == "Discount Percentage":
pricing_rules = filter(lambda x: x.for_price_list==args.price_list, pricing_rules) \
or pricing_rules
if len(pricing_rules) > 1:
frappe.throw(_("Multiple Price Rule exists with same criteria, please resolve \
conflict by assigning priority. Price Rules: {0}")
.format("\n".join([d.name for d in pricing_rules])), MultiplePricingRuleConflict)
elif pricing_rules:
return pricing_rules[0]
def if_all_rules_same(pricing_rules, fields):
all_rules_same = True
val = [pricing_rules[0][k] for k in fields]
for p in pricing_rules[1:]:
if val != [p[k] for k in fields]:
all_rules_same = False
break
return all_rules_same
def apply_internal_priority(pricing_rules, field_set, args):
filtered_rules = []
for field in field_set:
if args.get(field):
filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules)
if filtered_rules: break
return filtered_rules or pricing_rules

View File

@ -17,6 +17,7 @@ class TestPricingRule(unittest.TestCase):
"doctype": "Pricing Rule",
"apply_on": "Item Code",
"item_code": "_Test Item",
"selling": 1,
"price_or_discount": "Discount Percentage",
"price": 0,
"discount_percentage": 10,
@ -29,13 +30,15 @@ class TestPricingRule(unittest.TestCase):
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Sales Order",
"parenttype": "Sales Order",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"order_type": "Sales",
"transaction_type": "selling",
"customer": "_Test Customer",
"doctype": "Sales Order Item",
"name": None
})
details = get_item_details(args)
self.assertEquals(details.get("discount_percentage"), 10)
@ -71,7 +74,7 @@ class TestPricingRule(unittest.TestCase):
self.assertEquals(details.get("discount_percentage"), 5)
frappe.db.sql("update `tabPricing Rule` set priority=NULL where campaign='_Test Campaign'")
from erpnext.stock.get_item_details import MultiplePricingRuleConflict
from erpnext.accounts.doctype.pricing_rule.pricing_rule import MultiplePricingRuleConflict
self.assertRaises(MultiplePricingRuleConflict, get_item_details, args)
args.item_code = "_Test Item 2"

View File

@ -231,6 +231,14 @@
"print_hide": 1,
"read_only": 0
},
{
"fieldname": "ignore_pricing_rule",
"fieldtype": "Check",
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
"print_hide": 1
},
{
"fieldname": "items",
"fieldtype": "Section Break",
@ -744,7 +752,7 @@
"icon": "icon-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2014-06-04 08:45:25.582170",
"modified": "2014-06-19 15:50:50.898237",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
@ -823,6 +831,12 @@
"role": "Auditor",
"submit": 0,
"write": 0
},
{
"permlevel": 1,
"read": 1,
"role": "Accounts Manager",
"write": 1
}
],
"read_only_onload": 1,

View File

@ -241,6 +241,14 @@
"read_only": 0,
"reqd": 1
},
{
"fieldname": "ignore_pricing_rule",
"fieldtype": "Check",
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
"print_hide": 1
},
{
"fieldname": "items",
"fieldtype": "Section Break",
@ -1180,7 +1188,7 @@
"icon": "icon-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2014-05-27 03:49:17.806077",
"modified": "2014-06-19 16:01:19.720382",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
@ -1225,6 +1233,12 @@
"read": 1,
"report": 1,
"role": "Customer"
},
{
"permlevel": 1,
"read": 1,
"role": "Accounts Manager",
"write": 1
}
],
"read_only_onload": 1,

View File

@ -197,6 +197,14 @@
"permlevel": 0,
"print_hide": 1
},
{
"fieldname": "ignore_pricing_rule",
"fieldtype": "Check",
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
"print_hide": 1
},
{
"fieldname": "items",
"fieldtype": "Section Break",
@ -636,7 +644,7 @@
"icon": "icon-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2014-05-27 03:49:15.948363",
"modified": "2014-06-19 15:58:06.375217",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
@ -696,6 +704,12 @@
"read": 1,
"report": 1,
"role": "Supplier"
},
{
"permlevel": 1,
"read": 1,
"role": "Purchase Manager",
"write": 1
}
],
"read_only_onload": 1,

View File

@ -187,12 +187,13 @@ class PurchaseOrder(BuyingController):
def on_update(self):
pass
def set_missing_values(source, target):
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
@frappe.whitelist()
def make_purchase_receipt(source_name, target_doc=None):
def set_missing_values(source, target):
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
def update_item(obj, target, source_parent):
target.qty = flt(obj.qty) - flt(obj.received_qty)
target.stock_qty = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.conversion_factor)
@ -226,10 +227,6 @@ def make_purchase_receipt(source_name, target_doc=None):
@frappe.whitelist()
def make_purchase_invoice(source_name, target_doc=None):
def set_missing_values(source, target):
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
def update_item(obj, target, source_parent):
target.amount = flt(obj.amount) - flt(obj.billed_amt)
target.base_amount = target.amount * flt(source_parent.conversion_rate)

View File

@ -196,6 +196,14 @@
"permlevel": 0,
"print_hide": 1
},
{
"fieldname": "ignore_pricing_rule",
"fieldtype": "Check",
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
"print_hide": 1
},
{
"fieldname": "items",
"fieldtype": "Section Break",
@ -562,7 +570,7 @@
"icon": "icon-shopping-cart",
"idx": 1,
"is_submittable": 1,
"modified": "2014-05-27 03:49:20.226683",
"modified": "2014-06-19 15:54:27.919675",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",
@ -640,6 +648,12 @@
"role": "Supplier",
"submit": 0,
"write": 0
},
{
"permlevel": 1,
"read": 1,
"role": "Purchase Manager",
"write": 1
}
],
"read_only_onload": 1,

View File

@ -54,6 +54,7 @@ class SupplierQuotation(BuyingController):
@frappe.whitelist()
def make_purchase_order(source_name, target_doc=None):
def set_missing_values(source, target):
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
target.run_method("get_schedule_dates")
target.run_method("calculate_taxes_and_totals")

View File

@ -89,14 +89,14 @@ class AccountsController(TransactionBase):
"""set missing item values"""
from erpnext.stock.get_item_details import get_item_details
if hasattr(self, "fname"):
parent_dict = {"doctype": self.doctype}
parent_dict = {}
for fieldname in self.meta.get_valid_columns():
parent_dict[fieldname] = self.get(fieldname)
for item in self.get(self.fname):
if item.get("item_code"):
args = item.as_dict()
args.update(parent_dict)
args = parent_dict.copy()
args.update(item.as_dict())
ret = get_item_details(args)
for fieldname, value in ret.items():

View File

@ -62,3 +62,4 @@ erpnext.patches.v4_0.update_other_charges_in_custom_purchase_print_formats
erpnext.patches.v4_0.create_price_list_if_missing
execute:frappe.db.sql("update `tabItem` set end_of_life=null where end_of_life='0000-00-00'") #2014-06-16
erpnext.patches.v4_0.update_users_report_view_settings
erpnext.patches.v4_0.set_pricing_rule_for_buying_or_selling

View File

@ -0,0 +1,12 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.db.sql("""update `tabPricing Rule` set selling=1 where ifnull(applicable_for, '') in
('', 'Customer', 'Customer Group', 'Territory', 'Sales Partner', 'Campaign')""")
frappe.db.sql("""update `tabPricing Rule` set buying=1 where ifnull(applicable_for, '') in
('', 'Supplier', 'Supplier Type')""")

View File

@ -116,8 +116,8 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
barcode: item.barcode,
serial_no: item.serial_no,
warehouse: item.warehouse,
doctype: me.frm.doc.doctype,
docname: me.frm.doc.name,
parenttype: me.frm.doc.doctype,
parent: me.frm.doc.name,
customer: me.frm.doc.customer,
supplier: me.frm.doc.supplier,
currency: me.frm.doc.currency,
@ -130,7 +130,10 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
order_type: me.frm.doc.order_type,
is_pos: cint(me.frm.doc.is_pos),
is_subcontracted: me.frm.doc.is_subcontracted,
transaction_date: me.frm.doc.transaction_date
transaction_date: me.frm.doc.transaction_date,
ignore_pricing_rule: me.frm.doc.ignore_pricing_rule,
doctype: item.doctype,
name: item.name
}
},
callback: function(r) {
@ -196,7 +199,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
}
this.frm.script_manager.trigger("currency");
this.apply_pricing_rule()
this.apply_pricing_rule();
}
},
@ -229,7 +232,12 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate);
}
if(flt(this.frm.doc.conversion_rate)>0.0) {
this.apply_pricing_rule();
if(this.frm.doc.ignore_pricing_rule) {
this.calculate_taxes_and_totals();
} else {
this.apply_pricing_rule();
}
}
},
@ -283,12 +291,11 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
}
if(this.frm.doc.price_list_currency === this.frm.doc.currency) {
this.frm.set_value("conversion_rate", this.frm.doc.plc_conversion_rate);
this.apply_pricing_rule();
}
},
qty: function(doc, cdt, cdn) {
this.apply_pricing_rule(frappe.get_doc(cdt, cdn));
this.apply_pricing_rule(frappe.get_doc(cdt, cdn), true);
},
// tax rate
@ -331,51 +338,71 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
this.calculate_taxes_and_totals();
},
apply_pricing_rule: function(item) {
ignore_pricing_rule: function() {
this.apply_pricing_rule();
},
apply_pricing_rule: function(item, calculate_taxes_and_totals) {
var me = this;
var _apply_pricing_rule = function(item) {
return me.frm.call({
method: "erpnext.stock.get_item_details.apply_pricing_rule",
child: item,
args: {
args: {
item_code: item.item_code,
item_group: item.item_group,
brand: item.brand,
qty: item.qty,
customer: me.frm.doc.customer,
customer_group: me.frm.doc.customer_group,
territory: me.frm.doc.territory,
supplier: me.frm.doc.supplier,
supplier_type: me.frm.doc.supplier_type,
currency: me.frm.doc.currency,
conversion_rate: me.frm.doc.conversion_rate,
price_list: me.frm.doc.selling_price_list ||
me.frm.doc.buying_price_list,
plc_conversion_rate: me.frm.doc.plc_conversion_rate,
company: me.frm.doc.company,
transaction_date: me.frm.doc.transaction_date || me.frm.doc.posting_date,
campaign: me.frm.doc.campaign,
sales_partner: me.frm.doc.sales_partner
}
},
callback: function(r) {
if(!r.exc) {
me.frm.script_manager.trigger("price_list_rate", item.doctype, item.name);
}
var item_list = this._get_item_list(item);
var args = {
"item_list": item_list,
"customer": me.frm.doc.customer,
"customer_group": me.frm.doc.customer_group,
"territory": me.frm.doc.territory,
"supplier": me.frm.doc.supplier,
"supplier_type": me.frm.doc.supplier_type,
"currency": me.frm.doc.currency,
"conversion_rate": me.frm.doc.conversion_rate,
"price_list": me.frm.doc.selling_price_list || me.frm.doc.buying_price_list,
"plc_conversion_rate": me.frm.doc.plc_conversion_rate,
"company": me.frm.doc.company,
"transaction_date": me.frm.doc.transaction_date || me.frm.doc.posting_date,
"campaign": me.frm.doc.campaign,
"sales_partner": me.frm.doc.sales_partner,
"ignore_pricing_rule": me.frm.doc.ignore_pricing_rule,
"parenttype": me.frm.doc.doctype,
"parent": me.frm.doc.name
};
return this.frm.call({
method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule",
args: { args: args },
callback: function(r) {
if (!r.exc) {
$.each(r.message, function(i, d) {
$.each(d, function(k, v) {
if (["doctype", "name"].indexOf(k)===-1) {
frappe.model.set_value(d.doctype, d.name, k, v);
}
});
});
if(calculate_taxes_and_totals) me.calculate_taxes_and_totals();
}
}
});
},
_get_item_list: function(item) {
var item_list = [];
var append_item = function(d) {
item_list.push({
"doctype": d.doctype,
"name": d.name,
"item_code": d.item_code,
"item_group": d.item_group,
"brand": d.brand,
"qty": d.qty
});
}
};
if(item) {
_apply_pricing_rule(item);
if (item) {
append_item(item);
} else {
$.each(this.get_item_doclist(), function(n, item) {
_apply_pricing_rule(item);
$.each(this.get_item_doclist(), function(i, d) {
append_item(d);
});
}
return item_list;
},
included_in_print_rate: function(doc, cdt, cdn) {

View File

@ -274,6 +274,14 @@
"read_only": 0,
"reqd": 1
},
{
"fieldname": "ignore_pricing_rule",
"fieldtype": "Check",
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
"print_hide": 1
},
{
"fieldname": "items",
"fieldtype": "Section Break",
@ -818,7 +826,7 @@
"idx": 1,
"is_submittable": 1,
"max_attachments": 1,
"modified": "2014-05-27 03:49:16.670976",
"modified": "2014-06-19 15:59:30.019826",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",
@ -896,6 +904,12 @@
"role": "Maintenance User",
"submit": 1,
"write": 1
},
{
"permlevel": 1,
"read": 1,
"role": "Sales Manager",
"write": 1
}
],
"read_only_onload": 1,

View File

@ -102,7 +102,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
if customer:
target.customer = customer.name
target.customer_name = customer.customer_name
target.ignore_pricing_rule = 1
target.ignore_permissions = ignore_permissions
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")

View File

@ -285,6 +285,14 @@
"print_hide": 1,
"reqd": 1
},
{
"fieldname": "ignore_pricing_rule",
"fieldtype": "Check",
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
"print_hide": 1
},
{
"fieldname": "items",
"fieldtype": "Section Break",
@ -874,7 +882,7 @@
"idx": 1,
"is_submittable": 1,
"issingle": 0,
"modified": "2014-05-27 08:39:19.027965",
"modified": "2014-06-19 16:00:06.626037",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
@ -953,6 +961,12 @@
"read": 1,
"report": 1,
"role": "Material User"
},
{
"permlevel": 1,
"read": 1,
"role": "Sales Manager",
"write": 1
}
],
"read_only_onload": 1,

View File

@ -245,9 +245,6 @@ class SalesOrder(SellingController):
def get_portal_page(self):
return "order" if self.docstatus==1 else None
def set_missing_values(source, target):
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
@frappe.whitelist()
def make_material_request(source_name, target_doc=None):
@ -274,6 +271,11 @@ def make_material_request(source_name, target_doc=None):
@frappe.whitelist()
def make_delivery_note(source_name, target_doc=None):
def set_missing_values(source, target):
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
def update_item(source, target, source_parent):
target.base_amount = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.base_rate)
target.amount = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.rate)
@ -312,6 +314,7 @@ def make_delivery_note(source_name, target_doc=None):
def make_sales_invoice(source_name, target_doc=None):
def set_missing_values(source, target):
target.is_pos = 0
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")

View File

@ -275,6 +275,14 @@
"read_only": 0,
"reqd": 1
},
{
"fieldname": "ignore_pricing_rule",
"fieldtype": "Check",
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
"print_hide": 1
},
{
"fieldname": "items",
"fieldtype": "Section Break",
@ -999,7 +1007,7 @@
"idx": 1,
"in_create": 0,
"is_submittable": 1,
"modified": "2014-05-27 03:49:09.721622",
"modified": "2014-06-19 16:00:47.326127",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
@ -1073,6 +1081,12 @@
"read": 1,
"report": 1,
"role": "Customer"
},
{
"permlevel": 1,
"read": 1,
"role": "Material Manager",
"write": 1
}
],
"read_only_onload": 1,

View File

@ -280,6 +280,7 @@ def make_sales_invoice(source_name, target_doc=None):
def update_accounts(source, target):
target.is_pos = 0
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
if len(target.get("entries")) == 0:

View File

@ -17,7 +17,7 @@ class TestItem(unittest.TestCase):
item.is_stock_item = "Yes"
item.default_warehouse = None
self.assertRaises(WarehouseNotSet, item.insert)
def test_get_item_details(self):
from erpnext.stock.get_item_details import get_item_details
to_check = {
@ -41,23 +41,23 @@ class TestItem(unittest.TestCase):
"uom": "_Test UOM",
"conversion_factor": 1.0,
}
make_test_records("Item Price")
details = get_item_details({
"item_code": "_Test Item",
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"doctype": "Sales Order",
"parenttype": "Sales Order",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,
"order_type": "Sales",
"transaction_type": "selling"
})
for key, value in to_check.iteritems():
self.assertEquals(value, details.get(key))
test_records = frappe.get_test_records('Item')
test_records = frappe.get_test_records('Item')

View File

@ -195,6 +195,14 @@
"permlevel": 0,
"print_hide": 1
},
{
"fieldname": "ignore_pricing_rule",
"fieldtype": "Check",
"label": "Ignore Pricing Rule",
"no_copy": 1,
"permlevel": 1,
"print_hide": 1
},
{
"fieldname": "items",
"fieldtype": "Section Break",
@ -754,7 +762,7 @@
"icon": "icon-truck",
"idx": 1,
"is_submittable": 1,
"modified": "2014-05-27 03:49:16.302198",
"modified": "2014-06-19 15:58:37.932064",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",
@ -821,6 +829,12 @@
"read": 1,
"report": 1,
"role": "Supplier"
},
{
"permlevel": 1,
"read": 1,
"role": "Material Manager",
"write": 1
}
],
"read_only_onload": 1,

View File

@ -287,6 +287,7 @@ def make_purchase_invoice(source_name, target_doc=None):
frappe.throw(_("All items have already been invoiced"))
doc = frappe.get_doc(target)
doc.ignore_pricing_rule = 1
doc.run_method("set_missing_values")
doc.run_method("calculate_taxes_and_totals")

View File

@ -6,8 +6,7 @@ import frappe
from frappe import _, throw
from frappe.utils import flt, cint, add_days
import json
class MultiplePricingRuleConflict(frappe.ValidationError): pass
from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item
@frappe.whitelist()
def get_item_details(args):
@ -20,14 +19,15 @@ def get_item_details(args):
"selling_price_list": None,
"price_list_currency": None,
"plc_conversion_rate": 1.0,
"doctype": "",
"docname": "",
"parenttype": "",
"parent": "",
"supplier": None,
"transaction_date": None,
"conversion_rate": 1.0,
"buying_price_list": None,
"is_subcontracted": "Yes" / "No",
"transaction_type": "selling"
"transaction_type": "selling",
"ignore_pricing_rule": 0/1
}
"""
@ -37,7 +37,8 @@ def get_item_details(args):
args = frappe._dict(args)
if not args.get("transaction_type"):
if args.get("doctype")=="Material Request" or frappe.get_meta(args.get("doctype")).get_field("supplier"):
if args.get("parenttype")=="Material Request" or \
frappe.get_meta(args.get("parenttype")).get_field("supplier"):
args.transaction_type = "buying"
else:
args.transaction_type = "selling"
@ -73,9 +74,9 @@ def get_item_details(args):
if args.get(key) is None:
args[key] = value
out.update(apply_pricing_rule(args))
out.update(get_pricing_rule_for_item(args))
if args.get("doctype") in ("Sales Invoice", "Delivery Note"):
if args.get("parenttype") in ("Sales Invoice", "Delivery Note"):
if item_doc.has_serial_no == "Yes" and not args.serial_no:
out.serial_no = get_serial_nos_by_fifo(args, item_doc)
@ -113,7 +114,7 @@ def validate_item_details(args, item):
elif item.is_sales_item != "Yes":
throw(_("Item {0} must be a Sales Item").format(item.name))
elif args.transaction_type == "buying" and args.doctype != "Material Request":
elif args.transaction_type == "buying" and args.parenttype != "Material Request":
# validate if purchase item or subcontracted item
if item.is_purchase_item != "Yes":
throw(_("Item {0} must be a Purchase Item").format(item.name))
@ -144,7 +145,7 @@ def get_basic_details(args, item_doc):
"item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
item_doc.get("item_tax")))),
"uom": item.stock_uom,
"min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "",
"min_order_qty": flt(item.min_order_qty) if args.parenttype == "Material Request" else "",
"conversion_factor": 1.0,
"qty": 1.0,
"price_list_rate": 0.0,
@ -162,7 +163,7 @@ def get_basic_details(args, item_doc):
return out
def get_price_list_rate(args, item_doc, out):
meta = frappe.get_meta(args.doctype)
meta = frappe.get_meta(args.parenttype)
if meta.get_field("currency"):
validate_price_list(args)
@ -179,7 +180,7 @@ def get_price_list_rate(args, item_doc, out):
if not out.price_list_rate and args.transaction_type == "buying":
from erpnext.stock.doctype.item.item import get_last_purchase_details
out.update(get_last_purchase_details(item_doc.name,
args.docname, args.conversion_rate))
args.parent, args.conversion_rate))
def validate_price_list(args):
if args.get("price_list"):
@ -248,142 +249,6 @@ def get_pos_settings(company):
return pos_settings and pos_settings[0] or None
@frappe.whitelist()
def apply_pricing_rule(args):
if isinstance(args, basestring):
args = json.loads(args)
args = frappe._dict(args)
out = frappe._dict()
if args.get("doctype") == "Material Request" or not args.get("item_code"): return out
if not args.get("item_group") or not args.get("brand"):
args.item_group, args.brand = frappe.db.get_value("Item",
args.item_code, ["item_group", "brand"])
if args.get("customer") and (not args.get("customer_group") or not args.get("territory")):
args.customer_group, args.territory = frappe.db.get_value("Customer",
args.customer, ["customer_group", "territory"])
if args.get("supplier") and not args.get("supplier_type"):
args.supplier_type = frappe.db.get_value("Supplier", args.supplier, "supplier_type")
pricing_rules = get_pricing_rules(args)
pricing_rule = filter_pricing_rules(args, pricing_rules)
if pricing_rule:
out.pricing_rule = pricing_rule.name
if pricing_rule.price_or_discount == "Price":
out.base_price_list_rate = pricing_rule.price
out.price_list_rate = pricing_rule.price*flt(args.plc_conversion_rate)/flt(args.conversion_rate)
out.base_rate = out.base_price_list_rate
out.rate = out.price_list_rate
out.discount_percentage = 0.0
else:
out.discount_percentage = pricing_rule.discount_percentage
else:
out.pricing_rule = None
return out
def get_pricing_rules(args):
def _get_tree_conditions(doctype, allow_blank=True):
field = frappe.scrub(doctype)
condition = ""
if args.get(field):
lft, rgt = frappe.db.get_value(doctype, args[field], ["lft", "rgt"])
parent_groups = frappe.db.sql_list("""select name from `tab%s`
where lft<=%s and rgt>=%s""" % (doctype, '%s', '%s'), (lft, rgt))
if parent_groups:
if allow_blank: parent_groups.append('')
condition = " ifnull("+field+", '') in ('" + "', '".join(parent_groups)+"')"
return condition
conditions = ""
for field in ["company", "customer", "supplier", "supplier_type", "campaign", "sales_partner"]:
if args.get(field):
conditions += " and ifnull("+field+", '') in (%("+field+")s, '')"
else:
conditions += " and ifnull("+field+", '') = ''"
for doctype in ["Customer Group", "Territory"]:
group_condition = _get_tree_conditions(doctype)
if group_condition:
conditions += " and " + group_condition
conditions += " and ifnull(for_price_list, '') in (%(price_list)s, '')"
if args.get("transaction_date"):
conditions += """ and %(transaction_date)s between ifnull(valid_from, '2000-01-01')
and ifnull(valid_upto, '2500-12-31')"""
return frappe.db.sql("""select * from `tabPricing Rule`
where (item_code=%(item_code)s or {item_group_condition} or brand=%(brand)s)
and docstatus < 2 and ifnull(disable, 0) = 0 {conditions}
order by priority desc, name desc""".format(
item_group_condition=_get_tree_conditions("Item Group", False), conditions=conditions),
args, as_dict=1)
def filter_pricing_rules(args, pricing_rules):
# filter for qty
if pricing_rules and args.get("qty"):
pricing_rules = filter(lambda x: (args.qty>=flt(x.min_qty)
and (args.qty<=x.max_qty if x.max_qty else True)), pricing_rules)
# find pricing rule with highest priority
if pricing_rules:
max_priority = max([cint(p.priority) for p in pricing_rules])
if max_priority:
pricing_rules = filter(lambda x: cint(x.priority)==max_priority, pricing_rules)
# apply internal priority
all_fields = ["item_code", "item_group", "brand", "customer", "customer_group", "territory",
"supplier", "supplier_type", "campaign", "sales_partner"]
if len(pricing_rules) > 1:
for field_set in [["item_code", "item_group", "brand"],
["customer", "customer_group", "territory"], ["supplier", "supplier_type"]]:
remaining_fields = list(set(all_fields) - set(field_set))
if if_all_rules_same(pricing_rules, remaining_fields):
pricing_rules = apply_internal_priority(pricing_rules, field_set, args)
break
if len(pricing_rules) > 1:
price_or_discount = list(set([d.price_or_discount for d in pricing_rules]))
if len(price_or_discount) == 1 and price_or_discount[0] == "Discount Percentage":
pricing_rules = filter(lambda x: x.for_price_list==args.price_list, pricing_rules) \
or pricing_rules
if len(pricing_rules) > 1:
frappe.throw(_("Multiple Price Rule exists with same criteria, please resolve \
conflict by assigning priority. Price Rules: {0}")
.format("\n".join([d.name for d in pricing_rules])), MultiplePricingRuleConflict)
elif pricing_rules:
return pricing_rules[0]
def if_all_rules_same(pricing_rules, fields):
all_rules_same = True
val = [pricing_rules[0][k] for k in fields]
for p in pricing_rules[1:]:
if val != [p[k] for k in fields]:
all_rules_same = False
break
return all_rules_same
def apply_internal_priority(pricing_rules, field_set, args):
filtered_rules = []
for field in field_set:
if args.get(field):
filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules)
if filtered_rules: break
return filtered_rules or pricing_rules
def get_serial_nos_by_fifo(args, item_doc):
return "\n".join(frappe.db.sql_list("""select name from `tabSerial No`