From cd8422a840d97d748b0e1656f0da49ba6ea5647c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 6 Mar 2021 22:08:08 +0530 Subject: [PATCH 1/4] feat: recursive product discount --- .../doctype/pricing_rule/pricing_rule.json | 37 +++++++++++------- .../doctype/pricing_rule/pricing_rule.py | 1 + .../accounts/doctype/pricing_rule/utils.py | 38 +++++++++++-------- .../promotional_scheme/promotional_scheme.py | 2 +- .../promotional_scheme_product_discount.json | 15 +++++++- erpnext/controllers/taxes_and_totals.py | 5 ++- erpnext/public/js/controllers/transaction.js | 38 ++++++++++++------- 7 files changed, 90 insertions(+), 46 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index d08a854142..81890d50b9 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -44,6 +44,14 @@ "column_break_21", "min_amt", "max_amt", + "product_discount_scheme_section", + "same_item", + "free_item", + "free_qty", + "free_item_rate", + "column_break_42", + "free_item_uom", + "is_recursive", "section_break_23", "valid_from", "valid_upto", @@ -62,13 +70,6 @@ "discount_amount", "discount_percentage", "for_price_list", - "product_discount_scheme_section", - "same_item", - "free_item", - "free_qty", - "column_break_51", - "free_item_uom", - "free_item_rate", "section_break_13", "threshold_percentage", "priority", @@ -459,10 +460,6 @@ "fieldtype": "Float", "label": "Qty" }, - { - "fieldname": "column_break_51", - "fieldtype": "Column Break" - }, { "fieldname": "free_item_uom", "fieldtype": "Link", @@ -553,19 +550,33 @@ "fieldname": "promotional_scheme", "fieldtype": "Link", "label": "Promotional Scheme", - "options": "Promotional Scheme" + "no_copy": 1, + "options": "Promotional Scheme", + "print_hide": 1, + "read_only": 1 }, { "description": "Simple Python Expression, Example: territory != 'All Territories'", "fieldname": "condition", "fieldtype": "Code", "label": "Condition" + }, + { + "fieldname": "column_break_42", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on", + "fieldname": "is_recursive", + "fieldtype": "Check", + "label": "Is Recursive" } ], "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2020-12-04 00:36:24.698219", + "modified": "2021-03-06 22:01:24.840422", "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 05652642eb..9a3ea27621 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -237,6 +237,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa "doctype": args.doctype, "has_margin": False, "name": args.name, + "free_item_data": [], "parent": args.parent, "parenttype": args.parenttype, "child_docname": args.get('child_docname') diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index d163335996..210cd16009 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -367,7 +367,7 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args): if items and doc.get("items"): for row in doc.get('items'): - if row.get(apply_on) not in items: continue + if (row.get(apply_on) or args.get(apply_on)) not in items: continue if pr_doc.mixed_conditions: amt = args.get('qty') * args.get("price_list_rate") @@ -479,7 +479,7 @@ def apply_pricing_rule_on_transaction(doc): doc.calculate_taxes_and_totals() elif d.price_or_product_discount == 'Product': - item_details = frappe._dict({'parenttype': doc.doctype}) + item_details = frappe._dict({'parenttype': doc.doctype, 'free_item_data': []}) get_product_discount_rule(d, item_details, doc=doc) apply_pricing_rule_for_free_items(doc, item_details.free_item_data) doc.set_missing_values() @@ -508,9 +508,15 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): frappe.throw(_("Free item not set in the pricing rule {0}") .format(get_link_to_form("Pricing Rule", pricing_rule.name))) - item_details.free_item_data = { + qty = pricing_rule.free_qty or 1 + if pricing_rule.is_recursive: + transaction_qty = args.get('qty') if args else doc.total_qty + if transaction_qty: + qty = flt(transaction_qty) * qty + + free_item_data_args = { 'item_code': free_item, - 'qty': pricing_rule.free_qty or 1, + 'qty': qty, 'rate': pricing_rule.free_item_rate or 0, 'price_list_rate': pricing_rule.free_item_rate or 0, 'is_free_item': 1 @@ -519,24 +525,26 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): item_data = frappe.get_cached_value('Item', free_item, ['item_name', 'description', 'stock_uom'], as_dict=1) - item_details.free_item_data.update(item_data) - item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom - item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item, - item_details.free_item_data['uom']).get("conversion_factor", 1) + free_item_data_args.update(item_data) + free_item_data_args['uom'] = pricing_rule.free_item_uom or item_data.stock_uom + free_item_data_args['conversion_factor'] = get_conversion_factor(free_item, + free_item_data_args['uom']).get("conversion_factor", 1) if item_details.get("parenttype") == 'Purchase Order': - item_details.free_item_data['schedule_date'] = doc.schedule_date if doc else today() + free_item_data_args['schedule_date'] = doc.schedule_date if doc else today() if item_details.get("parenttype") == 'Sales Order': - item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today() + free_item_data_args['delivery_date'] = doc.delivery_date if doc else today() + + item_details.free_item_data.append(free_item_data_args) def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False): - if pricing_rule_args.get('item_code'): - items = [d.item_code for d in doc.items - if d.item_code == (pricing_rule_args.get("item_code")) and d.is_free_item] + if pricing_rule_args: + items = tuple([d.item_code for d in doc.items if d.is_free_item]) - if not items: - doc.append('items', pricing_rule_args) + for args in pricing_rule_args: + if not items or args.get('item_code') not in items: + doc.append('items', args) def get_pricing_rule_items(pr_doc): apply_on_data = [] diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 89f7238a06..6e13f0694c 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -21,7 +21,7 @@ price_discount_fields = ['rate_or_discount', 'apply_discount_on', 'apply_discoun 'rate', 'discount_amount', 'discount_percentage', 'validate_applied_rule'] product_discount_fields = ['free_item', 'free_qty', 'free_item_uom', - 'free_item_rate', 'same_item'] + 'free_item_rate', 'same_item', 'is_recursive'] class PromotionalScheme(Document): def validate(self): diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json index 72d53bfa01..3eab51510d 100644 --- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json @@ -1,10 +1,12 @@ { + "actions": [], "creation": "2019-03-24 14:48:59.649168", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ "disable", + "apply_multiple_pricing_rules", "column_break_2", "rule_description", "section_break_1", @@ -25,7 +27,7 @@ "threshold_percentage", "column_break_15", "priority", - "apply_multiple_pricing_rules" + "is_recursive" ], "fields": [ { @@ -152,10 +154,19 @@ "fieldname": "apply_multiple_pricing_rules", "fieldtype": "Check", "label": "Apply Multiple Pricing Rules" + }, + { + "default": "0", + "description": "Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on", + "fieldname": "is_recursive", + "fieldtype": "Check", + "label": "Is Recursive" } ], + "index_web_pages_for_search": 1, "istable": 1, - "modified": "2019-07-21 00:00:56.674284", + "links": [], + "modified": "2021-03-06 21:58:18.162346", "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Product Discount", diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 23541c1ba0..220c876cc2 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -111,7 +111,10 @@ class calculate_taxes_and_totals(object): item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item) if flt(item.rate_with_margin) > 0: item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate")) - item.discount_amount = item.rate_with_margin - item.rate + if not item.discount_amount: + item.discount_amount = item.rate_with_margin - item.rate + elif not item.discount_percentage: + item.rate -= item.discount_amount elif flt(item.price_list_rate) > 0: item.discount_amount = item.price_list_rate - item.rate elif flt(item.price_list_rate) > 0 and not item.discount_amount: diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 7d90d2662e..6f577936f2 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -576,7 +576,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ var d = locals[cdt][cdn]; me.add_taxes_from_item_tax_template(d.item_tax_rate); if (d.free_item_data) { - me.apply_product_discount(d.free_item_data); + me.apply_product_discount(d); } }, () => { @@ -1163,7 +1163,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ calculate_stock_uom_rate: function(doc, cdt, cdn) { let item = frappe.get_doc(cdt, cdn); - item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor); + item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor); refresh_field("stock_uom_rate", item.name, item.parentfield); }, service_stop_date: function(frm, cdt, cdn) { @@ -1504,7 +1504,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } if (d.free_item_data) { - me.apply_product_discount(d.free_item_data); + me.apply_product_discount(d); } if (d.apply_rule_on_other_items) { @@ -1538,20 +1538,30 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, - apply_product_discount: function(free_item_data) { - const items = this.frm.doc.items.filter(d => (d.item_code == free_item_data.item_code - && d.is_free_item)) || []; + apply_product_discount: function(args) { + const items = this.frm.doc.items.filter(d => (d.is_free_item)) || []; - if (!items.length) { - let row_to_modify = frappe.model.add_child(this.frm.doc, - this.frm.doc.doctype + ' Item', 'items'); + const exist_items = items.map(row => row.item_code); - for (let key in free_item_data) { - row_to_modify[key] = free_item_data[key]; + args.free_item_data.forEach(pr_row => { + let row_to_modify = {}; + if (!items || !in_list(exist_items, pr_row.item_code)) { + + row_to_modify = frappe.model.add_child(this.frm.doc, + this.frm.doc.doctype + ' Item', 'items'); + + } else if(items) { + row_to_modify = items.filter(d => d.item_code === pr_row.item_code)[0]; } - } if (items && items.length && free_item_data) { - items[0].qty = free_item_data.qty - } + + for (let key in pr_row) { + row_to_modify[key] = pr_row[key]; + } + }); + + // free_item_data is a temporary variable + args.free_item_data = ''; + refresh_field('items'); }, apply_price_list: function(item, reset_plc_conversion) { From fce552b811983869358d2a8d1658853ee9889199 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 7 Mar 2021 11:43:22 +0530 Subject: [PATCH 2/4] fix: nonetype object has no attribute options --- .../promotional_scheme/promotional_scheme.py | 4 +- .../promotional_scheme_price_discount.json | 721 ++---------------- erpnext/public/js/controllers/transaction.js | 5 +- 3 files changed, 61 insertions(+), 669 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 6e13f0694c..e1725bc89e 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -18,10 +18,10 @@ other_fields = ['min_qty', 'max_qty', 'min_amt', 'max_amt', 'priority','warehouse', 'threshold_percentage', 'rule_description'] price_discount_fields = ['rate_or_discount', 'apply_discount_on', 'apply_discount_on_rate', - 'rate', 'discount_amount', 'discount_percentage', 'validate_applied_rule'] + 'rate', 'discount_amount', 'discount_percentage', 'validate_applied_rule', 'apply_multiple_pricing_rules'] product_discount_fields = ['free_item', 'free_qty', 'free_item_uom', - 'free_item_rate', 'same_item', 'is_recursive'] + 'free_item_rate', 'same_item', 'is_recursive', 'apply_multiple_pricing_rules'] class PromotionalScheme(Document): def validate(self): diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json index 224b8de779..795fb1c6f4 100644 --- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json @@ -1,792 +1,181 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, + "actions": [], "creation": "2019-03-24 14:48:59.649168", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "disable", + "apply_multiple_pricing_rules", + "column_break_2", + "rule_description", + "section_break_2", + "min_qty", + "max_qty", + "column_break_3", + "min_amount", + "max_amount", + "section_break_6", + "rate_or_discount", + "column_break_10", + "rate", + "discount_amount", + "discount_percentage", + "section_break_11", + "warehouse", + "threshold_percentage", + "validate_applied_rule", + "column_break_14", + "priority", + "apply_discount_on_rate" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "disable", "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": "Disable", - "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 + "label": "Disable" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_2", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "rule_description", "fieldtype": "Small Text", - "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": "Rule Description", - "length": 0, "no_copy": 1, - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_2", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, "columns": 1, "default": "0", "fieldname": "min_qty", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Min Qty", - "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 + "label": "Min Qty" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, "columns": 1, "default": "0", "fieldname": "max_qty", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Max Qty", - "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 + "label": "Max Qty" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "fieldname": "min_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Min Amount", - "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 + "label": "Min Amount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", - "depends_on": "", "fieldname": "max_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Max Amount", - "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 + "label": "Max Amount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "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": "", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Discount Percentage", - "depends_on": "", "fieldname": "rate_or_discount", "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": 0, "label": "Discount Type", - "length": 0, - "no_copy": 0, - "options": "\nRate\nDiscount Percentage\nDiscount Amount", - "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 + "options": "\nRate\nDiscount Percentage\nDiscount Amount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", "fieldname": "column_break_10", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, "columns": 2, "depends_on": "eval:doc.rate_or_discount==\"Rate\"", "fieldname": "rate", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Rate", - "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 + "label": "Rate" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.rate_or_discount==\"Discount Amount\"", "fieldname": "discount_amount", "fieldtype": "Currency", - "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": "Discount Amount", - "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 + "label": "Discount Amount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.rate_or_discount==\"Discount Percentage\"", "fieldname": "discount_percentage", "fieldtype": "Float", - "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": "Discount Percentage", - "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 + "label": "Discount Percentage" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_11", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "warehouse", "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": "Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "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 + "options": "Warehouse" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "threshold_percentage", "fieldtype": "Percent", - "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": "Threshold for Suggestion", - "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 + "label": "Threshold for Suggestion" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "fieldname": "validate_applied_rule", "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": "Validate Applied Rule", - "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 + "label": "Validate Applied Rule" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_14", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "priority", "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": "Priority", - "length": 0, - "no_copy": 0, - "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20", - "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 + "options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "priority", "fieldname": "apply_multiple_pricing_rules", "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": "Apply Multiple Pricing Rules", - "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 + "label": "Apply Multiple Pricing Rules" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "depends_on": "eval:in_list(['Discount Percentage', 'Discount Amount'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules", "fieldname": "apply_discount_on_rate", "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": "Apply Discount on Rate", - "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 + "label": "Apply Discount on Rate" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, + "index_web_pages_for_search": 1, "istable": 1, - "max_attachments": 0, - "modified": "2019-03-24 14:48:59.649168", + "links": [], + "modified": "2021-03-07 11:56:23.424137", "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Price Discount", - "name_case": "", "owner": "Administrator", "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 6f577936f2..d6d1e6f025 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1492,7 +1492,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(k=="price_list_rate") { if(flt(v) != flt(d.price_list_rate)) price_list_rate_changed = true; } - frappe.model.set_value(d.doctype, d.name, k, v); + + if (k !== 'free_item_data') { + frappe.model.set_value(d.doctype, d.name, k, v); + } } } From 0486276789eb517fbd20d70aa482798ca27e4803 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 17 Mar 2021 18:49:54 +0530 Subject: [PATCH 3/4] fix: don't club same free item --- erpnext/accounts/doctype/pricing_rule/utils.py | 5 +++-- .../doctype/promotional_scheme/promotional_scheme.py | 2 +- erpnext/public/js/controllers/transaction.js | 7 ++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 210cd16009..c676abd4c6 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -517,6 +517,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): free_item_data_args = { 'item_code': free_item, 'qty': qty, + 'pricing_rules': pricing_rule.name, 'rate': pricing_rule.free_item_rate or 0, 'price_list_rate': pricing_rule.free_item_rate or 0, 'is_free_item': 1 @@ -540,10 +541,10 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False): if pricing_rule_args: - items = tuple([d.item_code for d in doc.items if d.is_free_item]) + items = tuple([(d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item]) for args in pricing_rule_args: - if not items or args.get('item_code') not in items: + if not items or (args.get('item_code'), args.get('pricing_rules')) not in items: doc.append('items', args) def get_pricing_rule_items(pr_doc): diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index e1725bc89e..523e9ee08a 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -12,7 +12,7 @@ from frappe.model.document import Document pricing_rule_fields = ['apply_on', 'mixed_conditions', 'is_cumulative', 'other_item_code', 'other_item_group' 'apply_rule_on_other', 'other_brand', 'selling', 'buying', 'applicable_for', 'valid_from', 'valid_upto', 'customer', 'customer_group', 'territory', 'sales_partner', 'campaign', 'supplier', - 'supplier_group', 'company', 'currency'] + 'supplier_group', 'company', 'currency', 'apply_multiple_pricing_rules'] other_fields = ['min_qty', 'max_qty', 'min_amt', 'max_amt', 'priority','warehouse', 'threshold_percentage', 'rule_description'] diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index d6d1e6f025..4173beb576 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1544,17 +1544,18 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ apply_product_discount: function(args) { const items = this.frm.doc.items.filter(d => (d.is_free_item)) || []; - const exist_items = items.map(row => row.item_code); + const exist_items = items.map(row => (row.item_code, row.pricing_rules)); args.free_item_data.forEach(pr_row => { let row_to_modify = {}; - if (!items || !in_list(exist_items, pr_row.item_code)) { + if (!items || !in_list(exist_items, (pr_row.item_code, pr_row.pricing_rules))) { row_to_modify = frappe.model.add_child(this.frm.doc, this.frm.doc.doctype + ' Item', 'items'); } else if(items) { - row_to_modify = items.filter(d => d.item_code === pr_row.item_code)[0]; + row_to_modify = items.filter(d => (d.item_code === pr_row.item_code + && d.pricing_rules === pr_row.pricing_rules))[0]; } for (let key in pr_row) { From 256b9c7bf9b92efc1426605d3e1f71a5f4340a17 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 22 Mar 2021 23:36:48 +0530 Subject: [PATCH 4/4] fix: total weight not set for free items --- erpnext/controllers/accounts_controller.py | 3 ++- erpnext/stock/get_item_details.py | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e6b14c2e40..256437966b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -25,7 +25,8 @@ from erpnext.stock.doctype.packed_item.packed_item import make_packing_list class AccountMissingError(frappe.ValidationError): pass -force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") +force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", + "pricing_rules", "weight_per_unit", "weight_uom", "total_weight") class AccountsController(TransactionBase): def __init__(self, *args, **kwargs): diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 873cfec85e..70e4c2c40e 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -314,7 +314,9 @@ def get_basic_details(args, item, overwrite_warehouse=True): "last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0, "transaction_date": args.get("transaction_date"), "against_blanket_order": args.get("against_blanket_order"), - "bom_no": item.get("default_bom") + "bom_no": item.get("default_bom"), + "weight_per_unit": args.get("weight_per_unit") or item.get("weight_per_unit"), + "weight_uom": args.get("weight_uom") or item.get("weight_uom") }) if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"): @@ -369,6 +371,9 @@ def get_basic_details(args, item, overwrite_warehouse=True): if meta.get_field("barcode"): update_barcode_value(out) + if out.get("weight_per_unit"): + out['total_weight'] = out.weight_per_unit * out.stock_qty + return out def get_item_warehouse(item, args, overwrite_warehouse, defaults={}):