From fdfab03c11dac62073987c0de495e423c0ecfa21 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Wed, 12 Aug 2020 20:18:31 +0530 Subject: [PATCH 01/15] feat: add condition field in pricing rule --- .../doctype/pricing_rule/pricing_rule.json | 11 ++++++++++- .../doctype/pricing_rule/pricing_rule.py | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 29d83783d07..87084c7560b 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:title", @@ -12,6 +13,7 @@ "apply_on", "price_or_product_discount", "warehouse", + "condition", "column_break_7", "items", "item_groups", @@ -550,11 +552,18 @@ "fieldtype": "Link", "label": "Promotional Scheme", "options": "Promotional Scheme" + }, + { + "description": "Simple Python Expression, Example: territory != 'All Territories'", + "fieldname": "condition", + "fieldtype": "Code", + "label": "Condition" } ], "icon": "fa fa-gift", "idx": 1, - "modified": "2019-12-18 17:29:22.957077", + "links": [], + "modified": "2020-08-12 20:15:32.279592", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index cff7d5ba220..d88ce222d67 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -7,9 +7,10 @@ from __future__ import unicode_literals import frappe import json import copy +import re + from frappe import throw, _ from frappe.utils import flt, cint, getdate - from frappe.model.document import Document from six import string_types @@ -31,6 +32,7 @@ class PricingRule(Document): self.validate_max_discount() self.validate_price_list_with_currency() self.validate_dates() + validate_condition(self) if not self.margin_type: self.margin_rate_or_amount = 0.0 @@ -141,6 +143,16 @@ class PricingRule(Document): if self.valid_from and self.valid_upto and getdate(self.valid_from) > getdate(self.valid_upto): frappe.throw(_("Valid from date must be less than valid upto date")) +def validate_condition(doc, args=None): + if doc.condition and ("=" in doc.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", doc.condition): + frappe.throw(_("Invalid condition in Pricing Rule: {0}").format(doc.name), frappe.ValidationError) + else: + try: + return frappe.safe_eval(doc.condition, None, args) if bool(args) else True + except Exception as e: + frappe.throw(doc.name + " Pricing Rule 'Condition' field error:
" + str(e).capitalize() ) + return False + #-------------------------------------------------------------------------------- @frappe.whitelist() @@ -252,6 +264,8 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if pricing_rule.get('suggestion'): continue + if not validate_condition(pricing_rule, args): continue + item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0) item_details.price_or_product_discount = pricing_rule.get("price_or_product_discount") From cbde04d3daf0245a2db0d99cfb64a413aaa25b25 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 13 Aug 2020 11:47:41 +0530 Subject: [PATCH 02/15] fix: Pass doc instead of args --- .../accounts/doctype/pricing_rule/pricing_rule.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index d88ce222d67..98abadbff61 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -32,7 +32,6 @@ class PricingRule(Document): self.validate_max_discount() self.validate_price_list_with_currency() self.validate_dates() - validate_condition(self) if not self.margin_type: self.margin_rate_or_amount = 0.0 @@ -143,16 +142,17 @@ class PricingRule(Document): if self.valid_from and self.valid_upto and getdate(self.valid_from) > getdate(self.valid_upto): frappe.throw(_("Valid from date must be less than valid upto date")) -def validate_condition(doc, args=None): - if doc.condition and ("=" in doc.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", doc.condition): - frappe.throw(_("Invalid condition in Pricing Rule: {0}").format(doc.name), frappe.ValidationError) +def validate_condition(pricing_rule, doc=None): + if pricing_rule.condition and ("=" in pricing_rule.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", pricing_rule.condition): + frappe.throw(_("Invalid condition in Pricing Rule: {0}").format(pricing_rule.name), frappe.ValidationError) else: try: - return frappe.safe_eval(doc.condition, None, args) if bool(args) else True + doc = doc.as_dict() + return frappe.safe_eval(pricing_rule.condition, None, doc) except Exception as e: frappe.throw(doc.name + " Pricing Rule 'Condition' field error:
" + str(e).capitalize() ) return False - + return True #-------------------------------------------------------------------------------- @frappe.whitelist() @@ -264,7 +264,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if pricing_rule.get('suggestion'): continue - if not validate_condition(pricing_rule, args): continue + if not validate_condition(pricing_rule, doc): continue item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0) item_details.price_or_product_discount = pricing_rule.get("price_or_product_discount") From ca898c6da3d63f78883e1b0a6cbf090076e51617 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 13 Aug 2020 11:54:18 +0530 Subject: [PATCH 03/15] fix: Condition fix --- 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 98abadbff61..a4a95e1c206 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -145,7 +145,7 @@ class PricingRule(Document): def validate_condition(pricing_rule, doc=None): if pricing_rule.condition and ("=" in pricing_rule.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", pricing_rule.condition): frappe.throw(_("Invalid condition in Pricing Rule: {0}").format(pricing_rule.name), frappe.ValidationError) - else: + elif pricing_rule.condition: try: doc = doc.as_dict() return frappe.safe_eval(pricing_rule.condition, None, doc) From 487aa30aef20b3689d7853ff73cda59d7d381c7a Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Thu, 13 Aug 2020 12:13:08 +0530 Subject: [PATCH 04/15] fix: eval fail message fix --- 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 a4a95e1c206..53f900fbf26 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -150,7 +150,7 @@ def validate_condition(pricing_rule, doc=None): doc = doc.as_dict() return frappe.safe_eval(pricing_rule.condition, None, doc) except Exception as e: - frappe.throw(doc.name + " Pricing Rule 'Condition' field error:
" + str(e).capitalize() ) + frappe.throw(" Pricing Rule - " + pricing_rule.name + " - 'Condition' field error:
" + str(e).capitalize() ) return False return True #-------------------------------------------------------------------------------- From a25760046fb4ad5d91bb7ed5ed028b3eea3222de Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Thu, 13 Aug 2020 13:22:58 +0530 Subject: [PATCH 05/15] fix: condition syntax validation readded, fetch item details if condition not met ignoring rule --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 53f900fbf26..e915618f4c6 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -32,6 +32,7 @@ class PricingRule(Document): self.validate_max_discount() self.validate_price_list_with_currency() self.validate_dates() + validate_condition(self) if not self.margin_type: self.margin_rate_or_amount = 0.0 @@ -144,13 +145,13 @@ class PricingRule(Document): def validate_condition(pricing_rule, doc=None): if pricing_rule.condition and ("=" in pricing_rule.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", pricing_rule.condition): - frappe.throw(_("Invalid condition in Pricing Rule: {0}").format(pricing_rule.name), frappe.ValidationError) - elif pricing_rule.condition: + frappe.throw(_("Invalid condition in Pricing Rule - {0}").format(pricing_rule.name), frappe.ValidationError) + elif doc: try: - doc = doc.as_dict() - return frappe.safe_eval(pricing_rule.condition, None, doc) + return frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()) except Exception as e: - frappe.throw(" Pricing Rule - " + pricing_rule.name + " - 'Condition' field error:
" + str(e).capitalize() ) + frappe.msgprint(_("Pricing Rule - " + pricing_rule.name + " - condition field error:
" + \ + str(e).capitalize() + "

Ignoring Pricing Rule"), indicator="orange", title=_("Warning")) return False return True #-------------------------------------------------------------------------------- From d9accededa0615598adcd80886d5cec9deb80d5e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 14 Aug 2020 22:36:01 +0530 Subject: [PATCH 06/15] fix: filter pricing rules based on condition --- .../doctype/pricing_rule/pricing_rule.py | 19 +++++-------------- .../accounts/doctype/pricing_rule/utils.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index e915618f4c6..611c2fca69c 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -32,7 +32,7 @@ class PricingRule(Document): self.validate_max_discount() self.validate_price_list_with_currency() self.validate_dates() - validate_condition(self) + self.validate_condition() if not self.margin_type: self.margin_rate_or_amount = 0.0 @@ -143,17 +143,10 @@ class PricingRule(Document): if self.valid_from and self.valid_upto and getdate(self.valid_from) > getdate(self.valid_upto): frappe.throw(_("Valid from date must be less than valid upto date")) -def validate_condition(pricing_rule, doc=None): - if pricing_rule.condition and ("=" in pricing_rule.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", pricing_rule.condition): - frappe.throw(_("Invalid condition in Pricing Rule - {0}").format(pricing_rule.name), frappe.ValidationError) - elif doc: - try: - return frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()) - except Exception as e: - frappe.msgprint(_("Pricing Rule - " + pricing_rule.name + " - condition field error:
" + \ - str(e).capitalize() + "

Ignoring Pricing Rule"), indicator="orange", title=_("Warning")) - return False - return True + def validate_condition(self): + if self.condition and ("=" in self.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", self.condition): + frappe.throw(_("Invalid condition in Pricing Rule - {0}").format(pricing_rule.name), frappe.ValidationError) + #-------------------------------------------------------------------------------- @frappe.whitelist() @@ -265,8 +258,6 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if pricing_rule.get('suggestion'): continue - if not validate_condition(pricing_rule, doc): continue - item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0) item_details.price_or_product_discount = pricing_rule.get("price_or_product_discount") diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 3fd316f75e6..59903a70b6e 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -37,6 +37,8 @@ def get_pricing_rules(args, doc=None): rules = [] + pricing_rules = filter_pricing_rule_based_on_condition(pricing_rules, doc) + if not pricing_rules: return [] if apply_multiple_pricing_rules(pricing_rules): @@ -51,6 +53,22 @@ def get_pricing_rules(args, doc=None): return rules +def filter_pricing_rule_based_on_condition(pricing_rules, doc=None): + filtered_pricing_rules = [] + if doc: + for pricing_rule in pricing_rules: + if pricing_rule.condition: + try: + if frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()): + filtered_pricing_rules.append(pricing_rule) + except Exception as e: + frappe.msgprint(_("Pricing Rule - " + pricing_rule.name + " - condition field error:
" + \ + str(e).capitalize() + "

Ignoring Pricing Rule"), indicator="orange", title=_("Warning")) + else: + filtered_pricing_rules.append(pricing_rule) + + return filtered_pricing_rules + def _get_pricing_rules(apply_on, args, values): apply_on_field = frappe.scrub(apply_on) From 1067f7ed3ea1b7c402c8a8dc9867da1ce0fd1485 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 17 Aug 2020 23:47:58 +0530 Subject: [PATCH 07/15] fix: Price rule filtering in case of no doc --- erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 2 +- erpnext/accounts/doctype/pricing_rule/utils.py | 2 ++ 2 files 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 611c2fca69c..5a930061841 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -145,7 +145,7 @@ class PricingRule(Document): def validate_condition(self): if self.condition and ("=" in self.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", self.condition): - frappe.throw(_("Invalid condition in Pricing Rule - {0}").format(pricing_rule.name), frappe.ValidationError) + frappe.throw(_("Invalid condition expression")) #-------------------------------------------------------------------------------- diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 59903a70b6e..fab533ebd2f 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -66,6 +66,8 @@ def filter_pricing_rule_based_on_condition(pricing_rules, doc=None): str(e).capitalize() + "

Ignoring Pricing Rule"), indicator="orange", title=_("Warning")) else: filtered_pricing_rules.append(pricing_rule) + else: + filtered_pricing_rules = pricing_rules return filtered_pricing_rules From 7d0bb25aca00b7758d1faa674c268658e4f1eb48 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Wed, 26 Aug 2020 12:22:31 +0530 Subject: [PATCH 08/15] fix: dont show message on condition syntax error --- erpnext/accounts/doctype/pricing_rule/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index fab533ebd2f..482abb81a32 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -61,9 +61,8 @@ def filter_pricing_rule_based_on_condition(pricing_rules, doc=None): try: if frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()): filtered_pricing_rules.append(pricing_rule) - except Exception as e: - frappe.msgprint(_("Pricing Rule - " + pricing_rule.name + " - condition field error:
" + \ - str(e).capitalize() + "

Ignoring Pricing Rule"), indicator="orange", title=_("Warning")) + except: + pass else: filtered_pricing_rules.append(pricing_rule) else: From dc9af639a7bfffe032c6650206875ec5f5c13443 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Wed, 26 Aug 2020 12:26:15 +0530 Subject: [PATCH 09/15] fix: move condition field --- erpnext/accounts/doctype/pricing_rule/pricing_rule.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 87084c7560b..c681c897fc6 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -13,7 +13,6 @@ "apply_on", "price_or_product_discount", "warehouse", - "condition", "column_break_7", "items", "item_groups", @@ -73,6 +72,7 @@ "section_break_13", "threshold_percentage", "priority", + "condition", "column_break_66", "apply_multiple_pricing_rules", "apply_discount_on_rate", @@ -563,7 +563,7 @@ "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2020-08-12 20:15:32.279592", + "modified": "2020-08-26 12:24:44.740734", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", From 71c24c8a90a5112bffd558befa0b6514839cc257 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Thu, 1 Oct 2020 14:54:11 +0530 Subject: [PATCH 10/15] feat: add tests --- .../doctype/pricing_rule/test_pricing_rule.py | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 2bf0b725635..3555ca895f1 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -429,7 +429,34 @@ class TestPricingRule(unittest.TestCase): details = get_item_details(args) self.assertTrue(details) - + + def test_pricing_rule_for_condition(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule") + + make_pricing_rule(selling=1, margin_type="Percentage", \ + condition="customer=='_Test Customer 1' and is_return==0", discount_percentage=10) + + # Incorrect Customer and Correct is_return value + si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 2", is_return=0) + si.items[0].price_list_rate = 1000 + si.submit() + item = si.items[0] + self.assertEquals(item.rate, 100) + + # Correct Customer and Incorrect is_return value + si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=1, qty=-1) + si.items[0].price_list_rate = 1000 + si.submit() + item = si.items[0] + self.assertEquals(item.rate, 100) + + # Correct Customer and correct is_return value + si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0) + si.items[0].price_list_rate = 1000 + si.submit() + item = si.items[0] + self.assertEquals(item.rate, 900) + def make_pricing_rule(**args): args = frappe._dict(args) @@ -448,7 +475,8 @@ def make_pricing_rule(**args): "discount_percentage": args.discount_percentage or 0.0, "rate": args.rate or 0.0, "margin_type": args.margin_type, - "margin_rate_or_amount": args.margin_rate_or_amount or 0.0 + "margin_rate_or_amount": args.margin_rate_or_amount or 0.0, + "condition": args.condition or '' }) apply_on = doc.apply_on.replace(' ', '_').lower() From a2aae5644e0605416c8209603e263909e39ba003 Mon Sep 17 00:00:00 2001 From: michellealva Date: Tue, 6 Oct 2020 15:02:01 +0530 Subject: [PATCH 11/15] fix(Budget): Change options in select field --- erpnext/accounts/doctype/budget/budget.json | 978 ++++---------------- 1 file changed, 194 insertions(+), 784 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.json b/erpnext/accounts/doctype/budget/budget.json index 50ad2e5ebce..b22ad3b5f9a 100644 --- a/erpnext/accounts/doctype/budget/budget.json +++ b/erpnext/accounts/doctype/budget/budget.json @@ -1,814 +1,224 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "beta": 0, - "creation": "2016-05-16 11:42:29.632528", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "allow_import": 1, + "creation": "2016-05-16 11:42:29.632528", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "budget_against", + "company", + "cost_center", + "project", + "fiscal_year", + "column_break_3", + "monthly_distribution", + "amended_from", + "section_break_6", + "applicable_on_material_request", + "action_if_annual_budget_exceeded_on_mr", + "action_if_accumulated_monthly_budget_exceeded_on_mr", + "column_break_13", + "applicable_on_purchase_order", + "action_if_annual_budget_exceeded_on_po", + "action_if_accumulated_monthly_budget_exceeded_on_po", + "section_break_16", + "applicable_on_booking_actual_expenses", + "action_if_annual_budget_exceeded", + "action_if_accumulated_monthly_budget_exceeded", + "section_break_21", + "accounts" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Cost Center", - "fetch_if_empty": 0, - "fieldname": "budget_against", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Budget Against", - "length": 0, - "no_copy": 0, - "options": "\nCost Center\nProject", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Cost Center", + "fieldname": "budget_against", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Budget Against", + "options": "Cost Center\nProject", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.budget_against == 'Cost Center'", - "fetch_if_empty": 0, - "fieldname": "cost_center", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Cost Center", - "length": 0, - "no_copy": 0, - "options": "Cost Center", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.budget_against == 'Cost Center'", + "fieldname": "cost_center", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Cost Center", + "options": "Cost Center" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.budget_against == 'Project'", - "fetch_if_empty": 0, - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Project", - "length": 0, - "no_copy": 0, - "options": "Project", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.budget_against == 'Project'", + "fieldname": "project", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Project", + "options": "Project" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "fiscal_year", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Fiscal Year", - "length": 0, - "no_copy": 0, - "options": "Fiscal Year", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "fiscal_year", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Fiscal Year", + "options": "Fiscal Year", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:in_list([\"Stop\", \"Warn\"], doc.action_if_accumulated_monthly_budget_exceeded_on_po || doc.action_if_accumulated_monthly_budget_exceeded_on_mr || doc.action_if_accumulated_monthly_budget_exceeded_on_actual)", - "fetch_if_empty": 0, - "fieldname": "monthly_distribution", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Monthly Distribution", - "length": 0, - "no_copy": 0, - "options": "Monthly Distribution", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:in_list([\"Stop\", \"Warn\"], doc.action_if_accumulated_monthly_budget_exceeded_on_po || doc.action_if_accumulated_monthly_budget_exceeded_on_mr || doc.action_if_accumulated_monthly_budget_exceeded_on_actual)", + "fieldname": "monthly_distribution", + "fieldtype": "Link", + "label": "Monthly Distribution", + "options": "Monthly Distribution" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Budget", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Budget", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Control Action", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Control Action" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "applicable_on_material_request", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Applicable on Material Request", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "applicable_on_material_request", + "fieldtype": "Check", + "label": "Applicable on Material Request" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Stop", - "depends_on": "eval:doc.applicable_on_material_request == 1", - "fetch_if_empty": 0, - "fieldname": "action_if_annual_budget_exceeded_on_mr", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Action if Annual Budget Exceeded on MR", - "length": 0, - "no_copy": 0, - "options": "\nStop\nWarn\nIgnore", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "Stop", + "depends_on": "eval:doc.applicable_on_material_request == 1", + "fieldname": "action_if_annual_budget_exceeded_on_mr", + "fieldtype": "Select", + "label": "Action if Annual Budget Exceeded on MR", + "options": "\nStop\nWarn\nIgnore" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Warn", - "depends_on": "eval:doc.applicable_on_material_request == 1", - "fetch_if_empty": 0, - "fieldname": "action_if_accumulated_monthly_budget_exceeded_on_mr", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Action if Accumulated Monthly Budget Exceeded on MR", - "length": 0, - "no_copy": 0, - "options": "\nStop\nWarn\nIgnore", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "Warn", + "depends_on": "eval:doc.applicable_on_material_request == 1", + "fieldname": "action_if_accumulated_monthly_budget_exceeded_on_mr", + "fieldtype": "Select", + "label": "Action if Accumulated Monthly Budget Exceeded on MR", + "options": "\nStop\nWarn\nIgnore" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_13", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_13", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "applicable_on_purchase_order", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Applicable on Purchase Order", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "applicable_on_purchase_order", + "fieldtype": "Check", + "label": "Applicable on Purchase Order" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Stop", - "depends_on": "eval:doc.applicable_on_purchase_order == 1", - "fetch_if_empty": 0, - "fieldname": "action_if_annual_budget_exceeded_on_po", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Action if Annual Budget Exceeded on PO", - "length": 0, - "no_copy": 0, - "options": "\nStop\nWarn\nIgnore", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "Stop", + "depends_on": "eval:doc.applicable_on_purchase_order == 1", + "fieldname": "action_if_annual_budget_exceeded_on_po", + "fieldtype": "Select", + "label": "Action if Annual Budget Exceeded on PO", + "options": "\nStop\nWarn\nIgnore" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Warn", - "depends_on": "eval:doc.applicable_on_purchase_order == 1", - "fetch_if_empty": 0, - "fieldname": "action_if_accumulated_monthly_budget_exceeded_on_po", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Action if Accumulated Monthly Budget Exceeded on PO", - "length": 0, - "no_copy": 0, - "options": "\nStop\nWarn\nIgnore", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "Warn", + "depends_on": "eval:doc.applicable_on_purchase_order == 1", + "fieldname": "action_if_accumulated_monthly_budget_exceeded_on_po", + "fieldtype": "Select", + "label": "Action if Accumulated Monthly Budget Exceeded on PO", + "options": "\nStop\nWarn\nIgnore" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_16", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_16", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "applicable_on_booking_actual_expenses", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Applicable on booking actual expenses", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "applicable_on_booking_actual_expenses", + "fieldtype": "Check", + "label": "Applicable on booking actual expenses" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Stop", - "depends_on": "eval:doc.applicable_on_booking_actual_expenses == 1", - "fetch_if_empty": 0, - "fieldname": "action_if_annual_budget_exceeded", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Action if Annual Budget Exceeded on Actual", - "length": 0, - "no_copy": 0, - "options": "\nStop\nWarn\nIgnore", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "Stop", + "depends_on": "eval:doc.applicable_on_booking_actual_expenses == 1", + "fieldname": "action_if_annual_budget_exceeded", + "fieldtype": "Select", + "label": "Action if Annual Budget Exceeded on Actual", + "options": "\nStop\nWarn\nIgnore" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Warn", - "depends_on": "eval:doc.applicable_on_booking_actual_expenses == 1", - "fetch_if_empty": 0, - "fieldname": "action_if_accumulated_monthly_budget_exceeded", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Action if Accumulated Monthly Budget Exceeded on Actual", - "length": 0, - "no_copy": 0, - "options": "\nStop\nWarn\nIgnore", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "Warn", + "depends_on": "eval:doc.applicable_on_booking_actual_expenses == 1", + "fieldname": "action_if_accumulated_monthly_budget_exceeded", + "fieldtype": "Select", + "label": "Action if Accumulated Monthly Budget Exceeded on Actual", + "options": "\nStop\nWarn\nIgnore" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_21", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_21", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "accounts", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Budget Accounts", - "length": 0, - "no_copy": 0, - "options": "Budget Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Budget Accounts", + "options": "Budget Account", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-22 12:06:02.323099", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Budget", - "name_case": "", - "owner": "Administrator", + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2020-10-06 14:59:18.193848", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Budget", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From 2e425efae85b324247ed6a52c2dd2a1ec08bee99 Mon Sep 17 00:00:00 2001 From: michellealva Date: Tue, 6 Oct 2020 15:15:29 +0530 Subject: [PATCH 12/15] fix: blank option in the beginning --- erpnext/accounts/doctype/budget/budget.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.json b/erpnext/accounts/doctype/budget/budget.json index b22ad3b5f9a..fc4dd200ea2 100644 --- a/erpnext/accounts/doctype/budget/budget.json +++ b/erpnext/accounts/doctype/budget/budget.json @@ -37,7 +37,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Budget Against", - "options": "Cost Center\nProject", + "options": "\nCost Center\nProject", "reqd": 1 }, { @@ -195,7 +195,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-10-06 14:59:18.193848", + "modified": "2020-10-06 15:13:54.055854", "modified_by": "Administrator", "module": "Accounts", "name": "Budget", From 3525ddabc5007940ba6f5154144bb6afbe799c3e Mon Sep 17 00:00:00 2001 From: michellealva Date: Wed, 7 Oct 2020 19:02:19 +0530 Subject: [PATCH 13/15] fix: Add links in Stock desk page --- erpnext/stock/desk_page/stock/stock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 2fba5fa8044..390fcd91e3b 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -3,7 +3,7 @@ { "hidden": 0, "label": "Items and Pricing", - "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Alternative\",\n \"name\": \"Item Alternative\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Manufacturer\",\n \"name\": \"Item Manufacturer\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Alternative\",\n \"name\": \"Item Alternative\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Manufacturer\",\n \"name\": \"Item Manufacturer\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Customs Tariff Number\",\n \"name\": \"Customs Tariff Number\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -18,7 +18,7 @@ { "hidden": 0, "label": "Settings", - "links": "[\n {\n \"label\": \"Stock Settings\",\n \"name\": \"Stock Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Attribute\",\n \"name\": \"Item Attribute\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"label\": \"Stock Settings\",\n \"name\": \"Stock Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Attribute\",\n \"name\": \"Item Attribute\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"UOM Conversion Factor\",\n \"name\": \"UOM Conversion Factor\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -58,7 +58,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-08-11 17:29:32.626067", + "modified": "2020-10-07 18:40:17.130207", "modified_by": "Administrator", "module": "Stock", "name": "Stock", From 38aee6e3cb0ace3bbbcc10c0ff8e973b90b821a8 Mon Sep 17 00:00:00 2001 From: michellealva Date: Thu, 8 Oct 2020 10:24:11 +0530 Subject: [PATCH 14/15] fix(Selling): Add missing reports on desk --- erpnext/selling/desk_page/selling/selling.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index 4c09ee94e04..b15df980b12 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -23,7 +23,7 @@ { "hidden": 0, "label": "Other Reports", - "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Territory Target Variance Based On Item Group\",\n \"name\": \"Territory Target Variance Based On Item Group\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person Target Variance Based On Item Group\",\n \"name\": \"Sales Person Target Variance Based On Item Group\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Partner Target Variance Based On Item Group\",\n \"name\": \"Sales Partner Target Variance based on Item Group\",\n \"type\": \"report\"\n }\n \n]" } ], "category": "Modules", @@ -44,7 +44,7 @@ "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-08-15 10:12:53.131621", + "modified": "2020-10-08 10:23:09.984377", "modified_by": "Administrator", "module": "Selling", "name": "Selling", From 22b4c86a388e2777e44c0facfe551b296e16c8f1 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 8 Oct 2020 11:23:21 +0530 Subject: [PATCH 15/15] fix: cannot merge pos invoices with inclusive tax (#23541) * fix: cannot merge pos invoices with inclusive tax * fix: item rate calculation --- .../doctype/pos_invoice/test_pos_invoice.py | 66 +++++++++++++++++++ .../pos_invoice_merge_log.py | 21 ++++-- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 514a2acd8c7..1a5920d8abb 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -285,6 +285,71 @@ class TestPOSInvoice(unittest.TestCase): after_redeem_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program) self.assertEqual(after_redeem_lp_details.loyalty_points, 9) + + def test_merging_into_sales_invoice_with_discount(self): + from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile + from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices + + frappe.db.sql("delete from `tabPOS Invoice`") + test_user, pos_profile = init_user_and_profile() + pos_inv = create_pos_invoice(rate=300, additional_discount_percentage=10, do_not_submit=1) + pos_inv.append('payments', { + 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300 + }) + pos_inv.submit() + + pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) + pos_inv2.append('payments', { + 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200 + }) + pos_inv2.submit() + + merge_pos_invoices() + + pos_inv.load_from_db() + sales_invoice = frappe.get_doc("Sales Invoice", pos_inv.consolidated_invoice) + self.assertEqual(sales_invoice.grand_total, 3500) + + def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self): + from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile + from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices + + frappe.db.sql("delete from `tabPOS Invoice`") + test_user, pos_profile = init_user_and_profile() + pos_inv = create_pos_invoice(rate=300, do_not_submit=1) + pos_inv.append('payments', { + 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300 + }) + pos_inv.append('taxes', { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Service Tax", + "rate": 14, + 'included_in_print_rate': 1 + }) + pos_inv.submit() + + pos_inv2 = create_pos_invoice(rate=300, qty=2, do_not_submit=1) + pos_inv2.additional_discount_percentage = 10 + pos_inv2.append('payments', { + 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 540 + }) + pos_inv2.append('taxes', { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Service Tax", + "rate": 14, + 'included_in_print_rate': 1 + }) + pos_inv2.submit() + + merge_pos_invoices() + + pos_inv.load_from_db() + sales_invoice = frappe.get_doc("Sales Invoice", pos_inv.consolidated_invoice) + self.assertEqual(sales_invoice.rounded_total, 840) def create_pos_invoice(**args): args = frappe._dict(args) @@ -294,6 +359,7 @@ def create_pos_invoice(**args): pos_profile.save() pos_inv = frappe.new_doc("POS Invoice") + pos_inv.update(args) pos_inv.update_stock = 1 pos_inv.is_pos = 1 pos_inv.pos_profile = args.pos_profile or pos_profile.name diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 11b9d2509e3..3a229b1787b 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -96,17 +96,28 @@ class POSInvoiceMergeLog(Document): loyalty_amount_sum += doc.loyalty_amount for item in doc.get('items'): - items.append(item) + found = False + for i in items: + if (i.item_code == item.item_code and not i.serial_no and not i.batch_no and + i.uom == item.uom and i.net_rate == item.net_rate): + found = True + i.qty = i.qty + item.qty + if not found: + item.rate = item.net_rate + items.append(item) for tax in doc.get('taxes'): found = False for t in taxes: - if t.account_head == tax.account_head and t.cost_center == tax.cost_center and t.rate == tax.rate: - t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount) - t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount) + if t.account_head == tax.account_head and t.cost_center == tax.cost_center: + t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount_after_discount_amount) + t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount_after_discount_amount) found = True if not found: tax.charge_type = 'Actual' + tax.included_in_print_rate = 0 + tax.tax_amount = tax.tax_amount_after_discount_amount + tax.base_tax_amount = tax.base_tax_amount_after_discount_amount taxes.append(tax) for payment in doc.get('payments'): @@ -127,6 +138,8 @@ class POSInvoiceMergeLog(Document): invoice.set('items', items) invoice.set('payments', payments) invoice.set('taxes', taxes) + invoice.additional_discount_percentage = 0 + invoice.discount_amount = 0.0 return invoice