Merge pull request #6659 from shreyasp/selling-price-validation
[Minor] Validate purchase price against selling price
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 3f4d12d..68e9155 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -8,6 +8,7 @@
from frappe import _, throw
from erpnext.stock.get_item_details import get_bin_details
from erpnext.stock.utils import get_incoming_rate
+from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.controllers.stock_controller import StockController
@@ -32,6 +33,7 @@
def validate(self):
super(SellingController, self).validate()
self.validate_max_discount()
+ self.validate_selling_price()
check_active_sales_items(self)
def set_missing_values(self, for_validate=False):
@@ -161,6 +163,29 @@
if discount and flt(d.discount_percentage) > discount:
frappe.throw(_("Maxiumm discount for Item {0} is {1}%").format(d.item_code, discount))
+ def validate_selling_price(self):
+ if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
+ return
+
+ for it in self.get("items"):
+ last_purchase_rate, is_stock_item = frappe.db.get_value("Item", it.name, ["last_purchase_rate", "is_stock_item"])
+
+ if flt(it.base_rate) < flt(last_purchase_rate):
+ throw(it.name, last_purchase_rate, "last purchase rate")
+
+ last_valuation_rate = frappe.db.sql("""
+ SELECT valuation_rate FROM `tabStock Ledger Entry` WHERE item_code = %s
+ AND warehouse = %s AND valuation_rate > 0
+ ORDER BY posting_date DESC, posting_time DESC, name DESC LIMIT 1
+ """, (it.item_code, it.warehouse))
+
+ if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate):
+ throw_message(it.name, last_valuation_rate, "valuation rate")
+
+ def throw_message(item_name, rate, ref_rate_field):
+ frappe.throw(_("""Selling price for item {0} is lower than its {1}. Selling price should be atleast {2}""")
+ .format(item_name, ref_rate_field, rate))
+
def get_item_list(self):
il = []
for d in self.get("items"):
@@ -230,7 +255,7 @@
status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status")
if status == "Closed":
frappe.throw(_("Sales Order {0} is {1}").format(d.get(ref_fieldname), status))
-
+
def update_reserved_qty(self):
so_map = {}
for d in self.get("items"):
@@ -310,7 +335,7 @@
item = frappe.db.sql("""select docstatus,
income_account from tabItem where name = %s""",
d.item_code, as_dict=True)[0]
-
+
if getattr(d, "income_account", None) and not item.income_account:
frappe.db.set_value("Item", d.item_code, "income_account",
d.income_account)
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.js b/erpnext/selling/doctype/selling_settings/selling_settings.js
new file mode 100644
index 0000000..cf6fb28
--- /dev/null
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Selling Settings', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index affa38b..dd9bcd7 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -2,29 +2,36 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-06-25 10:25:16",
"custom": 0,
"description": "Settings for Selling Module",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Other",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "Customer Name",
"fieldname": "cust_master_name",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Customer Naming By",
+ "length": 0,
"no_copy": 0,
"options": "Customer Name\nNaming Series",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -36,17 +43,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "campaign_naming_by",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Campaign Naming By",
+ "length": 0,
"no_copy": 0,
"options": "Campaign Name\nNaming Series",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -58,18 +69,22 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"description": "",
"fieldname": "customer_group",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Default Customer Group",
+ "length": 0,
"no_copy": 0,
"options": "Customer Group",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -81,18 +96,22 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"description": "",
"fieldname": "territory",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Default Territory",
+ "length": 0,
"no_copy": 0,
"options": "Territory",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -104,17 +123,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "selling_price_list",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Default Price List",
+ "length": 0,
"no_copy": 0,
"options": "Price List",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -126,15 +149,19 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -146,17 +173,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "so_required",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Sales Order Required",
+ "length": 0,
"no_copy": 0,
"options": "No\nYes",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -168,17 +199,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "dn_required",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Delivery Note Required",
+ "length": 0,
"no_copy": 0,
"options": "No\nYes",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -190,16 +225,20 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "maintain_same_sales_rate",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Maintain Same Rate Throughout Sales Cycle",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -211,16 +250,20 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "editable_price_list_rate",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Allow user to edit Price List Rate in transactions",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -232,17 +275,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "allow_multiple_items",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Allow Item to be added multiple times in a transaction",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -254,17 +301,47 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "allow_against_multiple_purchase_orders",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Allow multiple Sales Orders against a Customer's Purchase Order",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "validate_selling_price",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Validate Selling Price for Item against Purchase Rate or Valuation Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -277,12 +354,14 @@
"hide_toolbar": 0,
"icon": "icon-cog",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
- "modified": "2015-08-27 02:42:56.512460",
+ "max_attachments": 0,
+ "modified": "2016-10-20 08:17:45.621151",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling Settings",
@@ -298,6 +377,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -309,6 +389,10 @@
"write": 1
}
],
+ "quick_entry": 0,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
}
\ No newline at end of file