From e45ec661ea608a4a0bc51e1e84f3645b792112d5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 1 Aug 2018 17:44:34 +0530 Subject: [PATCH] Additional fields in Item Price (#14540) * [IMPROVEMENT] Item Price New Fields and Features * [IMPROVEMENT] Item Price - Item Price insert against Qty, Supplier/Customer, Validup/Validfrom - Stock Setting to accept Default Price List for Purchase or Sales Item - Move Item Section in Item Price * Fetch default Item Price based on Customer as null * test fixes * Code cleanup and fixed the issue of incorrect item price fetched against the customer * Removed min qty from packing_increment calculation * Removed auto_update_price_list_rate from the stock settings * Revert sales_order.json changes * Removed sales, purchase price list from stock settings because this fields are already available in selling, buying settings * Removed unnecessory file * Fixed item price validation code --- .../doctype/purchase_order/purchase_order.js | 3 + erpnext/public/js/controllers/buying.js | 4 +- erpnext/public/js/controllers/transaction.js | 2 + .../doctype/sales_order/sales_order.js | 3 + erpnext/selling/sales_common.js | 7 +- .../stock/doctype/item_price/item_price.js | 1 + .../stock/doctype/item_price/item_price.json | 990 +++++++++++++----- .../stock/doctype/item_price/item_price.py | 57 +- .../doctype/item_price/test_item_price.js | 2 +- .../doctype/item_price/test_item_price.py | 139 ++- .../doctype/item_price/test_records.json | 31 +- .../stock/doctype/price_list/price_list.json | 11 +- erpnext/stock/get_item_details.py | 124 ++- 13 files changed, 1066 insertions(+), 308 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index ea5c6f7c15..d7ffe04021 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -49,6 +49,9 @@ frappe.ui.form.on("Purchase Order", { onload: function(frm) { set_schedule_date(frm); + if (!frm.doc.transaction_date){ + frm.set_value('transaction_date', frappe.datetime.get_today()) + } erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index e420a48814..076b39c1c8 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -93,7 +93,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ supplier: function() { var me = this; - erpnext.utils.get_party_details(this.frm, null, null, function(){me.apply_pricing_rule()}); + erpnext.utils.get_party_details(this.frm, null, null, function(){ + me.apply_price_list(); + }); }, supplier_address: function() { diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 63a7a0e206..07ddcb42db 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -342,6 +342,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ conversion_factor: item.conversion_factor, weight_per_unit: item.weight_per_unit, weight_uom: item.weight_uom, + uom : item.uom, pos_profile: me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '' } }, @@ -1016,6 +1017,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ "item_group": d.item_group, "brand": d.brand, "qty": d.qty, + "uom": d.uom, "parenttype": d.parenttype, "parent": d.parent, "pricing_rule": d.pricing_rule, diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 36e181b833..9de9c6b0e5 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -30,6 +30,9 @@ frappe.ui.form.on("Sales Order", { } }, onload: function(frm) { + if (!frm.doc.transaction_date){ + frm.set_value('transaction_date', frappe.datetime.get_today()) + } erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 19acefa72c..4eb99c5df5 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -96,8 +96,9 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ customer: function() { var me = this; - erpnext.utils.get_party_details(this.frm, null, null, - function(){ me.apply_pricing_rule() }); + erpnext.utils.get_party_details(this.frm, null, null, function() { + me.apply_price_list(); + }); }, customer_address: function() { @@ -429,4 +430,4 @@ frappe.ui.form.on(cur_frm.doctype,"project", function(frm) { }) } } -}) \ No newline at end of file +}) diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js index 56bd185c0c..2729f4b15e 100644 --- a/erpnext/stock/doctype/item_price/item_price.js +++ b/erpnext/stock/doctype/item_price/item_price.js @@ -11,6 +11,7 @@ frappe.ui.form.on("Item Price", { // Fetch item details frm.add_fetch("item_code", "item_name", "item_name"); frm.add_fetch("item_code", "description", "item_description"); + frm.add_fetch("item_code", "stock_uom", "uom"); frm.set_df_property("bulk_import_help", "options", '' + __("Import in Bulk") + ''); diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json index a342c0fd68..2d73cbce06 100644 --- a/erpnext/stock/doctype/item_price/item_price.json +++ b/erpnext/stock/doctype/item_price/item_price.json @@ -16,220 +16,7 @@ "fields": [ { "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "price_list_details", - "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": "Price List", - "length": 0, - "no_copy": 0, - "options": "fa fa-tags", - "permlevel": 0, - "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_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "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 - }, - { - "allow_bulk_edit": 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 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "buying", - "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": "Buying", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "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 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "selling", - "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": "Selling", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "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 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_details", - "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, - "options": "fa fa-tag", - "permlevel": 0, - "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_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "currency", - "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": 0, - "label": "Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "print_hide": 0, - "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 - }, - { - "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -258,11 +45,558 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "uom", + "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": "UOM", + "length": 0, + "no_copy": 0, + "options": "UOM", + "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_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "description": "Quantity that must be bought or sold per UOM", + "fieldname": "packing_unit", + "fieldtype": "Int", + "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": "Packing Unit", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "fieldname": "min_qty", + "fieldtype": "Int", + "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": "Minimum 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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_17", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "item_name", + "fieldtype": "Data", + "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": "Item Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "item_code.brand", + "fieldname": "brand", + "fieldtype": "Read Only", + "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": "Brand", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "item_description", + "fieldtype": "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": "Item Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "price_list_details", + "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": "Price List", + "length": 0, + "no_copy": 0, + "options": "fa fa-tags", + "permlevel": 0, + "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_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "price_list", + "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": "Price List", + "length": 0, + "no_copy": 0, + "options": "Price List", + "permlevel": 0, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.selling == 1", + "fieldname": "customer", + "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": "Customer", + "length": 0, + "no_copy": 0, + "options": "Customer", + "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_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:doc.buying == 1", + "fieldname": "supplier", + "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": "Supplier", + "length": 0, + "no_copy": 0, + "options": "Supplier", + "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_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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "buying", + "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": "Buying", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "selling", + "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": "Selling", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "item_details", + "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, + "options": "fa fa-tag", + "permlevel": 0, + "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_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "currency", + "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": "Currency", + "length": 0, + "no_copy": 0, + "options": "Currency", + "permlevel": 0, + "print_hide": 0, + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "col_br_1", + "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, + "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_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -291,16 +625,114 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "col_br_1", + "fieldname": "section_break_15", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Today", + "fieldname": "valid_from", + "fieldtype": "Date", + "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": "Valid From ", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "lead_time_days", + "fieldtype": "Int", + "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": "Lead Time in days", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_18", "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, @@ -312,6 +744,7 @@ "length": 0, "no_copy": 0, "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, @@ -320,50 +753,18 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fetch_from": "item_code.brand", - "fieldname": "brand", - "fieldtype": "Read Only", - "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": "Brand", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "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 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_name", - "fieldtype": "Data", + "fieldname": "valid_upto", + "fieldtype": "Date", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -371,28 +772,61 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Item Name", + "label": "Valid Upto ", "length": 0, "no_copy": 0, "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 1, + "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "item_description", + "fieldname": "section_break_24", + "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 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "note", "fieldtype": "Text", "hidden": 0, "ignore_user_permissions": 0, @@ -401,19 +835,52 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Item Description", + "label": "Note", "length": 0, "no_copy": 0, "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 1, + "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, "set_only_once": 0, - "translatable": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "reference", + "fieldtype": "Data", + "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": "Reference", + "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 } ], @@ -428,10 +895,11 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-05-16 22:43:08.371709", + "modified": "2018-06-15 16:36:50.162766", "modified_by": "Administrator", "module": "Stock", "name": "Item Price", + "name_case": "Title Case", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index 6025ef7f9b..d182290427 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -3,41 +3,60 @@ from __future__ import unicode_literals import frappe -from frappe import throw, _ +from frappe import _ + class ItemPriceDuplicateItem(frappe.ValidationError): pass + from frappe.model.document import Document + class ItemPrice(Document): + def validate(self): self.validate_item() - self.validate_price_list() - self.check_duplicate_item() + self.validate_dates() self.update_price_list_details() self.update_item_details() + self.check_duplicates() def validate_item(self): if not frappe.db.exists("Item", self.item_code): - throw(_("Item {0} not found").format(self.item_code)) + frappe.throw(_("Item {0} not found").format(self.item_code)) - def validate_price_list(self): - enabled = frappe.db.get_value("Price List", self.price_list, "enabled") - if not enabled: - throw(_("Price List {0} is disabled").format(self.price_list)) - - def check_duplicate_item(self): - if frappe.db.sql("""select name from `tabItem Price` - where item_code=%s and price_list=%s and name!=%s""", (self.item_code, self.price_list, self.name)): - - frappe.throw(_("Item {0} appears multiple times in Price List {1}").format(self.item_code, self.price_list), - ItemPriceDuplicateItem) + def validate_dates(self): + if self.valid_from and self.valid_upto: + if self.valid_from > self.valid_upto: + frappe.throw(_("Valid From Date must be lesser than Valid Upto Date.")) def update_price_list_details(self): self.buying, self.selling, self.currency = \ - frappe.db.get_value("Price List", {"name": self.price_list, "enabled": 1}, - ["buying", "selling", "currency"]) + frappe.db.get_value("Price List", + {"name": self.price_list, "enabled": 1}, + ["buying", "selling", "currency"]) def update_item_details(self): - self.item_name, self.item_description = frappe.db.get_value("Item", - self.item_code, ["item_name", "description"]) + self.item_name, self.item_description = frappe.db.get_value("Item",self.item_code,["item_name", "description"]) + + def check_duplicates(self): + conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s" + + for field in ['uom', 'min_qty', 'valid_from', + 'valid_upto', 'packing_unit', 'customer', 'supplier']: + if self.get(field): + conditions += " and {0} = %({1})s".format(field, field) + + price_list_rate = frappe.db.sql(""" + SELECT price_list_rate + FROM `tabItem Price` + {conditions} """.format(conditions=conditions), self.as_dict()) + + if price_list_rate : + frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates."), ItemPriceDuplicateItem) + + def before_save(self): + if self.selling: + self.reference = self.customer + if self.buying: + self.reference = self.supplier diff --git a/erpnext/stock/doctype/item_price/test_item_price.js b/erpnext/stock/doctype/item_price/test_item_price.js index 8f2f28906a..49dbaa2051 100644 --- a/erpnext/stock/doctype/item_price/test_item_price.js +++ b/erpnext/stock/doctype/item_price/test_item_price.js @@ -19,4 +19,4 @@ QUnit.test("test item price", function(assert) { () => frappe.timeout(0.3), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py index 4fefcbe39f..455dff4e15 100644 --- a/erpnext/stock/doctype/item_price/test_item_price.py +++ b/erpnext/stock/doctype/item_price/test_item_price.py @@ -4,11 +4,144 @@ from __future__ import unicode_literals import unittest import frappe +from frappe.test_runner import make_test_records_for_doctype +from erpnext.stock.get_item_details import get_price_list_rate_for, process_args +from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem + + +class TestItemPrice(unittest.TestCase): + def setUp(self): + frappe.db.sql("delete from `tabItem Price`") + make_test_records_for_doctype("Item Price", force=True) -class TestItem(unittest.TestCase): def test_duplicate_item(self): - from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem doc = frappe.copy_doc(test_records[0]) self.assertRaises(ItemPriceDuplicateItem, doc.save) -test_records = frappe.get_test_records('Item Price') \ No newline at end of file + def test_addition_of_new_fields(self): + # Based on https://github.com/frappe/erpnext/issues/8456 + test_fields_existance = [ + 'supplier', 'customer', 'uom', 'min_qty', 'lead_time_days', + 'packing_unit', 'valid_from', 'valid_upto', 'note' + ] + doc_fields = frappe.copy_doc(test_records[1]).__dict__.keys() + + for test_field in test_fields_existance: + self.assertTrue(test_field in doc_fields) + + def test_dates_validation_error(self): + doc = frappe.copy_doc(test_records[1]) + # Enter invalid dates valid_from >= valid_upto + doc.valid_from = "2017-04-20" + doc.valid_upto = "2017-04-17" + # Valid Upto Date can not be less/equal than Valid From Date + self.assertRaises(frappe.ValidationError, doc.save) + + def test_price_in_a_qty(self): + # Check correct price at this quantity + doc = frappe.copy_doc(test_records[2]) + + args = { + "price_list": doc.price_list, + "min_qty": doc.min_qty, + "customer": doc.customer, + "uom": "_Test UOM", + "transaction_date": '2017-04-18', + "qty": 10 + } + + price = get_price_list_rate_for(process_args(args), doc.item_code) + self.assertEqual(price, 20.0) + + def test_price_with_no_qty(self): + # Check correct price when no quantity + doc = frappe.copy_doc(test_records[2]) + args = { + "price_list": doc.price_list, + "min_qty": 30, + "customer": doc.customer, + "uom": "_Test UOM", + "transaction_date": '2017-04-18', + } + + price = get_price_list_rate_for(args, doc.item_code) + self.assertEqual(price, None) + + + def test_prices_at_date(self): + # Check correct price at first date + doc = frappe.copy_doc(test_records[2]) + + args = { + "price_list": doc.price_list, + "min_qty": doc.min_qty, + "customer": "_Test Customer", + "uom": "_Test UOM", + "transaction_date": '2017-04-18', + "qty": 7 + } + + price = get_price_list_rate_for(args, doc.item_code) + self.assertEqual(price, 20) + + def test_prices_at_invalid_date(self): + #Check correct price at invalid date + doc = frappe.copy_doc(test_records[3]) + + args = { + "price_list": doc.price_list, + "min_qty": doc.min_qty, + "qty": 7, + "uom": "_Test UOM", + "transaction_date": "01-15-2019" + } + + price = get_price_list_rate_for(args, doc.item_code) + self.assertEqual(price, None) + + def test_prices_outside_of_date(self): + #Check correct price when outside of the date + doc = frappe.copy_doc(test_records[4]) + + args = { + "price_list": doc.price_list, + "min_qty": doc.min_qty, + "customer": "_Test Customer", + "uom": "_Test UOM", + "transaction_date": "2017-04-25", + "qty": 7, + } + + price = get_price_list_rate_for(args, doc.item_code) + self.assertEqual(price, None) + + def test_lowest_price_when_no_date_provided(self): + #Check lowest price when no date provided + doc = frappe.copy_doc(test_records[1]) + + args = { + "price_list": doc.price_list, + "min_qty": doc.min_qty, + "uom": "_Test UOM", + "qty": 7, + } + + price = get_price_list_rate_for(args, doc.item_code) + self.assertEqual(price, 10) + + + def test_invalid_item(self): + doc = frappe.copy_doc(test_records[1]) + # Enter invalid item code + doc.item_code = "This is not an item code" + # Valid item codes must already exist + self.assertRaises(frappe.ValidationError, doc.save) + + def test_invalid_price_list(self): + doc = frappe.copy_doc(test_records[1]) + # Check for invalid price list + doc.price_list = "This is not a price list" + # Valid price list must already exist + self.assertRaises(frappe.ValidationError, doc.save) + +test_records = frappe.get_test_records('Item Price') diff --git a/erpnext/stock/doctype/item_price/test_records.json b/erpnext/stock/doctype/item_price/test_records.json index 36870cdf7e..473bacb3c3 100644 --- a/erpnext/stock/doctype/item_price/test_records.json +++ b/erpnext/stock/doctype/item_price/test_records.json @@ -3,24 +3,47 @@ "doctype": "Item Price", "item_code": "_Test Item", "price_list": "_Test Price List", - "price_list_rate": 100 + "price_list_rate": 100, + "min_qty": 2, + "valid_from": "2017-04-18", + "valid_upto": "2017-04-26" }, { "doctype": "Item Price", "item_code": "_Test Item", "price_list": "_Test Price List Rest of the World", - "price_list_rate": 10 + "price_list_rate": 10, + "min_qty": 5 }, { "doctype": "Item Price", "item_code": "_Test Item 2", "price_list": "_Test Price List Rest of the World", - "price_list_rate": 20 + "price_list_rate": 20, + "valid_from": "2017-04-18", + "valid_upto": "2017-04-26", + "min_qty": 7, + "customer": "_Test Customer", + "uom": "_Test UOM" }, { "doctype": "Item Price", "item_code": "_Test Item Home Desktop 100", "price_list": "_Test Price List", - "price_list_rate": 1000 + "price_list_rate": 1000, + "min_qty" : 10, + "valid_from": "2017-04-10", + "valid_upto": "2017-04-17", + "min_qty": 2 + }, + { + "doctype": "Item Price", + "item_code": "_Test Item Home Desktop Manufactured", + "price_list": "_Test Price List", + "price_list_rate": 1000, + "min_qty" : 10, + "valid_from": "2017-04-10", + "valid_upto": "2017-04-17", + "min_qty": 2 } ] diff --git a/erpnext/stock/doctype/price_list/price_list.json b/erpnext/stock/doctype/price_list/price_list.json index d160f8acde..8add36d412 100644 --- a/erpnext/stock/doctype/price_list/price_list.json +++ b/erpnext/stock/doctype/price_list/price_list.json @@ -42,6 +42,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -70,6 +71,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -101,6 +103,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -131,6 +134,7 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -160,6 +164,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -189,6 +194,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -219,6 +225,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -247,6 +254,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -278,6 +286,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -292,7 +301,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2017-12-01 16:55:00.243382", + "modified": "2018-03-30 11:56:28.243161", "modified_by": "Administrator", "module": "Stock", "name": "Price List", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index f1afd1c296..82579a5a19 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -261,13 +261,14 @@ def get_basic_details(args, item): "is_fixed_asset": item.is_fixed_asset, "weight_per_unit":item.weight_per_unit, "weight_uom":item.weight_uom, - "last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0 + "last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0, + "transaction_date": args.get("transaction_date") }) if item.enable_deferred_revenue: service_end_date = add_months(args.transaction_date, item.no_of_months) out.update({ - "enable_deferred_revenue": item.enable_deferred_revenue, + "enable_deferred_revenue": item.enable_deferred_revenue, "deferred_revenue_account": get_default_deferred_revenue_account(args, item), "service_start_date": args.transaction_date, "service_end_date": service_end_date @@ -338,11 +339,7 @@ def get_price_list_rate(args, item_doc, out): if meta.get_field("currency") and args.price_list: validate_conversion_rate(args, meta) - price_list_rate = get_price_list_rate_for(args.price_list, item_doc.name) - - # variant - if not price_list_rate and item_doc.variant_of: - price_list_rate = get_price_list_rate_for(args.price_list, item_doc.variant_of) + price_list_rate = get_price_list_rate_for(args, item_doc.name) or 0 # insert in database if not price_list_rate: @@ -350,10 +347,12 @@ def get_price_list_rate(args, item_doc, out): insert_item_price(args) return {} + # variant + if not price_list_rate and item_doc.variant_of: + price_list_rate = get_price_list_rate_for(args, item_doc.variant_of) + out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \ / flt(args.conversion_rate) - if not args.price_list_uom_dependant: - out.price_list_rate = flt(out.price_list_rate * (flt(args.conversion_factor) or 1.0)) if not out.price_list_rate and args.transaction_type=="buying": from erpnext.stock.doctype.item.item import get_last_purchase_details @@ -365,8 +364,8 @@ def insert_item_price(args): if frappe.db.get_value("Price List", args.price_list, "currency") == args.currency \ and cint(frappe.db.get_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing")): if frappe.has_permission("Item Price", "write"): - price_list_rate = args.rate / args.conversion_factor \ - if args.get("conversion_factor") else args.rate + price_list_rate = (args.rate / args.get('conversion_factor') + if args.get("conversion_factor") else args.rate) item_price = frappe.get_doc({ "doctype": "Item Price", @@ -376,7 +375,8 @@ def insert_item_price(args): "price_list_rate": price_list_rate }) - name = frappe.db.get_value('Item Price', {'item_code': args.item_code, 'price_list': args.price_list, 'currency': args.currency}, 'name') + name = frappe.db.get_value('Item Price', + {'item_code': args.item_code, 'price_list': args.price_list, 'currency': args.currency}, 'name') if name: item_price = frappe.get_doc('Item Price', name) @@ -389,9 +389,103 @@ def insert_item_price(args): frappe.msgprint(_("Item Price added for {0} in Price List {1}").format(args.item_code, args.price_list)) -def get_price_list_rate_for(price_list, item_code): - return frappe.db.get_value("Item Price", - {"price_list": price_list, "item_code": item_code}, "price_list_rate") +def get_item_price(args, item_code): + """ + Get name, price_list_rate from Item Price based on conditions + Check if the Derised qty is within the increment of the packing list. + :param args: dict (or frappe._dict) with mandatory fields price_list, uom + optional fields min_qty, transaction_date, customer, supplier + :param item_code: str, Item Doctype field item_code + """ + + args['item_code'] = item_code + conditions = "where (customer is null or customer = '') and (supplier is null or supplier = '')" + if args.get("customer"): + conditions = "where customer=%(customer)s" + + if args.get("supplier"): + conditions = "where supplier=%(supplier)s" + + conditions += """ and item_code=%(item_code)s + and price_list=%(price_list)s + and ifnull(uom, '') in ('', %(uom)s)""" + + if args.get('min_qty'): + conditions += " and ifnull(min_qty, 0) <= %(min_qty)s" + + if args.get('transaction_date'): + conditions += """ and %(transaction_date)s between + ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')""" + + return frappe.db.sql(""" select name, price_list_rate, uom + from `tabItem Price` {conditions} + order by uom desc, min_qty desc """.format(conditions=conditions), args) + +def get_price_list_rate_for(args, item_code): + """ + Return Price Rate based on min_qty of each Item Price Rate.\ + For example, desired qty is 10 and Item Price Rates exists + for min_qty 9 and min_qty 20. It returns Item Price Rate for qty 9 as + the best fit in the range of avaliable min_qtyies + + :param customer: link to Customer DocType + :param supplier: link to Supplier DocType + :param price_list: str (Standard Buying or Standard Selling) + :param item_code: str, Item Doctype field item_code + :param qty: Derised Qty + :param transaction_date: Date of the price + """ + item_price_args = { + "item_code": item_code, + "price_list": args.get('price_list'), + "customer": args.get('customer'), + "supplier": args.get('supplier'), + "uom": args.get('uom'), + "min_qty": args.get('qty'), + "transaction_date": args.get('transaction_date'), + } + + item_price_data = 0 + price_list_rate = get_item_price(item_price_args, item_code) + if price_list_rate: + desired_qty = args.get("qty") + if check_packing_list(price_list_rate[0][0], desired_qty, item_code): + item_price_data = price_list_rate + else: + for field in ["customer", "supplier", "min_qty"]: + del item_price_args[field] + + general_price_list_rate = get_item_price(item_price_args, item_code) + if not general_price_list_rate and args.get("uom") != args.get("stock_uom"): + item_price_args["args"] = args.get("stock_uom") + general_price_list_rate = get_item_price(item_price_args, item_code) + + if general_price_list_rate: + item_price_data = general_price_list_rate + + if item_price_data: + if item_price_data[0][2] == args.get("uom"): + return item_price_data[0][1] + elif not args.get('price_list_uom_dependant'): + return flt(item_price_data[0][1] * flt(args.get("conversion_factor", 1))) + else: + return item_price_data[0][1] + +def check_packing_list(price_list_rate_name, desired_qty, item_code): + """ + Check if the Derised qty is within the increment of the packing list. + :param price_list_rate_name: Name of Item Price + :param desired_qty: Derised Qt + :param item_code: str, Item Doctype field item_code + :param qty: Derised Qt + """ + + item_price = frappe.get_doc("Item Price", price_list_rate_name) + if desired_qty: + packing_increment = desired_qty % item_price.packing_unit + + if packing_increment == 0: + return True def validate_price_list(args): if args.get("price_list"):