From 496123a646bcf7ccadec38d53c1924db8c0d69f2 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 19 Jun 2014 19:25:19 +0530 Subject: [PATCH 01/27] Inventory Accounting: Cost Center only required for Expense Account --- erpnext/controllers/stock_controller.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 96b8a6e8019..d31034753c9 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -237,8 +237,11 @@ class StockController(AccountsController): if not item.get("expense_account"): frappe.throw(_("Expense or Difference account is mandatory for Item {0} as it impacts overall stock value").format(item.item_code)) - if item.get("expense_account") and not item.get("cost_center"): - frappe.throw(_("""Cost Center is mandatory for Item {0}""").format(item.get("item_code"))) + else: + is_expense_account = frappe.db.get_value("Account", item.get("expense_account"), "report_type")=="Profit and Loss" + if is_expense_account and not item.get("cost_center"): + frappe.throw(_("{0} {1}: Cost Center is mandatory for Item {2}").format( + _(self.doctype), self.name, item.get("item_code"))) def get_sl_entries(self, d, args): sl_dict = { From 444f956e7b6f0d085ce09a1048164065ad55e6c2 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 20 Jun 2014 15:59:49 +0530 Subject: [PATCH 02/27] Pricing Rule fixes and improvements. Fixes #1795 --- .../doctype/pricing_rule/pricing_rule.js | 27 ++ .../doctype/pricing_rule/pricing_rule.json | 440 +++++++++--------- .../doctype/pricing_rule/pricing_rule.py | 196 +++++++- .../doctype/pricing_rule/test_pricing_rule.py | 7 +- .../purchase_invoice/purchase_invoice.json | 16 +- .../doctype/sales_invoice/sales_invoice.json | 16 +- .../purchase_order/purchase_order.json | 16 +- .../doctype/purchase_order/purchase_order.py | 13 +- .../supplier_quotation.json | 16 +- .../supplier_quotation/supplier_quotation.py | 1 + erpnext/controllers/accounts_controller.py | 6 +- erpnext/patches.txt | 1 + .../set_pricing_rule_for_buying_or_selling.py | 12 + erpnext/public/js/transaction.js | 117 +++-- .../selling/doctype/quotation/quotation.json | 16 +- .../selling/doctype/quotation/quotation.py | 2 +- .../doctype/sales_order/sales_order.json | 16 +- .../doctype/sales_order/sales_order.py | 9 +- .../doctype/delivery_note/delivery_note.json | 16 +- .../doctype/delivery_note/delivery_note.py | 1 + erpnext/stock/doctype/item/test_item.py | 12 +- .../purchase_receipt/purchase_receipt.json | 16 +- .../purchase_receipt/purchase_receipt.py | 1 + erpnext/stock/get_item_details.py | 161 +------ 24 files changed, 695 insertions(+), 439 deletions(-) create mode 100644 erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js index 356cc0de9c3..a1859e5d570 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js @@ -61,4 +61,31 @@ frappe.ui.form.on("Pricing Rule", "refresh", function(frm) { ''].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 b20563fa76a..e15cea83b85 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -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" -} +} \ 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 a15b45a381a..5cf500a597a 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -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 diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index b17c9952984..e8496d068b0 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 @@ 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" diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 505a3ba79a0..8eb3b0907e2 100755 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -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, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 983f2bb405e..a07b69d09f0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -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, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index d293683ef48..794c0415bd2 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -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, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 91cc865b7b9..3a081249f38 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -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) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 19b0283c502..c3c5bf4f39e 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -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, diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py index 74a37b38d56..2af7bb93a69 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py @@ -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") diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3af82903a67..847e09e73d5 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -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(): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index dd57c60b189..041bbd3cc00 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -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 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 00000000000..218029d8aef --- /dev/null +++ b/erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py @@ -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')""") diff --git a/erpnext/public/js/transaction.js b/erpnext/public/js/transaction.js index 2c372042eab..ea576d5ae07 100644 --- a/erpnext/public/js/transaction.js +++ b/erpnext/public/js/transaction.js @@ -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) { diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 02217386de8..1ae0adb3636 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -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, diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 62577db247f..f396191a2df 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -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") diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index c8992271dcc..a036370db54 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -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, diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 24da5773a35..c14612ba34e 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -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") diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 9b13b10ec89..690fd055fad 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -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, diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index bbc9f81ff47..4b147cc3610 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -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: diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 506e5d016c3..7ab93ebf4c0 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -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') \ 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 e585bef754b..ae748ce3fe3 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -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, diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 13bb193f4ba..71c07eba026 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -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") diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index c5c1280fdb2..fe320d153a8 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -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` From be50c289f8ffcd36dfb8ba927e884d9d966f1ef0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 20 Jun 2014 16:19:55 +0530 Subject: [PATCH 03/27] validate pricing rule discount with item max discount --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 5cf500a597a..c86e3f61e57 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -19,6 +19,7 @@ class PricingRule(Document): 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"]: @@ -61,6 +62,13 @@ class PricingRule(Document): 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 flt(self.discount_percentage) > max_discount: + throw(_("Max discount allowed for item: {0} is {1}%".format(self.item_code, max_discount))) + + #-------------------------------------------------------------------------------- @frappe.whitelist() From 1d37698d4c1efb7e15c7a17bdf412105d5502b79 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 20 Jun 2014 16:30:44 +0530 Subject: [PATCH 04/27] Reload pricing rule in patch --- erpnext/patches/v4_0/set_pricing_rule_for_buying_or_selling.py | 1 + 1 file changed, 1 insertion(+) 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 index 218029d8aef..8be846ff162 100644 --- 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 @@ -5,6 +5,7 @@ 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')""") From 3c946b2a72d014f85f429f5f73b14c62ec692e2a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 20 Jun 2014 19:07:44 +0530 Subject: [PATCH 05/27] validate pricing rule discount with item max discount --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index c86e3f61e57..77b52b18f81 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -65,7 +65,7 @@ class PricingRule(Document): 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 flt(self.discount_percentage) > 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))) From 56f6d017574235beb9ff4d4f140b3477e400964a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 20 Jun 2014 19:37:53 +0530 Subject: [PATCH 06/27] Minor fixes --- .../doctype/pricing_rule/pricing_rule.json | 19 +++++++++---------- .../doctype/pricing_rule/pricing_rule.py | 13 +++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index e15cea83b85..2d318c6360e 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -126,18 +126,18 @@ "options": "Supplier Type", "permlevel": 0 }, - { - "fieldname": "max_qty", - "fieldtype": "Float", - "label": "Max Qty", - "permlevel": 0 - }, { "fieldname": "min_qty", "fieldtype": "Float", "label": "Min Qty", "permlevel": 0 }, + { + "fieldname": "max_qty", + "fieldtype": "Float", + "label": "Max Qty", + "permlevel": 0 + }, { "fieldname": "col_break1", "fieldtype": "Column Break", @@ -235,7 +235,7 @@ "icon": "icon-gift", "idx": 1, "istable": 0, - "modified": "2014-06-19 15:00:09.962572", + "modified": "2014-06-20 19:36:22.502381", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", @@ -244,8 +244,8 @@ { "create": 1, "delete": 1, - "export": 0, - "import": 0, + "export": 1, + "import": 1, "permlevel": 0, "read": 1, "report": 1, @@ -290,7 +290,6 @@ "permlevel": 0, "read": 1, "report": 1, - "restrict": 1, "role": "System Manager", "write": 1 } diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 77b52b18f81..967d583aab4 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -66,7 +66,7 @@ class PricingRule(Document): 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))) + throw(_("Max discount allowed for item: {0} is {1}%").format(self.item_code, max_discount)) #-------------------------------------------------------------------------------- @@ -141,7 +141,8 @@ def get_pricing_rule_for_item(args): 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), + "price_list_rate": pricing_rule.price/flt(args.conversion_rate) \ + if args.conversion_rate else 0.0, "discount_percentage": 0.0 }) else: @@ -167,10 +168,10 @@ def get_pricing_rules(args): 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+", '') = ''" + 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) From f0a1735ac81d023703617fcfa38822841cba48af Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 20 Jun 2014 20:19:51 +0530 Subject: [PATCH 07/27] Naming Series property type as Text --- erpnext/setup/doctype/naming_series/naming_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/naming_series/naming_series.py b/erpnext/setup/doctype/naming_series/naming_series.py index 100c8ba1cfc..cb0d43780fd 100644 --- a/erpnext/setup/doctype/naming_series/naming_series.py +++ b/erpnext/setup/doctype/naming_series/naming_series.py @@ -72,7 +72,7 @@ class NamingSeries(Document): 'field_name': 'naming_series', 'property': prop, 'value': prop_dict[prop], - 'property_type': 'Select', + 'property_type': 'Text', '__islocal': 1 }) ps.save() From 58ff651c6d1aca04aef1addb4b7d438ba34a687f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Thu, 19 Jun 2014 14:43:54 +0530 Subject: [PATCH 08/27] fixes to bom.js --- erpnext/manufacturing/doctype/bom/bom.js | 41 ++++++++++++------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 1cee6b9103b..ef4f399bdc8 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -2,6 +2,7 @@ // License: GNU General Public License v3. See license.txt // On REFRESH +frappe.provide("erpnext.bom"); cur_frm.cscript.refresh = function(doc,dt,dn){ cur_frm.toggle_enable("item", doc.__islocal); @@ -10,7 +11,7 @@ cur_frm.cscript.refresh = function(doc,dt,dn){ } cur_frm.cscript.with_operations(doc); - set_operation_no(doc); + erpnext.bom.set_operation_no(doc); } cur_frm.cscript.update_cost = function() { @@ -30,10 +31,10 @@ cur_frm.cscript.with_operations = function(doc) { cur_frm.cscript.operation_no = function(doc, cdt, cdn) { var child = locals[cdt][cdn]; - if(child.parentfield=="bom_operations") set_operation_no(doc); + if(child.parentfield=="bom_operations") erpnext.bom.set_operation_no(doc); } -var set_operation_no = function(doc) { +erpnext.bom.set_operation_no = function(doc) { var op_table = doc.bom_operations || []; var operations = []; @@ -53,7 +54,7 @@ var set_operation_no = function(doc) { } cur_frm.fields_dict["bom_operations"].grid.on_row_delete = function(cdt, cdn){ - set_operation_no(doc); + erpnext.bom.set_operation_no(doc); } cur_frm.add_fetch("item", "description", "description"); @@ -64,15 +65,15 @@ cur_frm.cscript.workstation = function(doc,dt,dn) { frappe.model.with_doc("Workstation", d.workstation, function(i, r) { d.hour_rate = r.docs[0].hour_rate; refresh_field("hour_rate", dn, "bom_operations"); - calculate_op_cost(doc); - calculate_total(doc); + erpnext.bom.calculate_op_cost(doc); + erpnext.bom.calculate_total(doc); }); } cur_frm.cscript.hour_rate = function(doc, dt, dn) { - calculate_op_cost(doc); - calculate_total(doc); + erpnext.bom.calculate_op_cost(doc); + erpnext.bom.calculate_total(doc); } @@ -106,8 +107,8 @@ var get_bom_material_detail= function(doc, cdt, cdn) { $.extend(d, r.message); refresh_field("bom_materials"); doc = locals[doc.doctype][doc.name]; - calculate_rm_cost(doc); - calculate_total(doc); + erpnext.bom.calculate_rm_cost(doc); + erpnext.bom.calculate_total(doc); }, freeze: true }); @@ -116,8 +117,8 @@ var get_bom_material_detail= function(doc, cdt, cdn) { cur_frm.cscript.qty = function(doc, cdt, cdn) { - calculate_rm_cost(doc); - calculate_total(doc); + erpnext.bom.calculate_rm_cost(doc); + erpnext.bom.calculate_total(doc); } cur_frm.cscript.rate = function(doc, cdt, cdn) { @@ -126,12 +127,12 @@ cur_frm.cscript.rate = function(doc, cdt, cdn) { msgprint(__("You can not change rate if BOM mentioned agianst any item")); get_bom_material_detail(doc, cdt, cdn); } else { - calculate_rm_cost(doc); - calculate_total(doc); + erpnext.bom.calculate_rm_cost(doc); + erpnext.bom.calculate_total(doc); } } -var calculate_op_cost = function(doc) { +erpnext.bom.calculate_op_cost = function(doc) { var op = doc.bom_operations || []; total_op_cost = 0; for(var i=0;i Date: Mon, 23 Jun 2014 12:20:12 +0530 Subject: [PATCH 09/27] fix end of life query for item --- erpnext/controllers/queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 789e7a331a2..0f1d5f6dabe 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -141,7 +141,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): concat(substr(tabItem.description, 1, 40), "..."), description) as decription from tabItem where tabItem.docstatus < 2 - and (tabItem.end_of_life is null or tabItem.end_of_life > %(today)s) + and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00') and (tabItem.`{key}` LIKE %(txt)s or tabItem.item_name LIKE %(txt)s) {fcond} {mcond} From e84f8b23aaa509511415bcac0a2a7b01730eba9b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 23 Jun 2014 10:03:23 +0530 Subject: [PATCH 10/27] minor fix in sales invoice --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 +++--- erpnext/selling/doctype/sms_center/sms_center.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0d8eb50f03f..0f11af42c99 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -263,10 +263,10 @@ class SalesInvoice(SellingController): for d in self.get('entries'): item = frappe.db.sql("""select name,is_asset_item,is_sales_item from `tabItem` where name = %s and (ifnull(end_of_life,'')='' or end_of_life > now())""", d.item_code) - acc = frappe.db.sql("""select account_type from `tabAccount` + acc = frappe.db.sql("""select account_type from `tabAccount` where name = %s and docstatus != 2""", d.income_account) - if item and item[0][1] == 'Yes' and not acc[0][0] == 'Fixed Asset': - msgprint(_("Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item").format(d.item_code), raise_exception=True) + if item and item[0][1] == 'Yes' and acc and acc[0][0] != 'Fixed Asset': + msgprint(_("Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item").format(acc[0][0], d.item_code), raise_exception=True) def validate_with_previous_doc(self): super(SalesInvoice, self).validate_with_previous_doc(self.tname, { diff --git a/erpnext/selling/doctype/sms_center/sms_center.py b/erpnext/selling/doctype/sms_center/sms_center.py index 81939546bf0..8c4cad3207d 100644 --- a/erpnext/selling/doctype/sms_center/sms_center.py +++ b/erpnext/selling/doctype/sms_center/sms_center.py @@ -12,7 +12,6 @@ from frappe.model.document import Document from erpnext.setup.doctype.sms_settings.sms_settings import send_sms class SMSCenter(Document): - def create_receiver_list(self): rec, where_clause = '', '' if self.send_to == 'All Customer Contact': @@ -71,6 +70,7 @@ class SMSCenter(Document): return receiver_nos def send_sms(self): + receiver_list = [] if not self.message: msgprint(_("Please enter message before sending")) else: From 36da171a3dbfeb632fab044e2996a7b9ff6babf8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 23 Jun 2014 15:36:06 +0530 Subject: [PATCH 11/27] Set status button in serial no --- erpnext/stock/doctype/serial_no/serial_no.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/stock/doctype/serial_no/serial_no.js b/erpnext/stock/doctype/serial_no/serial_no.js index bb131f35c1a..f7c484b5f31 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.js +++ b/erpnext/stock/doctype/serial_no/serial_no.js @@ -17,4 +17,12 @@ cur_frm.cscript.onload = function() { frappe.ui.form.on("Serial No", "refresh", function(frm) { frm.toggle_enable("item_code", frm.doc.__islocal); + + if(frm.doc.status == "Sales Returned" && frm.doc.warehouse) + cur_frm.add_custom_button(__('Set Status as Available'), cur_frm.cscript.set_status_as_available); }); + +cur_frm.cscript.set_status_as_available = function() { + cur_frm.set_value("status", "Available"); + cur_frm.save() +} From da490e3f5353be00b9deb384813b48952a8db3cc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 23 Jun 2014 15:51:03 +0530 Subject: [PATCH 12/27] Set status button in serial no --- erpnext/stock/doctype/serial_no/serial_no.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.js b/erpnext/stock/doctype/serial_no/serial_no.js index f7c484b5f31..dce9d4569c8 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.js +++ b/erpnext/stock/doctype/serial_no/serial_no.js @@ -19,10 +19,8 @@ frappe.ui.form.on("Serial No", "refresh", function(frm) { frm.toggle_enable("item_code", frm.doc.__islocal); if(frm.doc.status == "Sales Returned" && frm.doc.warehouse) - cur_frm.add_custom_button(__('Set Status as Available'), cur_frm.cscript.set_status_as_available); + cur_frm.add_custom_button(__('Set Status as Available'), function() { + cur_frm.set_value("status", "Available"); + cur_frm.save(); + }); }); - -cur_frm.cscript.set_status_as_available = function() { - cur_frm.set_value("status", "Available"); - cur_frm.save() -} From 16fa6472b3b2184db9e8551e19651f32c3b2838a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 23 Jun 2014 16:42:11 +0530 Subject: [PATCH 13/27] Made warehouse and selling pricing list non-mandatory in pos setting --- erpnext/accounts/doctype/pos_setting/pos_setting.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pos_setting/pos_setting.json b/erpnext/accounts/doctype/pos_setting/pos_setting.json index 27d79f31df9..d0a338c92ad 100755 --- a/erpnext/accounts/doctype/pos_setting/pos_setting.json +++ b/erpnext/accounts/doctype/pos_setting/pos_setting.json @@ -62,7 +62,7 @@ "options": "Price List", "permlevel": 0, "read_only": 0, - "reqd": 1 + "reqd": 0 }, { "fieldname": "company", @@ -147,7 +147,7 @@ "options": "Warehouse", "permlevel": 0, "read_only": 0, - "reqd": 1 + "reqd": 0 }, { "fieldname": "cost_center", @@ -205,7 +205,7 @@ ], "icon": "icon-cog", "idx": 1, - "modified": "2014-05-27 03:49:14.735138", + "modified": "2014-06-23 16:40:59.510132", "modified_by": "Administrator", "module": "Accounts", "name": "POS Setting", From 83da92fdccba4c704cfc78f8f24cd85b5693de51 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 23 Jun 2014 18:04:07 +0530 Subject: [PATCH 14/27] Naming Series Property Setter Patch --- .../accounts/doctype/gl_entry/gl_entry.json | 367 ++++++++-------- .../period_closing_voucher.json | 5 +- .../purchase_order/purchase_order.json | 5 +- .../quality_inspection.json | 5 +- .../supplier_quotation.json | 5 +- erpnext/hr/doctype/appraisal/appraisal.json | 5 +- .../doctype/expense_claim/expense_claim.json | 5 +- .../leave_allocation/leave_allocation.json | 5 +- .../hr/doctype/salary_slip/salary_slip.json | 5 +- .../production_order/production_order.json | 405 +++++++++--------- erpnext/patches.txt | 1 + erpnext/patches/repair_tools/__init__.py | 0 ...ix_naming_series_records_lost_by_reload.py | 204 +++++++++ .../v4_0/set_naming_series_property_setter.py | 98 +++++ .../installation_note/installation_note.json | 5 +- .../doctype/opportunity/opportunity.json | 5 +- .../selling/doctype/quotation/quotation.json | 5 +- .../doctype/sales_order/sales_order.json | 5 +- .../doctype/delivery_note/delivery_note.json | 5 +- .../material_request/material_request.json | 5 +- .../purchase_receipt/purchase_receipt.json | 4 +- .../stock_ledger_entry.json | 8 +- .../customer_issue/customer_issue.json | 5 +- .../maintenance_visit/maintenance_visit.json | 5 +- 24 files changed, 746 insertions(+), 421 deletions(-) create mode 100644 erpnext/patches/repair_tools/__init__.py create mode 100644 erpnext/patches/repair_tools/fix_naming_series_records_lost_by_reload.py create mode 100644 erpnext/patches/v4_0/set_naming_series_property_setter.py diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index ce17278d82c..07578e2761a 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -1,226 +1,229 @@ { - "autoname": "GL.#######", - "creation": "2013-01-10 16:34:06", - "docstatus": 0, - "doctype": "DocType", + "autoname": "GL.#######", + "creation": "2013-01-10 16:34:06", + "docstatus": 0, + "doctype": "DocType", "fields": [ { - "fieldname": "posting_date", - "fieldtype": "Date", - "in_filter": 1, - "in_list_view": 1, - "label": "Posting Date", - "oldfieldname": "posting_date", - "oldfieldtype": "Date", - "permlevel": 0, + "fieldname": "posting_date", + "fieldtype": "Date", + "in_filter": 1, + "in_list_view": 1, + "label": "Posting Date", + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "permlevel": 0, "search_index": 1 - }, + }, { - "fieldname": "transaction_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Transaction Date", - "oldfieldname": "transaction_date", - "oldfieldtype": "Date", + "fieldname": "transaction_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Transaction Date", + "oldfieldname": "transaction_date", + "oldfieldtype": "Date", "permlevel": 0 - }, + }, { - "fieldname": "aging_date", - "fieldtype": "Date", - "in_filter": 1, - "in_list_view": 1, - "label": "Aging Date", - "oldfieldname": "aging_date", - "oldfieldtype": "Date", - "permlevel": 0, + "fieldname": "aging_date", + "fieldtype": "Date", + "in_filter": 1, + "in_list_view": 1, + "label": "Aging Date", + "oldfieldname": "aging_date", + "oldfieldtype": "Date", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "account", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 1, - "label": "Account", - "oldfieldname": "account", - "oldfieldtype": "Link", - "options": "Account", - "permlevel": 0, + "fieldname": "account", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "label": "Account", + "oldfieldname": "account", + "oldfieldtype": "Link", + "options": "Account", + "permlevel": 0, "search_index": 1 - }, + }, { - "fieldname": "cost_center", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 1, - "label": "Cost Center", - "oldfieldname": "cost_center", - "oldfieldtype": "Link", - "options": "Cost Center", - "permlevel": 0, + "fieldname": "cost_center", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "label": "Cost Center", + "oldfieldname": "cost_center", + "oldfieldtype": "Link", + "options": "Cost Center", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "debit", - "fieldtype": "Currency", - "label": "Debit Amt", - "oldfieldname": "debit", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", + "fieldname": "debit", + "fieldtype": "Currency", + "label": "Debit Amt", + "oldfieldname": "debit", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", "permlevel": 0 - }, + }, { - "fieldname": "credit", - "fieldtype": "Currency", - "label": "Credit Amt", - "oldfieldname": "credit", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", + "fieldname": "credit", + "fieldtype": "Currency", + "label": "Credit Amt", + "oldfieldname": "credit", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", "permlevel": 0 - }, + }, { - "fieldname": "against", - "fieldtype": "Text", - "in_filter": 1, - "label": "Against", - "oldfieldname": "against", - "oldfieldtype": "Text", + "fieldname": "against", + "fieldtype": "Text", + "in_filter": 1, + "label": "Against", + "oldfieldname": "against", + "oldfieldtype": "Text", "permlevel": 0 - }, + }, { - "fieldname": "against_voucher", - "fieldtype": "Data", - "in_filter": 1, - "label": "Against Voucher", - "oldfieldname": "against_voucher", - "oldfieldtype": "Data", - "permlevel": 0, + "fieldname": "against_voucher_type", + "fieldtype": "Link", + "in_filter": 0, + "label": "Against Voucher Type", + "oldfieldname": "against_voucher_type", + "oldfieldtype": "Data", + "options": "DocType", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "against_voucher_type", - "fieldtype": "Data", - "in_filter": 0, - "label": "Against Voucher Type", - "oldfieldname": "against_voucher_type", - "oldfieldtype": "Data", - "permlevel": 0, + "fieldname": "against_voucher", + "fieldtype": "Dynamic Link", + "in_filter": 1, + "label": "Against Voucher", + "oldfieldname": "against_voucher", + "oldfieldtype": "Data", + "options": "against_voucher_type", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "voucher_type", - "fieldtype": "Select", - "in_filter": 1, - "label": "Voucher Type", - "oldfieldname": "voucher_type", - "oldfieldtype": "Select", - "options": "Journal Voucher\nSales Invoice\nPurchase Invoice\nPeriod Closing Voucher\nPurchase Receipt\nDelivery Note\nStock Entry\nStock Reconciliation", - "permlevel": 0, + "fieldname": "voucher_type", + "fieldtype": "Link", + "in_filter": 1, + "label": "Voucher Type", + "oldfieldname": "voucher_type", + "oldfieldtype": "Select", + "options": "DocType", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "voucher_no", - "fieldtype": "Data", - "in_filter": 1, - "label": "Voucher No", - "oldfieldname": "voucher_no", - "oldfieldtype": "Data", - "permlevel": 0, + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "in_filter": 1, + "label": "Voucher No", + "oldfieldname": "voucher_no", + "oldfieldtype": "Data", + "options": "voucher_type", + "permlevel": 0, "search_index": 1 - }, + }, { - "fieldname": "remarks", - "fieldtype": "Text", - "in_filter": 1, - "label": "Remarks", - "no_copy": 1, - "oldfieldname": "remarks", - "oldfieldtype": "Text", - "permlevel": 0, + "fieldname": "remarks", + "fieldtype": "Text", + "in_filter": 1, + "label": "Remarks", + "no_copy": 1, + "oldfieldname": "remarks", + "oldfieldtype": "Text", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "is_opening", - "fieldtype": "Select", - "in_filter": 1, - "label": "Is Opening", - "oldfieldname": "is_opening", - "oldfieldtype": "Select", - "options": "No\nYes", - "permlevel": 0, + "fieldname": "is_opening", + "fieldtype": "Select", + "in_filter": 1, + "label": "Is Opening", + "oldfieldname": "is_opening", + "oldfieldtype": "Select", + "options": "No\nYes", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "is_advance", - "fieldtype": "Select", - "in_filter": 0, - "label": "Is Advance", - "oldfieldname": "is_advance", - "oldfieldtype": "Select", - "options": "No\nYes", - "permlevel": 0, + "fieldname": "is_advance", + "fieldtype": "Select", + "in_filter": 0, + "label": "Is Advance", + "oldfieldname": "is_advance", + "oldfieldtype": "Select", + "options": "No\nYes", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "fiscal_year", - "fieldtype": "Link", - "in_filter": 1, - "label": "Fiscal Year", - "oldfieldname": "fiscal_year", - "oldfieldtype": "Select", - "options": "Fiscal Year", - "permlevel": 0, + "fieldname": "fiscal_year", + "fieldtype": "Link", + "in_filter": 1, + "label": "Fiscal Year", + "oldfieldname": "fiscal_year", + "oldfieldtype": "Select", + "options": "Fiscal Year", + "permlevel": 0, "search_index": 0 - }, + }, { - "fieldname": "company", - "fieldtype": "Link", - "in_filter": 1, - "label": "Company", - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "permlevel": 0, + "fieldname": "company", + "fieldtype": "Link", + "in_filter": 1, + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "permlevel": 0, "search_index": 0 } - ], - "icon": "icon-list", - "idx": 1, - "in_create": 1, - "modified": "2014-06-19 01:51:29.340077", - "modified_by": "Administrator", - "module": "Accounts", - "name": "GL Entry", - "owner": "Administrator", + ], + "icon": "icon-list", + "idx": 1, + "in_create": 1, + "modified": "2014-06-23 08:07:30.678730", + "modified_by": "Administrator", + "module": "Accounts", + "name": "GL Entry", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 1, - "create": 0, - "email": 1, - "export": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "submit": 0, + "amend": 0, + "apply_user_permissions": 1, + "create": 0, + "email": 1, + "export": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "submit": 0, "write": 0 - }, + }, { - "amend": 0, - "create": 0, - "email": 1, - "export": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "submit": 0, + "amend": 0, + "create": 0, + "email": 1, + "export": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "submit": 0, "write": 0 } - ], - "search_fields": "voucher_no,account,posting_date,against_voucher", - "sort_field": "modified", + ], + "search_fields": "voucher_no,account,posting_date,against_voucher", + "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json index e1aa66ff113..c9e7dc2c3d1 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json @@ -43,13 +43,14 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "ignore_user_permissions": 1, "in_list_view": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Period Closing Voucher", "permlevel": 0, "read_only": 1 }, @@ -101,7 +102,7 @@ "icon": "icon-file-text", "idx": 1, "is_submittable": 1, - "modified": "2014-05-26 03:05:50.722547", + "modified": "2014-06-23 07:55:49.946225", "modified_by": "Administrator", "module": "Accounts", "name": "Period Closing Voucher", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 794c0415bd2..14693c434fc 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -104,13 +104,14 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Purchase Order", "permlevel": 0, "print_hide": 1, "read_only": 1, @@ -644,7 +645,7 @@ "icon": "icon-file-text", "idx": 1, "is_submittable": 1, - "modified": "2014-06-19 15:58:06.375217", + "modified": "2014-06-23 07:55:50.372486", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/quality_inspection/quality_inspection.json b/erpnext/buying/doctype/quality_inspection/quality_inspection.json index 4da6e63f36b..3e05b319b67 100644 --- a/erpnext/buying/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/buying/doctype/quality_inspection/quality_inspection.json @@ -168,12 +168,13 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Quality Inspection", "permlevel": 0, "print_hide": 1, "read_only": 1 @@ -206,7 +207,7 @@ "icon": "icon-search", "idx": 1, "is_submittable": 1, - "modified": "2014-05-26 03:05:52.140251", + "modified": "2014-06-23 07:55:51.183113", "modified_by": "Administrator", "module": "Buying", "name": "Quality Inspection", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index c3c5bf4f39e..955aa6857c8 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -104,13 +104,14 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Supplier Quotation", "permlevel": 0, "print_hide": 1, "read_only": 1, @@ -570,7 +571,7 @@ "icon": "icon-shopping-cart", "idx": 1, "is_submittable": 1, - "modified": "2014-06-19 15:54:27.919675", + "modified": "2014-06-23 07:55:52.993616", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/hr/doctype/appraisal/appraisal.json b/erpnext/hr/doctype/appraisal/appraisal.json index 2fec94f1e09..beddeefdd9d 100644 --- a/erpnext/hr/doctype/appraisal/appraisal.json +++ b/erpnext/hr/doctype/appraisal/appraisal.json @@ -179,13 +179,14 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Appraisal", "permlevel": 0, "print_hide": 1, "read_only": 1, @@ -196,7 +197,7 @@ "icon": "icon-thumbs-up", "idx": 1, "is_submittable": 1, - "modified": "2014-05-27 03:49:07.393120", + "modified": "2014-06-23 07:55:40.801381", "modified_by": "Administrator", "module": "HR", "name": "Appraisal", diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 4ebc30f362f..c13710af3fe 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -171,12 +171,13 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Expense Claim", "permlevel": 0, "print_hide": 1, "read_only": 1, @@ -187,7 +188,7 @@ "icon": "icon-money", "idx": 1, "is_submittable": 1, - "modified": "2014-05-27 03:49:10.736177", + "modified": "2014-06-23 07:55:48.580747", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index ca583a1e44c..ede86f3c632 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -121,13 +121,14 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Leave Allocation", "permlevel": 0, "print_hide": 1, "read_only": 1 @@ -136,7 +137,7 @@ "icon": "icon-ok", "idx": 1, "is_submittable": 1, - "modified": "2014-05-27 03:49:12.744348", + "modified": "2014-06-23 07:55:48.989894", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json index 374d11e93b7..5d2f028e9c4 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.json +++ b/erpnext/hr/doctype/salary_slip/salary_slip.json @@ -180,13 +180,14 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Salary Slip", "permlevel": 0, "print_hide": 1, "report_hide": 0 @@ -325,7 +326,7 @@ "icon": "icon-file-text", "idx": 1, "is_submittable": 1, - "modified": "2014-05-27 03:49:17.213045", + "modified": "2014-06-23 07:55:52.259962", "modified_by": "Administrator", "module": "HR", "name": "Salary Slip", diff --git a/erpnext/manufacturing/doctype/production_order/production_order.json b/erpnext/manufacturing/doctype/production_order/production_order.json index 7e068cff741..f5e43b0144e 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.json +++ b/erpnext/manufacturing/doctype/production_order/production_order.json @@ -1,256 +1,257 @@ { - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2013-01-10 16:34:16", - "docstatus": 0, - "doctype": "DocType", + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-01-10 16:34:16", + "docstatus": 0, + "doctype": "DocType", "fields": [ { - "fieldname": "item", - "fieldtype": "Section Break", - "label": "Item", - "options": "icon-gift", + "fieldname": "item", + "fieldtype": "Section Break", + "label": "Item", + "options": "icon-gift", "permlevel": 0 - }, + }, { - "default": "PRO-", - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "options": "PRO-", - "permlevel": 0, + "default": "PRO-", + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "PRO-", + "permlevel": 0, "reqd": 1 - }, + }, { - "depends_on": "eval:!doc.__islocal", - "fieldname": "status", - "fieldtype": "Select", - "in_filter": 1, - "in_list_view": 1, - "label": "Status", - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nStopped\nIn Process\nCompleted\nCancelled", - "permlevel": 0, - "read_only": 1, - "reqd": 1, + "depends_on": "eval:!doc.__islocal", + "fieldname": "status", + "fieldtype": "Select", + "in_filter": 1, + "in_list_view": 1, + "label": "Status", + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "\nDraft\nSubmitted\nStopped\nIn Process\nCompleted\nCancelled", + "permlevel": 0, + "read_only": 1, + "reqd": 1, "search_index": 1 - }, + }, { - "fieldname": "production_item", - "fieldtype": "Link", - "in_filter": 1, - "in_list_view": 1, - "label": "Item To Manufacture", - "oldfieldname": "production_item", - "oldfieldtype": "Link", - "options": "Item", - "permlevel": 0, - "read_only": 0, + "fieldname": "production_item", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "label": "Item To Manufacture", + "oldfieldname": "production_item", + "oldfieldtype": "Link", + "options": "Item", + "permlevel": 0, + "read_only": 0, "reqd": 1 - }, + }, { - "depends_on": "production_item", - "description": "Bill of Material to be considered for manufacturing", - "fieldname": "bom_no", - "fieldtype": "Link", - "in_list_view": 1, - "label": "BOM No", - "oldfieldname": "bom_no", - "oldfieldtype": "Link", - "options": "BOM", - "permlevel": 0, - "read_only": 0, + "depends_on": "production_item", + "description": "Bill of Material to be considered for manufacturing", + "fieldname": "bom_no", + "fieldtype": "Link", + "in_list_view": 1, + "label": "BOM No", + "oldfieldname": "bom_no", + "oldfieldtype": "Link", + "options": "BOM", + "permlevel": 0, + "read_only": 0, "reqd": 1 - }, + }, { - "default": "1", - "description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.", - "fieldname": "use_multi_level_bom", - "fieldtype": "Check", - "label": "Use Multi-Level BOM", + "default": "1", + "description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.", + "fieldname": "use_multi_level_bom", + "fieldtype": "Check", + "label": "Use Multi-Level BOM", "permlevel": 0 - }, + }, { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "permlevel": 0, - "read_only": 0, + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "permlevel": 0, + "read_only": 0, "width": "50%" - }, + }, { - "description": "Manufacture against Sales Order", - "fieldname": "sales_order", - "fieldtype": "Link", - "label": "Sales Order", - "options": "Sales Order", - "permlevel": 0, + "description": "Manufacture against Sales Order", + "fieldname": "sales_order", + "fieldtype": "Link", + "label": "Sales Order", + "options": "Sales Order", + "permlevel": 0, "read_only": 0 - }, + }, { - "depends_on": "production_item", - "fieldname": "qty", - "fieldtype": "Float", - "in_list_view": 1, - "label": "Qty To Manufacture", - "oldfieldname": "qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "read_only": 0, + "depends_on": "production_item", + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty To Manufacture", + "oldfieldname": "qty", + "oldfieldtype": "Currency", + "permlevel": 0, + "read_only": 0, "reqd": 1 - }, + }, { - "depends_on": "eval:doc.docstatus==1", - "description": "Automatically updated via Stock Entry of type Manufacture/Repack", - "fieldname": "produced_qty", - "fieldtype": "Float", - "label": "Manufactured Qty", - "no_copy": 1, - "oldfieldname": "produced_qty", - "oldfieldtype": "Currency", - "permlevel": 0, + "depends_on": "eval:doc.docstatus==1", + "description": "Automatically updated via Stock Entry of type Manufacture/Repack", + "fieldname": "produced_qty", + "fieldtype": "Float", + "label": "Manufactured Qty", + "no_copy": 1, + "oldfieldname": "produced_qty", + "oldfieldtype": "Currency", + "permlevel": 0, "read_only": 1 - }, + }, { - "depends_on": "sales_order", - "fieldname": "expected_delivery_date", - "fieldtype": "Date", - "label": "Expected Delivery Date", - "permlevel": 0, + "depends_on": "sales_order", + "fieldname": "expected_delivery_date", + "fieldtype": "Date", + "label": "Expected Delivery Date", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "warehouses", - "fieldtype": "Section Break", - "label": "Warehouses", - "options": "icon-building", + "fieldname": "warehouses", + "fieldtype": "Section Break", + "label": "Warehouses", + "options": "icon-building", "permlevel": 0 - }, + }, { - "depends_on": "production_item", - "description": "Manufactured quantity will be updated in this warehouse", - "fieldname": "fg_warehouse", - "fieldtype": "Link", - "in_list_view": 0, - "label": "For Warehouse", - "options": "Warehouse", - "permlevel": 0, - "read_only": 0, + "depends_on": "production_item", + "description": "Manufactured quantity will be updated in this warehouse", + "fieldname": "fg_warehouse", + "fieldtype": "Link", + "in_list_view": 0, + "label": "For Warehouse", + "options": "Warehouse", + "permlevel": 0, + "read_only": 0, "reqd": 0 - }, + }, { - "fieldname": "column_break_12", - "fieldtype": "Column Break", + "fieldname": "column_break_12", + "fieldtype": "Column Break", "permlevel": 0 - }, + }, { - "fieldname": "wip_warehouse", - "fieldtype": "Link", - "label": "Work-in-Progress Warehouse", - "options": "Warehouse", - "permlevel": 0, + "fieldname": "wip_warehouse", + "fieldtype": "Link", + "label": "Work-in-Progress Warehouse", + "options": "Warehouse", + "permlevel": 0, "reqd": 0 - }, + }, { - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Info", - "options": "icon-file-text", - "permlevel": 0, + "fieldname": "more_info", + "fieldtype": "Section Break", + "label": "More Info", + "options": "icon-file-text", + "permlevel": 0, "read_only": 0 - }, + }, { - "fieldname": "description", - "fieldtype": "Small Text", - "label": "Item Description", - "permlevel": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Item Description", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "project_name", - "fieldtype": "Link", - "in_filter": 1, - "label": "Project Name", - "oldfieldname": "project_name", - "oldfieldtype": "Link", - "options": "Project", - "permlevel": 0, + "fieldname": "project_name", + "fieldtype": "Link", + "in_filter": 1, + "label": "Project Name", + "oldfieldname": "project_name", + "oldfieldtype": "Link", + "options": "Project", + "permlevel": 0, "read_only": 0 - }, + }, { - "fieldname": "column_break2", - "fieldtype": "Column Break", - "permlevel": 0, - "read_only": 0, + "fieldname": "column_break2", + "fieldtype": "Column Break", + "permlevel": 0, + "read_only": 0, "width": "50%" - }, + }, { - "depends_on": "production_item", - "fieldname": "stock_uom", - "fieldtype": "Link", - "label": "Stock UOM", - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", - "options": "UOM", - "permlevel": 0, + "depends_on": "production_item", + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", + "permlevel": 0, "read_only": 1 - }, + }, { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "permlevel": 0, - "read_only": 0, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "permlevel": 0, + "read_only": 0, "reqd": 1 - }, + }, { - "fieldname": "amended_from", - "fieldtype": "Data", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "permlevel": 0, + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Production Order", + "permlevel": 0, "read_only": 1 } - ], - "icon": "icon-cogs", - "idx": 1, - "in_create": 0, - "is_submittable": 1, - "modified": "2014-05-27 03:49:15.008942", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Production Order", - "owner": "Administrator", + ], + "icon": "icon-cogs", + "idx": 1, + "in_create": 0, + "is_submittable": 1, + "modified": "2014-06-23 07:55:50.092300", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Production Order", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "apply_user_permissions": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "submit": 1, + "amend": 1, + "apply_user_permissions": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "submit": 1, "write": 1 - }, + }, { - "apply_user_permissions": 1, - "permlevel": 0, - "read": 1, - "report": 1, + "apply_user_permissions": 1, + "permlevel": 0, + "read": 1, + "report": 1, "role": "Material User" } ] -} +} \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 041bbd3cc00..d94dd53097b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -63,3 +63,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 +erpnext.patches.v4_0.set_naming_series_property_setter diff --git a/erpnext/patches/repair_tools/__init__.py b/erpnext/patches/repair_tools/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/patches/repair_tools/fix_naming_series_records_lost_by_reload.py b/erpnext/patches/repair_tools/fix_naming_series_records_lost_by_reload.py new file mode 100644 index 00000000000..7fb54b3cc84 --- /dev/null +++ b/erpnext/patches/repair_tools/fix_naming_series_records_lost_by_reload.py @@ -0,0 +1,204 @@ +# 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 +import json +from frappe.model.naming import make_autoname +from frappe.utils import cint +from frappe.utils.email_lib import sendmail_to_system_managers + +doctype_series_map = { + 'Attendance': 'ATT-', + 'C-Form': 'C-FORM-', + 'Customer': 'CUST-', + 'Customer Issue': 'CI-', + 'Delivery Note': 'DN-', + 'Installation Note': 'IN-', + 'Item': 'ITEM-', + 'Journal Voucher': 'JV-', + 'Lead': 'LEAD-', + 'Opportunity': 'OPTY-', + 'Packing Slip': 'PS-', + 'Production Order': 'PRO-', + 'Purchase Invoice': 'PINV-', + 'Purchase Order': 'PO-', + 'Purchase Receipt': 'PREC-', + 'Quality Inspection': 'QI-', + 'Quotation': 'QTN-', + 'Sales Invoice': 'SINV-', + 'Sales Order': 'SO-', + 'Stock Entry': 'STE-', + 'Supplier': 'SUPP-', + 'Supplier Quotation': 'SQTN-', + 'Support Ticket': 'SUP-' +} + +def check_docs_to_rename(): + if "erpnext" not in frappe.get_installed_apps(): + return + + docs_to_rename = get_docs_to_rename() + if docs_to_rename: + print "To Rename" + print json.dumps(docs_to_rename, indent=1, sort_keys=True) + + frappe.db.rollback() + +def check_gl_sl_entries_to_fix(): + if "erpnext" not in frappe.get_installed_apps(): + return + + gl_entries_to_fix = get_gl_entries_to_fix() + if gl_entries_to_fix: + print "General Ledger Entries to Fix" + print json.dumps(gl_entries_to_fix, indent=1, sort_keys=True) + + sl_entries_to_fix = get_sl_entries_to_fix() + if sl_entries_to_fix: + print "Stock Ledger Entries to Fix" + print json.dumps(sl_entries_to_fix, indent=1, sort_keys=True) + + frappe.db.rollback() + +def guess_reference_date(): + return (frappe.db.get_value("Patch Log", {"patch": "erpnext.patches.v4_0.validate_v3_patch"}, "creation") + or "2014-05-06") + +def get_docs_to_rename(): + reference_date = guess_reference_date() + + docs_to_rename = {} + for doctype, new_series in doctype_series_map.items(): + if doctype in ("Item", "Customer", "Lead", "Supplier"): + if not frappe.db.sql("""select name from `tab{doctype}` + where ifnull(naming_series, '')!='' + and name like concat(naming_series, '%%') limit 1""".format(doctype=doctype)): + continue + + # fix newly formed records using old series! + records_with_new_series = frappe.db.sql_list("""select name from `tab{doctype}` + where date(creation) >= date(%s) and naming_series=%s + and exists (select name from `tab{doctype}` where ifnull(naming_series, '') not in ('', %s) limit 1) + order by name asc""".format(doctype=doctype), (reference_date, new_series, new_series)) + + if records_with_new_series: + docs_to_rename[doctype] = records_with_new_series + + return docs_to_rename + +def get_gl_entries_to_fix(): + bad_gl_entries = {} + + for dt in frappe.db.sql_list("""select distinct voucher_type from `tabGL Entry` + where ifnull(voucher_type, '')!=''"""): + + if dt not in doctype_series_map: + continue + + out = frappe.db.sql("""select gl.name, gl.voucher_no from `tabGL Entry` gl + where ifnull(voucher_type, '')=%s and voucher_no like %s and + not exists (select name from `tab{voucher_type}` vt where vt.name=gl.voucher_no)""".format(voucher_type=dt), + (dt, doctype_series_map[dt] + "%%"), as_dict=True) + + if out: + bad_gl_entries.setdefault(dt, []).extend(out) + + for dt in frappe.db.sql_list("""select distinct against_voucher_type + from `tabGL Entry` where ifnull(against_voucher_type, '')!=''"""): + + if dt not in doctype_series_map: + continue + + out = frappe.db.sql("""select gl.name, gl.against_voucher from `tabGL Entry` gl + where ifnull(against_voucher_type, '')=%s and against_voucher like %s and + not exists (select name from `tab{against_voucher_type}` vt + where vt.name=gl.against_voucher)""".format(against_voucher_type=dt), + (dt, doctype_series_map[dt] + "%%"), as_dict=True) + + if out: + bad_gl_entries.setdefault(dt, []).extend(out) + + return bad_gl_entries + +def get_sl_entries_to_fix(): + bad_sl_entries = {} + + for dt in frappe.db.sql_list("""select distinct voucher_type from `tabStock Ledger Entry` + where ifnull(voucher_type, '')!=''"""): + + if dt not in doctype_series_map: + continue + + out = frappe.db.sql("""select sl.name, sl.voucher_no from `tabStock Ledger Entry` sl + where voucher_type=%s and voucher_no like %s and + not exists (select name from `tab{voucher_type}` vt where vt.name=sl.voucher_no)""".format(voucher_type=dt), + (dt, doctype_series_map[dt] + "%%"), as_dict=True) + if out: + bad_sl_entries.setdefault(dt, []).extend(out) + + return bad_sl_entries + +def add_comment(doctype, old_name, new_name): + frappe.get_doc({ + "doctype":"Comment", + "comment_by": frappe.session.user, + "comment_doctype": doctype, + "comment_docname": new_name, + "comment": """Renamed from **{old_name}** to {new_name}""".format(old_name=old_name, new_name=new_name) + }).insert(ignore_permissions=True) + +def _rename_doc(doctype, name, naming_series): + if frappe.get_meta(doctype).get_field("amended_from"): + amended_from = frappe.db.get_value(doctype, name, "amended_from") + else: + amended_from = None + + if amended_from: + am_id = 1 + am_prefix = amended_from + if frappe.db.get_value(doctype, amended_from, "amended_from"): + am_id = cint(amended_from.split('-')[-1]) + 1 + am_prefix = '-'.join(amended_from.split('-')[:-1]) # except the last hyphen + + fixed_name = am_prefix + '-' + str(am_id) + else: + fixed_name = make_autoname(naming_series+'.#####') + + frappe.db.set_value(doctype, name, "naming_series", naming_series) + frappe.rename_doc(doctype, name, fixed_name, force=True) + add_comment(doctype, name, fixed_name) + + return fixed_name + +def rename_docs(): + _log = [] + def log(msg): + _log.append(msg) + print msg + + commit = False + docs_to_rename = get_docs_to_rename() + for doctype, list_of_names in docs_to_rename.items(): + naming_series_field = frappe.get_meta(doctype).get_field("naming_series") + default_series = naming_series_field.default or filter(None, (naming_series_field.options or "").split("\n"))[0] + + print + print "Rename", doctype, list_of_names, "using series", default_series + confirm = raw_input("do it? (yes / anything else): ") + + if confirm == "yes": + commit = True + for name in list_of_names: + fixed_name = _rename_doc(doctype, name, default_series) + log("Renamed {doctype} {name} --> {fixed_name}".format(doctype=doctype, name=name, fixed_name=fixed_name)) + + if commit: + content = """These documents have been renamed in your ERPNext instance: {site}\n\n{_log}""".format(site=frappe.local.site, _log="\n".join(_log)) + + print content + + frappe.db.commit() + + sendmail_to_system_managers("[Important] [ERPNext] Renamed Documents via Patch", content) + diff --git a/erpnext/patches/v4_0/set_naming_series_property_setter.py b/erpnext/patches/v4_0/set_naming_series_property_setter.py new file mode 100644 index 00000000000..7161492cfa4 --- /dev/null +++ b/erpnext/patches/v4_0/set_naming_series_property_setter.py @@ -0,0 +1,98 @@ +# 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 +from frappe.core.doctype.property_setter.property_setter import make_property_setter + +doctype_series_map = { + 'Attendance': 'ATT-', + 'C-Form': 'C-FORM-', + 'Customer': 'CUST-', + 'Customer Issue': 'CI-', + 'Delivery Note': 'DN-', + 'Installation Note': 'IN-', + 'Item': 'ITEM-', + 'Journal Voucher': 'JV-', + 'Lead': 'LEAD-', + 'Opportunity': 'OPTY-', + 'Packing Slip': 'PS-', + 'Production Order': 'PRO-', + 'Purchase Invoice': 'PINV-', + 'Purchase Order': 'PO-', + 'Purchase Receipt': 'PREC-', + 'Quality Inspection': 'QI-', + 'Quotation': 'QTN-', + 'Sales Invoice': 'SINV-', + 'Sales Order': 'SO-', + 'Stock Entry': 'STE-', + 'Supplier': 'SUPP-', + 'Supplier Quotation': 'SQTN-', + 'Support Ticket': 'SUP-' +} + +def execute(): + series_to_set = get_series_to_set() + for doctype, opts in series_to_set.items(): + print "Setting naming series", doctype, opts + set_series(doctype, opts["options"], opts["default"]) + +def set_series(doctype, options, default): + make_property_setter(doctype, "naming_series", "options", options, "Text") + make_property_setter(doctype, "naming_series", "default", default, "Text") + +def get_series_to_set(): + series_to_set = {} + + for doctype, new_series in doctype_series_map.items(): + # you can't fix what does not exist :) + if not frappe.db.a_row_exists(doctype): + continue + + series_to_preserve = get_series_to_preserve(doctype, new_series) + + if not series_to_preserve: + continue + + default_series = get_default_series(doctype, new_series) + if not default_series: + continue + + existing_series = (frappe.get_meta(doctype).get_field("naming_series").options or "").split("\n") + existing_series = filter(None, [d.strip() for d in existing_series]) + + if (not (set(existing_series).difference(series_to_preserve) or set(series_to_preserve).difference(existing_series)) + and len(series_to_preserve)==len(existing_series)): + # print "No change for", doctype, ":", existing_series, "=", series_to_preserve + continue + + # set naming series property setter + series_to_preserve = list(set(series_to_preserve + existing_series)) + if new_series in series_to_preserve: + series_to_preserve.remove(new_series) + + if series_to_preserve: + series_to_set[doctype] = {"options": "\n".join(series_to_preserve), "default": default_series} + + return series_to_set + +def get_series_to_preserve(doctype, new_series): + series_to_preserve = frappe.db.sql_list("""select distinct naming_series from `tab{doctype}` + where ifnull(naming_series, '') not in ('', %s)""".format(doctype=doctype), new_series) + + series_to_preserve.sort() + + return series_to_preserve + +def get_default_series(doctype, new_series): + default_series = frappe.db.sql("""select naming_series from `tab{doctype}` where ifnull(naming_series, '') not in ('', %s) + and creation=(select max(creation) from `tab{doctype}` + where ifnull(naming_series, '') not in ('', %s)) order by creation desc limit 1""".format(doctype=doctype), + (new_series, new_series)) + + if not (default_series and default_series[0][0]): + print "[Skipping] Cannot guess which naming series to use for", doctype + return + + return default_series[0][0] diff --git a/erpnext/selling/doctype/installation_note/installation_note.json b/erpnext/selling/doctype/installation_note/installation_note.json index 859ff5f4ced..56df0615a32 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.json +++ b/erpnext/selling/doctype/installation_note/installation_note.json @@ -195,12 +195,13 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Installation Note", "permlevel": 0, "print_hide": 1, "read_only": 1 @@ -235,7 +236,7 @@ "icon": "icon-wrench", "idx": 1, "is_submittable": 1, - "modified": "2014-05-27 03:49:11.449598", + "modified": "2014-06-23 07:55:48.805241", "modified_by": "Administrator", "module": "Selling", "name": "Installation Note", diff --git a/erpnext/selling/doctype/opportunity/opportunity.json b/erpnext/selling/doctype/opportunity/opportunity.json index 249a0ff50cc..be5f52b4837 100644 --- a/erpnext/selling/doctype/opportunity/opportunity.json +++ b/erpnext/selling/doctype/opportunity/opportunity.json @@ -385,12 +385,13 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Opportunity", "permlevel": 0, "print_hide": 1, "read_only": 1, @@ -409,7 +410,7 @@ "icon": "icon-info-sign", "idx": 1, "is_submittable": 1, - "modified": "2014-05-27 03:49:14.057062", + "modified": "2014-06-23 07:55:49.718301", "modified_by": "Administrator", "module": "Selling", "name": "Opportunity", diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 1ae0adb3636..bdd27c4cc75 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -139,12 +139,13 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Quotation", "permlevel": 0, "print_hide": 1, "read_only": 1, @@ -826,7 +827,7 @@ "idx": 1, "is_submittable": 1, "max_attachments": 1, - "modified": "2014-06-19 15:59:30.019826", + "modified": "2014-06-23 07:55:51.859025", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index a036370db54..6c330e67e29 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -112,13 +112,14 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Sales Order", "permlevel": 0, "print_hide": 1, "read_only": 1, @@ -882,7 +883,7 @@ "idx": 1, "is_submittable": 1, "issingle": 0, - "modified": "2014-06-19 16:00:06.626037", + "modified": "2014-06-23 07:55:52.555192", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 690fd055fad..f1dc5a390e7 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -129,12 +129,13 @@ { "allow_on_submit": 0, "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Delivery Note", "permlevel": 0, "print_hide": 1, "print_width": "150px", @@ -1007,7 +1008,7 @@ "idx": 1, "in_create": 0, "is_submittable": 1, - "modified": "2014-06-19 16:00:47.326127", + "modified": "2014-06-23 07:55:47.859869", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index 8e8f75662a0..0f066dab3be 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -41,12 +41,13 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Material Request", "permlevel": 0, "print_hide": 1, "print_width": "150px", @@ -229,7 +230,7 @@ "icon": "icon-ticket", "idx": 1, "is_submittable": 1, - "modified": "2014-05-27 03:49:13.642995", + "modified": "2014-06-23 07:55:49.393708", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index ae748ce3fe3..4ea3864258a 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -524,7 +524,7 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, "label": "Amended From", @@ -762,7 +762,7 @@ "icon": "icon-truck", "idx": 1, "is_submittable": 1, - "modified": "2014-06-19 15:58:37.932064", + "modified": "2014-06-23 07:55:50.761516", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json index cdddabedf20..f6bd010ba89 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -91,11 +91,12 @@ }, { "fieldname": "voucher_type", - "fieldtype": "Data", + "fieldtype": "Link", "in_filter": 1, "label": "Voucher Type", "oldfieldname": "voucher_type", "oldfieldtype": "Data", + "options": "DocType", "permlevel": 0, "print_width": "150px", "read_only": 1, @@ -104,11 +105,12 @@ }, { "fieldname": "voucher_no", - "fieldtype": "Data", + "fieldtype": "Dynamic Link", "in_filter": 1, "label": "Voucher No", "oldfieldname": "voucher_no", "oldfieldtype": "Data", + "options": "voucher_type", "permlevel": 0, "print_width": "150px", "read_only": 1, @@ -264,7 +266,7 @@ "icon": "icon-list", "idx": 1, "in_create": 1, - "modified": "2014-06-09 01:51:44.014466", + "modified": "2014-06-23 08:07:56.370276", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", diff --git a/erpnext/support/doctype/customer_issue/customer_issue.json b/erpnext/support/doctype/customer_issue/customer_issue.json index 8230f9cc88f..b755135b737 100644 --- a/erpnext/support/doctype/customer_issue/customer_issue.json +++ b/erpnext/support/doctype/customer_issue/customer_issue.json @@ -379,13 +379,14 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Customer Issue", "permlevel": 0, "print_hide": 1, "width": "150px" @@ -394,7 +395,7 @@ "icon": "icon-bug", "idx": 1, "is_submittable": 0, - "modified": "2014-05-27 03:49:09.483145", + "modified": "2014-06-23 07:55:47.488335", "modified_by": "Administrator", "module": "Support", "name": "Customer Issue", diff --git a/erpnext/support/doctype/maintenance_visit/maintenance_visit.json b/erpnext/support/doctype/maintenance_visit/maintenance_visit.json index 4a13e400030..743d210861f 100644 --- a/erpnext/support/doctype/maintenance_visit/maintenance_visit.json +++ b/erpnext/support/doctype/maintenance_visit/maintenance_visit.json @@ -190,12 +190,13 @@ }, { "fieldname": "amended_from", - "fieldtype": "Data", + "fieldtype": "Link", "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", + "options": "Maintenance Visit", "permlevel": 0, "print_hide": 1, "read_only": 1, @@ -278,7 +279,7 @@ "icon": "icon-file-text", "idx": 1, "is_submittable": 1, - "modified": "2014-05-27 03:49:13.466221", + "modified": "2014-06-23 07:55:49.200714", "modified_by": "Administrator", "module": "Support", "name": "Maintenance Visit", From 65efd0a9a10fcf393ba3827e6858cccb216ff484 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 23 Jun 2014 18:21:37 +0530 Subject: [PATCH 15/27] sms sending issue fixed --- erpnext/setup/doctype/sms_settings/sms_settings.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/sms_settings/sms_settings.py b/erpnext/setup/doctype/sms_settings/sms_settings.py index a9afacfe30e..de1164118e7 100644 --- a/erpnext/setup/doctype/sms_settings/sms_settings.py +++ b/erpnext/setup/doctype/sms_settings/sms_settings.py @@ -16,8 +16,7 @@ def validate_receiver_nos(receiver_list): validated_receiver_list = [] for d in receiver_list: # remove invalid character - invalid_char_list = [' ', '+', '-', '(', ')'] - for x in invalid_char_list: + for x in [' ', '+', '-', '(', ')']: d = d.replace(x, '') validated_receiver_list.append(d) @@ -48,6 +47,13 @@ def get_contact_number(contact_name, value, key): @frappe.whitelist() def send_sms(receiver_list, msg, sender_name = ''): + + import json + if isinstance(receiver_list, basestring): + receiver_list = json.loads(receiver_list) + if not isinstance(receiver_list, list): + receiver_list = [receiver_list] + receiver_list = validate_receiver_nos(receiver_list) arg = { From f62b75c731f9aa597a054ff0d2e6986795d3f5d9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 24 Jun 2014 11:00:13 +0530 Subject: [PATCH 16/27] Fixes in pricing rule --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 967d583aab4..2d592d1f96d 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe import json +import copy from frappe import throw, _ from frappe.utils import flt, cint from frappe.model.document import Document @@ -106,8 +107,9 @@ def apply_pricing_rule(args): args.transaction_type = "buying" if frappe.get_meta(args.parenttype).get_field("supplier") \ else "selling" + args_copy = copy.deepcopy(args) + args_copy.pop("item_list") for item in args.get("item_list"): - args_copy = args.copy() args_copy.update(item) out.append(get_pricing_rule_for_item(args_copy)) From efd426650de0db4348aa8e84dc0ac575ecc4e80e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 24 Jun 2014 11:11:08 +0530 Subject: [PATCH 17/27] Fixes in pricing rule --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 2d592d1f96d..0474639377d 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -107,9 +107,9 @@ def apply_pricing_rule(args): args.transaction_type = "buying" if frappe.get_meta(args.parenttype).get_field("supplier") \ else "selling" - args_copy = copy.deepcopy(args) - args_copy.pop("item_list") for item in args.get("item_list"): + args_copy = copy.deepcopy(args) + args_copy.pop("item_list") args_copy.update(item) out.append(get_pricing_rule_for_item(args_copy)) From 613d784cfb6b92fbdefdd8d5ddea77ce01cf337f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 24 Jun 2014 12:07:56 +0530 Subject: [PATCH 18/27] Fixes in pricing rule --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 0474639377d..2706603bc68 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -107,9 +107,11 @@ def apply_pricing_rule(args): args.transaction_type = "buying" if frappe.get_meta(args.parenttype).get_field("supplier") \ else "selling" - for item in args.get("item_list"): + item_list = args.get("item_list") + args.pop("item_list") + + for item in item_list: args_copy = copy.deepcopy(args) - args_copy.pop("item_list") args_copy.update(item) out.append(get_pricing_rule_for_item(args_copy)) From fdf38dab6ffb5b48de646459b689ecf7f83a35f1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 24 Jun 2014 12:42:36 +0530 Subject: [PATCH 19/27] stock entry incoming rate based on precision --- erpnext/stock/doctype/stock_entry/stock_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 297542b50f4..125a293586b 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -220,8 +220,8 @@ class StockEntry(StockController): # get incoming rate if not d.bom_no: if not flt(d.incoming_rate) or d.s_warehouse or self.purpose == "Sales Return": - incoming_rate = self.get_incoming_rate(args) - if flt(incoming_rate) > 0: + incoming_rate = flt(self.get_incoming_rate(args), self.precision("incoming_rate", d)) + if incoming_rate > 0: d.incoming_rate = incoming_rate d.amount = flt(d.transfer_qty) * flt(d.incoming_rate) From 21efc93e5424c2505a2c2a5a3f59b964f4b6dc49 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 24 Jun 2014 12:44:49 +0530 Subject: [PATCH 20/27] Allowed renaming for project --- erpnext/projects/doctype/project/project.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 5489d334687..c894bb8b26b 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -1,6 +1,7 @@ { "allow_attach": 1, "allow_import": 1, + "allow_rename": 1, "autoname": "field:project_name", "creation": "2013-03-07 11:55:07", "docstatus": 0, @@ -258,7 +259,7 @@ "icon": "icon-puzzle-piece", "idx": 1, "max_attachments": 4, - "modified": "2014-05-27 03:49:15.252736", + "modified": "2014-06-24 12:44:19.530707", "modified_by": "Administrator", "module": "Projects", "name": "Project", From a1be6021c4d13b1212dff74dc981a602951994fb Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 24 Jun 2014 15:26:40 +0530 Subject: [PATCH 21/27] Fix in pricing rule patch --- erpnext/patches/v4_0/customer_discount_to_pricing_rule.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py b/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py index fa35898df30..bd27174a0e8 100644 --- a/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py +++ b/erpnext/patches/v4_0/customer_discount_to_pricing_rule.py @@ -24,7 +24,8 @@ def execute(): "applicable_for": "Customer", "customer": d.parent, "price_or_discount": "Discount Percentage", - "discount_percentage": d.discount + "discount_percentage": d.discount, + "selling": 1 }).insert() frappe.db.auto_commit_on_many_writes = False From d0bf91d6399b891fc687e2aa858dacea14331a10 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 24 Jun 2014 16:19:16 +0530 Subject: [PATCH 22/27] Fixes for recurring invoice --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0f11af42c99..348543ee28d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -732,7 +732,7 @@ def notify_errors(inv, customer, owner): frappe.sendmail(recipients + [frappe.db.get_value("User", owner, "email")], subject="[Urgent] Error while creating recurring invoice for %s" % inv, - message = frappe.get_template("template/emails/recurring_invoice_failed.html").render({ + message = frappe.get_template("templates/emails/recurring_invoice_failed.html").render({ "name": inv, "customer": customer })) From e9b95bbfe86919b9a0ca70fdc7e2c569a812a139 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 24 Jun 2014 16:35:00 +0530 Subject: [PATCH 23/27] Fixed address deletion issue --- .../doctype/contact_control/contact_control.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/erpnext/setup/doctype/contact_control/contact_control.js b/erpnext/setup/doctype/contact_control/contact_control.js index 6b6878ffd5c..b0535416839 100755 --- a/erpnext/setup/doctype/contact_control/contact_control.js +++ b/erpnext/setup/doctype/contact_control/contact_control.js @@ -139,19 +139,10 @@ cur_frm.cscript.delete_doc = function(doctype, name) { var go_ahead = confirm(__("Delete {0} {1}?", [doctype, name])); if (!go_ahead) return; - return frappe.call({ - method: 'frappe.model.delete_doc', - args: { - dt: doctype, - dn: name - }, - callback: function(r) { - //console.log(r); - if (!r.exc) { - // run the correct list - var list_name = doctype.toLowerCase() + '_list'; - cur_frm[list_name].run(); - } + frappe.model.delete_doc(doctype, name, function(r) { + if (!r.exc) { + var list_name = doctype.toLowerCase() + '_list'; + cur_frm[list_name].run(); } }); } From 0b15755441c09a316a7bc48d60c90f9381dca5dd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 24 Jun 2014 17:02:45 +0530 Subject: [PATCH 24/27] Cost center-wise net profit in financial analytics report --- .../financial_analytics.js | 4 +- erpnext/selling/sales_common.js | 45 +++++++++---------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/page/financial_analytics/financial_analytics.js b/erpnext/accounts/page/financial_analytics/financial_analytics.js index 4574390ce1f..2da93813e9d 100644 --- a/erpnext/accounts/page/financial_analytics/financial_analytics.js +++ b/erpnext/accounts/page/financial_analytics/financial_analytics.js @@ -232,7 +232,7 @@ erpnext.FinancialAnalytics = erpnext.AccountTreeGrid.extend({ indent: 0, opening: 0, checked: false, - report_type: me.pl_or_bs, + report_type: me.pl_or_bs=="Balance Sheet"? "Balance Sheet" : "Profit and Loss", }; me.item_by_name[net_profit.name] = net_profit; @@ -244,7 +244,7 @@ erpnext.FinancialAnalytics = erpnext.AccountTreeGrid.extend({ $.each(me.data, function(i, ac) { if(!ac.parent_account && me.apply_filter(ac, "company") && - ac.report_type==me.pl_or_bs) { + ac.report_type==net_profit.report_type) { $.each(me.columns, function(i, col) { if(col.formatter==me.currency_formatter && col.balance_type=="Dr") { var bal = net_profit[col.date+"_dr"] - diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 63ffb36fb0b..aba390e2812 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -481,7 +481,27 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ set_dynamic_labels: function() { this._super(); - set_sales_bom_help(this.frm.doc); + this.set_sales_bom_help(this.frm.doc); + }, + + set_sales_bom_help: function(doc) { + if(!cur_frm.fields_dict.packing_list) return; + if ((doc.packing_details || []).length) { + $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true); + + if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + help_msg = "
" + + __("For 'Sales BOM' items, warehouse, serial no and batch no will be considered from the 'Packing List' table. If warehouse and batch no are same for all packing items for any 'Sales BOM' item, those values can be entered in the main item table, values will be copied to 'Packing List' table.")+ + "
"; + frappe.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = help_msg; + } + } else { + $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(false); + if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + frappe.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = ''; + } + } + refresh_field('sales_bom_help'); }, change_form_labels: function(company_currency) { @@ -574,26 +594,5 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ var df = frappe.meta.get_docfield(fname[0], fname[1], me.frm.doc.name); if(df) df.label = label; }); - }, -}); - -// Help for Sales BOM items -var set_sales_bom_help = function(doc) { - if(!cur_frm.fields_dict.packing_list) return; - if ((doc.packing_details || []).length) { - $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true); - - if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { - help_msg = "
" + - __("For 'Sales BOM' items, warehouse, serial no and batch no will be considered from the 'Packing List' table. If warehouse and batch no are same for all packing items for any 'Sales BOM' item, those values can be entered in the main item table, values will be copied to 'Packing List' table.")+ - "
"; - frappe.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = help_msg; - } - } else { - $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(false); - if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { - frappe.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = ''; - } } - refresh_field('sales_bom_help'); -} +}); From 60c48fee16a936a967500a4008484fd7804acf74 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 24 Jun 2014 17:34:52 +0530 Subject: [PATCH 25/27] minor fixes --- erpnext/selling/sales_common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index aba390e2812..2021490bb77 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -491,7 +491,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { help_msg = "
" + - __("For 'Sales BOM' items, warehouse, serial no and batch no will be considered from the 'Packing List' table. If warehouse and batch no are same for all packing items for any 'Sales BOM' item, those values can be entered in the main item table, values will be copied to 'Packing List' table.")+ + __("For 'Sales BOM' items, Warehouse, Serial No and Batch No will be considered from the 'Packing List' table. If Warehouse and Batch No are same for all packing items for any 'Sales BOM' item, those values can be entered in the main Item table, values will be copied to 'Packing List' table.")+ "
"; frappe.meta.get_docfield(doc.doctype, 'sales_bom_help', doc.name).options = help_msg; } From faefeaa644dd400745950a95b97520b5245fb293 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 24 Jun 2014 18:53:04 +0530 Subject: [PATCH 26/27] Warehouse query filtered by company --- erpnext/controllers/queries.py | 1 + .../public/js/controllers/stock_controller.js | 37 +++++++++++++++++-- erpnext/public/js/queries.js | 6 +++ erpnext/public/js/transaction.js | 2 + 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 0f1d5f6dabe..16d27d47fca 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -254,3 +254,4 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters): return frappe.widgets.reportview.execute("Account", filters = filter_list, fields = ["name", "parent_account"], limit_start=start, limit_page_length=page_len, as_list=True) + diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js index e50b2af35f3..97bcb6cf1c1 100644 --- a/erpnext/public/js/controllers/stock_controller.js +++ b/erpnext/public/js/controllers/stock_controller.js @@ -4,6 +4,37 @@ frappe.provide("erpnext.stock"); erpnext.stock.StockController = frappe.ui.form.Controller.extend({ + onload: function() { + // warehouse query if company + if (this.frm.fields_dict.company) { + this.setup_warehouse_query(); + } + }, + + setup_warehouse_query: function() { + var me = this; + + var _set_warehouse_query = function(doctype, parentfield) { + var warehouse_link_fields = frappe.meta.get_docfields(doctype, me.frm.doc.name, + {"fieldtype": "Link", "options": "Warehouse"}); + $.each(warehouse_link_fields, function(i, df) { + me.frm.set_query(df.fieldname, parentfield, function() { + return erpnext.queries.warehouse(me.frm.doc); + }) + }); + }; + + _set_warehouse_query(me.frm.doc.doctype); + + // warehouse field in tables + var table_fields = frappe.meta.get_docfields(me.frm.doc.doctype, me.frm.doc.name, + {"fieldtype": "Table"}); + + $.each(table_fields, function(i, df) { + _set_warehouse_query(df.options, df.fieldname); + }); + }, + show_stock_ledger: function() { var me = this; if(this.frm.doc.docstatus===1) { @@ -17,12 +48,12 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ frappe.set_route("query-report", "Stock Ledger"); }, "icon-bar-chart"); } - + }, show_general_ledger: function() { var me = this; - if(this.frm.doc.docstatus===1 && cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { + if(this.frm.doc.docstatus===1 && cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { cur_frm.appframe.add_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: me.frm.doc.name, @@ -46,4 +77,4 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ } refresh_field(this.frm.cscript.fname); } -}); \ No newline at end of file +}); diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index 1f404db39a8..b57b765ad6e 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -67,5 +67,11 @@ $.extend(erpnext.queries, { employee: function() { return { query: "erpnext.controllers.queries.employee_query" } + }, + + warehouse: function(doc) { + return { + filters: [["Warehouse", "company", "in", ["", doc.company]]] + } } }); diff --git a/erpnext/public/js/transaction.js b/erpnext/public/js/transaction.js index ea576d5ae07..a4b1abbb6a6 100644 --- a/erpnext/public/js/transaction.js +++ b/erpnext/public/js/transaction.js @@ -7,6 +7,8 @@ frappe.require("assets/erpnext/js/controllers/stock_controller.js"); erpnext.TransactionController = erpnext.stock.StockController.extend({ onload: function() { var me = this; + this._super(); + if(this.frm.doc.__islocal) { var today = get_today(), currency = frappe.defaults.get_user_default("currency"); From 4864c9e96ad5caf92fc4f9df9f16249ad6fc3ed3 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 24 Jun 2014 18:53:17 +0530 Subject: [PATCH 27/27] Fixed gantt chart query for Task --- erpnext/projects/doctype/task/task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 7fbaa45138a..6b0b237a402 100644 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -52,7 +52,7 @@ def get_events(start, end, filters=None): frappe.msgprint(_("No Permission"), raise_exception=1) conditions = build_match_conditions("Task") - conditions and (" and " + conditions) or "" + conditions = conditions and (" and " + conditions) or "" if filters: filters = json.loads(filters)