Merge pull request #1827 from anandpdoshi/anand-wip
Naming Series property type as Text
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js
index 356cc0d..a1859e5 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js
@@ -61,4 +61,31 @@
'</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();
+}
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
index b20563f..2d318c6 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
@@ -1,288 +1,299 @@
{
- "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": "min_qty",
+ "fieldtype": "Float",
+ "label": "Min Qty",
"permlevel": 0
- },
+ },
{
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company",
+ "fieldname": "max_qty",
+ "fieldtype": "Float",
+ "label": "Max 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-20 19:36:22.502381",
+ "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": 1,
+ "import": 1,
+ "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,
+ "role": "System Manager",
"write": 1
}
- ],
- "sort_field": "modified",
+ ],
+ "sort_field": "modified",
"sort_order": "DESC"
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index a15b45a..967d583 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -5,16 +5,21 @@
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()
+ self.validate_max_discount()
def validate_mandatory(self):
for field in ["apply_on", "applicable_for"]:
@@ -22,6 +27,18 @@
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 +61,189 @@
for field in ["Price", "Discount Percentage"]:
if flt(self.get(frappe.scrub(field))) < 0:
throw(_("{0} can not be negative").format(field))
+
+ def validate_max_discount(self):
+ if self.price_or_discount == "Discount Percentage" and self.item_code:
+ max_discount = frappe.db.get_value("Item", self.item_code, "max_discount")
+ if max_discount and flt(self.discount_percentage) > flt(max_discount):
+ throw(_("Max discount allowed for item: {0} is {1}%").format(self.item_code, max_discount))
+
+
+#--------------------------------------------------------------------------------
+
+@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.conversion_rate) \
+ if args.conversion_rate else 0.0,
+ "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
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index b17c995..e8496d0 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -17,6 +17,7 @@
"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 @@
"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 @@
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"
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 505a3ba..8eb3b09 100755
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -232,6 +232,14 @@
"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",
"label": "Items",
@@ -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,
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 983f2bb..a07b69d 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -242,6 +242,14 @@
"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",
"label": "Items",
@@ -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,
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index d293683..794c041 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -198,6 +198,14 @@
"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",
"label": "Items",
@@ -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,
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 91cc865..3a08124 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -187,12 +187,13 @@
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 @@
@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)
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 19b0283..c3c5bf4 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -197,6 +197,14 @@
"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",
"label": "Items",
@@ -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,
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index 74a37b3..2af7bb9 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -54,6 +54,7 @@
@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")
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 3af8290..847e09e 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -89,14 +89,14 @@
"""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():
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index dd57c60..041bbd3 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -62,3 +62,4 @@
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
diff --git a/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py b/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py
new file mode 100644
index 0000000..8be846f
--- /dev/null
+++ b/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py
@@ -0,0 +1,13 @@
+# 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.reload_doc("accounts", "doctype", "pricing_rule")
+ 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')""")
diff --git a/erpnext/public/js/transaction.js b/erpnext/public/js/transaction.js
index 2c37204..ea576d5 100644
--- a/erpnext/public/js/transaction.js
+++ b/erpnext/public/js/transaction.js
@@ -116,8 +116,8 @@
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 @@
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 @@
}
this.frm.script_manager.trigger("currency");
- this.apply_pricing_rule()
+ this.apply_pricing_rule();
}
},
@@ -229,7 +232,12 @@
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 @@
}
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 @@
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) {
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index 0221738..1ae0adb 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -275,6 +275,14 @@
"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",
"label": "Items",
@@ -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,
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 62577db..f396191 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -102,7 +102,7 @@
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")
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index c899227..a036370 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -286,6 +286,14 @@
"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",
"label": "Items",
@@ -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,
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 24da577..c14612b 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -245,9 +245,6 @@
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 @@
@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_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")
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 9b13b10..690fd05 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -276,6 +276,14 @@
"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",
"label": "Items",
@@ -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,
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index bbc9f81..4b147cc 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -280,6 +280,7 @@
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:
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 506e5d0..7ab93eb 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -17,7 +17,7 @@
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 @@
"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')
\ No newline at end of file
+test_records = frappe.get_test_records('Item')
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index e585bef..ae748ce 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -196,6 +196,14 @@
"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",
"label": "Items",
@@ -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,
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 13bb193..71c07eb 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -287,6 +287,7 @@
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")
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index c5c1280..fe320d1 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -6,8 +6,7 @@
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 @@
"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 @@
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 @@
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 @@
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 @@
"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 @@
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 @@
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 @@
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`