From a248dfb9a5f58fae3219d9bbb7bcc1fa8e5d4bfe Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 16 Jul 2020 17:27:26 +0530 Subject: [PATCH 01/12] fix: multiple pricing rules are not working on selling side --- .../doctype/pricing_rule/pricing_rule.py | 38 ++++++++++--------- .../accounts/doctype/pricing_rule/utils.py | 15 ++++++++ erpnext/controllers/accounts_controller.py | 17 +++++++++ erpnext/controllers/taxes_and_totals.py | 11 ++++-- 4 files changed, 59 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 454776e4e74..149c47673c6 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -60,6 +60,15 @@ class PricingRule(Document): if self.price_or_product_discount == 'Price' and not self.rate_or_discount: throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError) + if self.apply_discount_on_rate: + if not self.priority: + throw(_("As the field {0} is enabled, the field {1} is mandatory.") + .format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority"))) + + if self.priority and cint(self.priority) == 1: + throw(_("As the field {0} is enabled, the value of the field {1} should be more than 1.") + .format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority"))) + 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")) @@ -226,12 +235,11 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa item_details = frappe._dict({ "doctype": args.doctype, + "has_margin": False, "name": args.name, "parent": args.parent, "parenttype": args.parenttype, - "child_docname": args.get('child_docname'), - "discount_percentage_on_rate": [], - "discount_amount_on_rate": [] + "child_docname": args.get('child_docname') }) if args.ignore_pricing_rule or not args.item_code: @@ -279,6 +287,10 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa else: get_product_discount_rule(pricing_rule, item_details, args, doc) + if not item_details.get("has_margin"): + item_details.margin_type = None + item_details.margin_rate_or_amount = 0.0 + item_details.has_pricing_rule = 1 item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules]) @@ -330,13 +342,11 @@ def get_pricing_rule_details(args, pricing_rule): def apply_price_discount_rule(pricing_rule, item_details, args): item_details.pricing_rule_for = pricing_rule.rate_or_discount - if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency) + if ((pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == args.currency) or (pricing_rule.margin_type == 'Percentage')): item_details.margin_type = pricing_rule.margin_type item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount - else: - item_details.margin_type = None - item_details.margin_rate_or_amount = 0.0 + item_details.has_margin = True if pricing_rule.rate_or_discount == 'Rate': pricing_rule_rate = 0.0 @@ -351,9 +361,9 @@ def apply_price_discount_rule(pricing_rule, item_details, args): if pricing_rule.rate_or_discount != apply_on: continue field = frappe.scrub(apply_on) - if pricing_rule.apply_discount_on_rate: - discount_field = "{0}_on_rate".format(field) - item_details[discount_field].append(pricing_rule.get(field, 0)) + if pricing_rule.apply_discount_on_rate and item_details.get("discount_percentage"): + # Apply discount on discounted rate + item_details[field] += ((100 - item_details[field]) * (pricing_rule.get(field, 0) / 100)) else: if field not in item_details: item_details.setdefault(field, 0) @@ -361,14 +371,6 @@ def apply_price_discount_rule(pricing_rule, item_details, args): item_details[field] += (pricing_rule.get(field, 0) if pricing_rule else args.get(field, 0)) -def set_discount_amount(rate, item_details): - for field in ['discount_percentage_on_rate', 'discount_amount_on_rate']: - for d in item_details.get(field): - dis_amount = (rate * d / 100 - if field == 'discount_percentage_on_rate' else d) - rate -= dis_amount - item_details.rate = rate - def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None): from erpnext.accounts.doctype.pricing_rule.utils import (get_applied_pricing_rules, get_pricing_rule_items) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 25840d4fb79..1a58b1dba1b 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -42,6 +42,7 @@ def get_pricing_rules(args, doc=None): if not pricing_rules: return [] if apply_multiple_pricing_rules(pricing_rules): + pricing_rules = sorted_by_priority(pricing_rules) for pricing_rule in pricing_rules: pricing_rule = filter_pricing_rules(args, pricing_rule, doc) if pricing_rule: @@ -53,6 +54,20 @@ def get_pricing_rules(args, doc=None): return rules +def sorted_by_priority(pricing_rules): + # If more than one pricing rules, then sort by priority + pricing_rules_list = [] + pricing_rule_dict = {} + for pricing_rule in pricing_rules: + if not pricing_rule.get("priority"): continue + + pricing_rule_dict.setdefault(cint(pricing_rule.get("priority")), []).append(pricing_rule) + + for key in sorted(pricing_rule_dict): + pricing_rules_list.append(pricing_rule_dict.get(key)) + + return pricing_rules_list or pricing_rules + def filter_pricing_rule_based_on_condition(pricing_rules, doc=None): filtered_pricing_rules = [] if doc: diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 983cfa8c152..6108a614ca5 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -263,6 +263,7 @@ class AccountsController(TransactionBase): if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"): parent_dict.update({"customer": parent_dict.get("party_name")}) + self.pricing_rules = [] for item in self.get("items"): if item.get("item_code"): args = parent_dict.copy() @@ -301,6 +302,7 @@ class AccountsController(TransactionBase): if ret.get("pricing_rules"): self.apply_pricing_rule_on_items(item, ret) + self.set_pricing_rule_details(item, ret) if self.doctype == "Purchase Invoice": self.set_expense_account(for_validate) @@ -322,6 +324,9 @@ class AccountsController(TransactionBase): if item.get('discount_amount'): item.rate = item.price_list_rate - item.discount_amount + if item.get("apply_discount_on_discounted_rate") and pricing_rule_args.get("rate"): + item.rate = pricing_rule_args.get("rate") + elif pricing_rule_args.get('free_item_data'): apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data')) @@ -335,6 +340,18 @@ class AccountsController(TransactionBase): frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}") .format(item.idx, frappe.bold(title), frappe.bold(item.item_code))) + def set_pricing_rule_details(self, item_row, args): + pricing_rules = get_applied_pricing_rules(args.get("pricing_rules")) + if not pricing_rules: return + + for pricing_rule in pricing_rules: + self.append("pricing_rules", { + "pricing_rule": pricing_rule, + "item_code": item_row.item_code, + "child_docname": item_row.name, + "rule_applied": True + }) + def set_taxes(self): if not self.meta.get_field("taxes"): return diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 92cfdb7f1a1..81d07c1327e 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -608,16 +608,19 @@ class calculate_taxes_and_totals(object): base_rate_with_margin = 0.0 if item.price_list_rate: if item.pricing_rules and not self.doc.ignore_pricing_rule: + has_margin = False for d in get_applied_pricing_rules(item.pricing_rules): pricing_rule = frappe.get_cached_doc('Pricing Rule', d) - if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\ + if (pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == self.doc.currency)\ or (pricing_rule.margin_type == 'Percentage'): item.margin_type = pricing_rule.margin_type item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount - else: - item.margin_type = None - item.margin_rate_or_amount = 0.0 + has_margin = True + + if not has_margin: + item.margin_type = None + item.margin_rate_or_amount = 0.0 if item.margin_type and item.margin_rate_or_amount: margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100 From 94a630c2551e1b699c2dc5be7b8f966e3ba75bcd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Oct 2020 17:15:14 +0530 Subject: [PATCH 02/12] fix: Do not allow Company as accounting dimension --- .../doctype/accounting_dimension/accounting_dimension.js | 2 +- .../doctype/accounting_dimension/accounting_dimension.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index 3c12f85f938..9a6c3893393 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -7,7 +7,7 @@ frappe.ui.form.on('Accounting Dimension', { frm.set_query('document_type', () => { let invalid_doctypes = frappe.model.core_doctypes_list; invalid_doctypes.push('Accounting Dimension', 'Project', - 'Cost Center', 'Accounting Dimension Detail'); + 'Cost Center', 'Accounting Dimension Detail', 'Company'); return { filters: { diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 8834385135f..f888d9e038a 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -19,7 +19,7 @@ class AccountingDimension(Document): def validate(self): if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project', - 'Cost Center', 'Accounting Dimension Detail') : + 'Cost Center', 'Accounting Dimension Detail', 'Company') : msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type) frappe.throw(msg) From 0fa741b267631d7fbc16fae098905eebb6cb5b80 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 28 Oct 2020 16:51:42 +0530 Subject: [PATCH 03/12] added test cases --- .../doctype/pricing_rule/pricing_rule.json | 6 +-- .../doctype/pricing_rule/test_pricing_rule.py | 48 +++++++++++++++---- .../accounts/doctype/pricing_rule/utils.py | 14 +++--- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index c681c897fc6..cc8ed4bc491 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -504,10 +504,10 @@ }, { "default": "0", - "depends_on": "eval:in_list(['Discount Percentage', 'Discount Amount'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules", + "depends_on": "eval:in_list(['Discount Percentage'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules", "fieldname": "apply_discount_on_rate", "fieldtype": "Check", - "label": "Apply Discount on Rate" + "label": "Apply Discount on Discounted Rate" }, { "default": "0", @@ -563,7 +563,7 @@ "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2020-08-26 12:24:44.740734", + "modified": "2020-10-28 16:53:14.416172", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 3555ca895f1..22a031c162f 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -385,7 +385,7 @@ class TestPricingRule(unittest.TestCase): so.load_from_db() self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].item_code, "_Test Item 2") - + def test_cumulative_pricing_rule(self): frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule') test_record = { @@ -429,34 +429,61 @@ 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 test_multiple_pricing_rules(self): + make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1, + title="_Test Pricing Rule 1") + make_pricing_rule(discount_percentage=10, selling=1, title="_Test Pricing Rule 2", priority=2, + apply_multiple_pricing_rules=1) + si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1) + self.assertEqual(si.items[0].discount_percentage, 30) + si.delete() + + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + + def test_multiple_pricing_rules_with_apply_discount_on_discounted_rate(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule") + + make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1, + title="_Test Pricing Rule 1") + make_pricing_rule(discount_percentage=10, selling=1, priority=2, + apply_discount_on_rate=1, title="_Test Pricing Rule 2", apply_multiple_pricing_rules=1) + + si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1) + self.assertEqual(si.items[0].discount_percentage, 28) + si.delete() + + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + def make_pricing_rule(**args): args = frappe._dict(args) @@ -468,6 +495,7 @@ def make_pricing_rule(**args): "applicable_for": args.applicable_for, "selling": args.selling or 0, "currency": "USD", + "apply_discount_on_rate": args.apply_discount_on_rate or 0, "buying": args.buying or 0, "min_qty": args.min_qty or 0.0, "max_qty": args.max_qty or 0.0, @@ -476,9 +504,13 @@ def make_pricing_rule(**args): "rate": args.rate or 0.0, "margin_type": args.margin_type, "margin_rate_or_amount": args.margin_rate_or_amount or 0.0, - "condition": args.condition or '' + "condition": args.condition or '', + "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0 }) + if args.get("priority"): + doc.priority = args.get("priority") + apply_on = doc.apply_on.replace(' ', '_').lower() child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'} doc.append(child_table.get(doc.apply_on), { diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 1a58b1dba1b..b003328cc47 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -14,9 +14,8 @@ import frappe from erpnext.setup.doctype.item_group.item_group import get_child_item_groups from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses from erpnext.stock.get_item_details import get_conversion_factor -from frappe import _, throw -from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today - +from frappe import _, bold +from frappe.utils import cint, flt, get_link_to_form, getdate, today, fmt_money class MultiplePricingRuleConflict(frappe.ValidationError): pass @@ -299,12 +298,13 @@ def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, tr fieldname = field if fieldname: - msg = _("""If you {0} {1} quantities of the item {2}, the scheme {3} - will be applied on the item.""").format(type_of_transaction, args.get(fieldname), item_code, args.rule_description) + msg = (_("If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item.") + .format(type_of_transaction, args.get(fieldname), bold(item_code), bold(args.rule_description))) if fieldname in ['min_amt', 'max_amt']: - msg = _("""If you {0} {1} worth item {2}, the scheme {3} will be applied on the item. - """).format(frappe.fmt_money(type_of_transaction, args.get(fieldname)), item_code, args.rule_description) + msg = (_("If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.") + .format(type_of_transaction, fmt_money(args.get(fieldname), currency=args.get("currency")), + bold(item_code), bold(args.rule_description))) frappe.msgprint(msg) From c17cdd7c2e16454b7bcd9bbac8a4366ddf32522f Mon Sep 17 00:00:00 2001 From: Syed Mujeer Hashmi Date: Thu, 29 Oct 2020 11:10:06 +0530 Subject: [PATCH 04/12] fix: Fix for LMS Sign Up link (#23743) Signed-off-by: Syed Mujeer Hashmi Co-authored-by: Shivam Mishra Co-authored-by: Rucha Mahabal --- erpnext/www/lms/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/www/lms/index.html b/erpnext/www/lms/index.html index 7ce3521273f..7b239acd56a 100644 --- a/erpnext/www/lms/index.html +++ b/erpnext/www/lms/index.html @@ -45,7 +45,7 @@

{{ education_settings.description }}

{% if frappe.session.user == 'Guest' %} - {{_('Sign Up')}} + {{_('Sign Up')}} {% endif %}

@@ -62,4 +62,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} From 10f246807699b67315e7b05cce2dc5c9245a27e0 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> Date: Thu, 29 Oct 2020 12:02:48 +0530 Subject: [PATCH 05/12] fix: make asset dashboard charts public (#23751) --- .../asset_value_analytics/asset_value_analytics.json | 4 ++-- .../category_wise_asset_value/category_wise_asset_value.json | 4 ++-- .../location_wise_asset_value/location_wise_asset_value.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json index bc2edc9d7d6..94debf12431 100644 --- a/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json +++ b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json @@ -9,9 +9,9 @@ "filters_json": "{\"status\":\"In Location\",\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"date_based_on\":\"Purchase Date\",\"group_by\":\"--Select a group--\"}", "group_by_type": "Count", "idx": 0, - "is_public": 0, + "is_public": 1, "is_standard": 1, - "modified": "2020-07-23 13:53:33.211371", + "modified": "2020-10-28 23:15:58.432189", "modified_by": "Administrator", "module": "Assets", "name": "Asset Value Analytics", diff --git a/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json index e79d2d73722..78611da0036 100644 --- a/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json +++ b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json @@ -8,9 +8,9 @@ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}", "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"is_existing_asset\":0}", "idx": 0, - "is_public": 0, + "is_public": 1, "is_standard": 1, - "modified": "2020-07-23 13:39:32.429240", + "modified": "2020-10-28 23:16:16.939070", "modified_by": "Administrator", "module": "Assets", "name": "Category-wise Asset Value", diff --git a/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json index 481586e7ca9..848184cc148 100644 --- a/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json +++ b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json @@ -8,9 +8,9 @@ "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}", "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"is_existing_asset\":0}", "idx": 0, - "is_public": 0, + "is_public": 1, "is_standard": 1, - "modified": "2020-07-23 13:42:44.912551", + "modified": "2020-10-28 23:16:07.883312", "modified_by": "Administrator", "module": "Assets", "name": "Location-wise Asset Value", From b193731158a21c0d6a76dd28e3a966258225df70 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 29 Oct 2020 13:23:50 +0530 Subject: [PATCH 06/12] fix: pos profile has no attr 'show_only_available_items' (#23758) --- erpnext/accounts/doctype/pos_profile/pos_profile.json | 9 ++++++++- erpnext/selling/page/point_of_sale/point_of_sale.py | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index 4e22218c6ec..570111acac7 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -14,6 +14,7 @@ "column_break_9", "update_stock", "ignore_pricing_rule", + "hide_unavailable_items", "warehouse", "campaign", "company_address", @@ -307,13 +308,19 @@ "fieldtype": "Check", "label": "Update Stock", "read_only": 1 + }, + { + "default": "0", + "fieldname": "hide_unavailable_items", + "fieldtype": "Check", + "label": "Hide Unavailable Items" } ], "icon": "icon-cog", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-10-20 13:16:50.665081", + "modified": "2020-10-29 13:18:38.795925", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index e5b50d77892..6b9939e8ef0 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -14,11 +14,11 @@ from six import string_types def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""): data = dict() result = [] - warehouse, show_only_available_items = "", False + warehouse, hide_unavailable_items = "", False allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock') if not allow_negative_stock: - warehouse, show_only_available_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'show_only_available_items']) + warehouse, hide_unavailable_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items']) if not frappe.db.exists('Item Group', item_group): item_group = get_root_of('Item Group') @@ -48,7 +48,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt']) bin_join_selection, bin_join_condition = "", "" - if show_only_available_items: + if hide_unavailable_items: bin_join_selection = ", `tabBin` bin" bin_join_condition = "AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0" From d0c2700c8541ee35186c1dea339d824ea3f6a50a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 29 Oct 2020 18:52:16 +0530 Subject: [PATCH 07/12] fix: subscription test case (#23763) Co-authored-by: Marica --- erpnext/accounts/doctype/subscription/test_subscription.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 811fc356cf4..c17fccdce04 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -237,7 +237,7 @@ class TestSubscription(unittest.TestCase): subscription.party_type = 'Customer' subscription.party = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) - subscription.start_date = '2018-01-01' + subscription.start_date = add_days(nowdate(), -1000) subscription.insert() subscription.process() # generate first invoice From bf54ea9976fbf4cf8013e2a78251bb433a74066f Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 29 Oct 2020 18:54:36 +0530 Subject: [PATCH 08/12] chore: (Production Plan) Simplify and fix translation in message popup (#23753) --- .../doctype/production_plan/production_plan.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index b6552d5d6b1..feac9c8fa2b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -735,10 +735,12 @@ def get_items_for_material_requests(doc, warehouses=None): mr_items = new_mr_items if not mr_items: - frappe.msgprint(_("""As raw materials projected quantity is more than required quantity, - there is no need to create material request for the warehouse {0}. - Still if you want to make material request, - kindly enable Ignore Existing Projected Quantity checkbox""").format(doc.get('for_warehouse'))) + to_enable = frappe.bold(_("Ignore Existing Projected Quantity")) + warehouse = frappe.bold(doc.get('for_warehouse')) + message = _("As there are sufficient raw materials, Material Request is not required for Warehouse {0}.").format(warehouse) + "

" + message += _(" If you still want to proceed, please enable {0}.").format(to_enable) + + frappe.msgprint(message, title=_("Note")) return mr_items From 8f6bd851d37a7a456dd1f02a5377e91f9b5b20d5 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 29 Oct 2020 18:55:52 +0530 Subject: [PATCH 09/12] chore: Show Toast alert feedback on Scanning Barcode (#23760) --- erpnext/public/js/controllers/transaction.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 02b1dc0bebb..1358a4bd088 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -352,9 +352,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ let show_description = function(idx, exist = null) { if (exist) { - scan_barcode_field.set_new_description(__('Row #{0}: Qty increased by 1', [idx])); + frappe.show_alert({ + message: __('Row #{0}: Qty increased by 1', [idx]), + indicator: 'green' + }); } else { - scan_barcode_field.set_new_description(__('Row #{0}: Item added', [idx])); + frappe.show_alert({ + message: __('Row #{0}: Item added', [idx]), + indicator: 'green' + }); } } @@ -365,7 +371,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }).then(r => { const data = r && r.message; if (!data || Object.keys(data).length === 0) { - scan_barcode_field.set_new_description(__('Cannot find Item with this barcode')); + frappe.show_alert({ + message: __('Cannot find Item with this Barcode'), + indicator: 'red' + }); return; } From 9c67629e281696bf673fc4582ebf59a50b4b05ea Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 29 Oct 2020 19:51:57 +0530 Subject: [PATCH 10/12] fix: Ignore cancelled entries instock balance report (#23757) Co-authored-by: Nabin Hait --- erpnext/stock/report/stock_balance/stock_balance.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 042087a4a77..1339d9b6820 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -168,6 +168,7 @@ def get_stock_ledger_entries(filters, items): from `tabStock Ledger Entry` sle force index (posting_sort_index) where sle.docstatus < 2 %s %s + and is_cancelled = 0 order by sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty""" % #nosec (item_conditions_sql, conditions), as_dict=1) From 9db26155ad6fea07e4d03a14eb9e6f33d8b429ae Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 29 Oct 2020 22:39:21 +0530 Subject: [PATCH 11/12] fix: set SLA variance in seconds for Duration fieldtype (#23765) * fix: calculate SLA variance in seconds for Duration fieldtype * fix: sider --- erpnext/support/doctype/issue/issue.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 920c13c38d6..62b39cced53 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -7,7 +7,7 @@ import json from frappe import _ from frappe import utils from frappe.model.document import Document -from frappe.utils import time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff +from frappe.utils import now_datetime, getdate, get_weekdays, add_to_date, get_time, get_datetime, time_diff_in_seconds from datetime import datetime, timedelta from frappe.model.mapper import get_mapped_doc from frappe.utils.user import is_website_user @@ -355,13 +355,13 @@ def set_service_level_agreement_variance(issue=None): doc = frappe.get_doc("Issue", issue.name) if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer - variance = round(time_diff_in_hours(doc.response_by, current_time), 2) + variance = round(time_diff_in_seconds(doc.response_by, current_time), 2) frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False) if variance < 0: frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False) if not doc.resolution_date: # resolution_date set when issue has been closed - variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2) + variance = round(time_diff_in_seconds(doc.resolution_by, current_time), 2) frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False) if variance < 0: frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False) From ca5f5cb367b1522835e85c65bdfd809aa6e45620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Ryckel?= Date: Thu, 29 Oct 2020 20:34:13 +0300 Subject: [PATCH 12/12] Update student_attendance.py (#23761) Co-authored-by: Rucha Mahabal --- .../education/doctype/student_attendance/student_attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/education/doctype/student_attendance/student_attendance.py b/erpnext/education/doctype/student_attendance/student_attendance.py index 595dff9b9dd..72a8f55c663 100644 --- a/erpnext/education/doctype/student_attendance/student_attendance.py +++ b/erpnext/education/doctype/student_attendance/student_attendance.py @@ -75,6 +75,6 @@ class StudentAttendance(Document): }) if attendance_record: - record = get_link_to_form('Attendance Record', attendance_record) + record = get_link_to_form('Student Attendance', attendance_record) frappe.throw(_('Student Attendance record {0} already exists against the Student {1}') .format(record, frappe.bold(self.student)), title=_('Duplicate Entry'))