[enhancement] allow user to set warning for multiple items and sales order against multiple purchase orders #3699
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json
index 0ffe121..f6c38b6 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.json
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.json
@@ -1,76 +1,209 @@
 {
+ "allow_copy": 0, 
+ "allow_import": 0, 
+ "allow_rename": 0, 
  "creation": "2013-06-25 11:04:03", 
+ "custom": 0, 
  "description": "Settings for Buying Module", 
  "docstatus": 0, 
  "doctype": "DocType", 
  "document_type": "Other", 
  "fields": [
   {
+   "allow_on_submit": 0, 
    "default": "Supplier Name", 
    "fieldname": "supp_master_name", 
    "fieldtype": "Select", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
    "label": "Supplier Naming By", 
+   "no_copy": 0, 
    "options": "Supplier Name\nNaming Series", 
-   "permlevel": 0
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
   }, 
   {
+   "allow_on_submit": 0, 
    "fieldname": "supplier_type", 
    "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
    "label": "Default Supplier Type", 
+   "no_copy": 0, 
    "options": "Supplier Type", 
-   "permlevel": 0
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
   }, 
   {
+   "allow_on_submit": 0, 
    "fieldname": "buying_price_list", 
    "fieldtype": "Link", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
    "label": "Default Buying Price List", 
+   "no_copy": 0, 
    "options": "Price List", 
-   "permlevel": 0
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
   }, 
   {
+   "allow_on_submit": 0, 
    "fieldname": "column_break_3", 
    "fieldtype": "Column Break", 
-   "permlevel": 0
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
   }, 
   {
-   "fieldname": "maintain_same_rate", 
-   "fieldtype": "Check", 
-   "label": "Maintain same rate throughout purchase cycle", 
-   "permlevel": 0
-  }, 
-  {
+   "allow_on_submit": 0, 
    "fieldname": "po_required", 
    "fieldtype": "Select", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
    "label": "Purchase Order Required", 
+   "no_copy": 0, 
    "options": "No\nYes", 
-   "permlevel": 0
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
   }, 
   {
+   "allow_on_submit": 0, 
    "fieldname": "pr_required", 
    "fieldtype": "Select", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
    "label": "Purchase Receipt Required", 
+   "no_copy": 0, 
    "options": "No\nYes", 
-   "permlevel": 0
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_on_submit": 0, 
+   "fieldname": "maintain_same_rate", 
+   "fieldtype": "Check", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
+   "label": "Maintain same rate throughout purchase cycle", 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_on_submit": 0, 
+   "fieldname": "allow_multiple_items", 
+   "fieldtype": "Check", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
+   "label": "Allow Item to be added multiple times in a transaction", 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
   }
  ], 
+ "hide_heading": 0, 
+ "hide_toolbar": 0, 
  "icon": "icon-cog", 
  "idx": 1, 
+ "in_create": 0, 
+ "in_dialog": 0, 
+ "is_submittable": 0, 
  "issingle": 1, 
- "modified": "2015-02-05 05:11:35.373253", 
+ "istable": 0, 
+ "modified": "2015-08-25 04:55:06.052342", 
  "modified_by": "Administrator", 
  "module": "Buying", 
  "name": "Buying Settings", 
  "owner": "Administrator", 
  "permissions": [
   {
+   "amend": 0, 
+   "apply_user_permissions": 0, 
+   "cancel": 0, 
    "create": 1, 
+   "delete": 0, 
    "email": 1, 
+   "export": 0, 
+   "if_owner": 0, 
+   "import": 0, 
    "permlevel": 0, 
    "print": 1, 
    "read": 1, 
+   "report": 0, 
    "role": "System Manager", 
+   "set_user_permissions": 0, 
    "share": 1, 
+   "submit": 0, 
    "write": 1
   }
- ]
+ ], 
+ "read_only": 0, 
+ "read_only_onload": 0
 }
\ No newline at end of file
diff --git a/erpnext/buying/doctype/purchase_common/purchase_common.py b/erpnext/buying/doctype/purchase_common/purchase_common.py
index 7c55d59..28ea23c 100644
--- a/erpnext/buying/doctype/purchase_common/purchase_common.py
+++ b/erpnext/buying/doctype/purchase_common/purchase_common.py
@@ -3,7 +3,7 @@
 
 from __future__ import unicode_literals
 import frappe
-from frappe.utils import flt, cstr
+from frappe.utils import flt, cstr, cint
 from frappe import _
 
 from erpnext.stock.doctype.item.item import get_last_purchase_details
@@ -72,7 +72,8 @@
 					frappe.throw(_("{0} must be a Purchased or Sub-Contracted Item in row {1}").format(d.item_code, d.idx))
 
 			items.append(cstr(d.item_code))
-		if items and len(items) != len(set(items)):
+		if items and len(items) != len(set(items)) and \
+			not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0):
 			frappe.msgprint(_("Warning: Same item has been entered multiple times."))
 
 
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index b05c009..720255a 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -5,7 +5,7 @@
 import frappe
 import json
 import frappe.utils
-from frappe.utils import cstr, flt, getdate, comma_and
+from frappe.utils import cstr, flt, getdate, comma_and, cint
 from frappe import _
 from frappe.model.mapper import get_mapped_doc
 
@@ -33,7 +33,8 @@
 			so = frappe.db.sql("select name from `tabSales Order` \
 				where ifnull(po_no, '') = %s and name != %s and docstatus < 2\
 				and customer = %s", (self.po_no, self.name, self.customer))
-			if so and so[0][0]:
+			if so and so[0][0] and not \
+				cint(frappe.db.get_single_value("Selling Settings", "allow_against_multiple_purchase_orders")):
 				frappe.msgprint(_("Warning: Sales Order {0} already exists against same Purchase Order number").format(so[0][0]))
 
 	def validate_for_items(self):
@@ -53,8 +54,11 @@
 			tot_avail_qty = frappe.db.sql("select projected_qty from `tabBin` \
 				where item_code = %s and warehouse = %s", (d.item_code,d.warehouse))
 			d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0
+
+		# check for same entry multiple times
 		unique_chk_list = set(check_list)
-		if len(unique_chk_list) != len(check_list):
+		if len(unique_chk_list) != len(check_list) and \
+			not cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
 			frappe.msgprint(_("Warning: Same item has been entered multiple times."))
 
 	def product_bundle_has_stock_item(self, product_bundle):
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index fb239d6..30d7e51 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -207,6 +207,46 @@
    "search_index": 0, 
    "set_only_once": 0, 
    "unique": 0
+  }, 
+  {
+   "allow_on_submit": 0, 
+   "fieldname": "allow_multiple_items", 
+   "fieldtype": "Check", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
+   "label": "Allow Item to be added multiple times in a transaction", 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_on_submit": 0, 
+   "fieldname": "allow_against_multiple_purchase_orders", 
+   "fieldtype": "Check", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "in_filter": 0, 
+   "in_list_view": 0, 
+   "label": "Allow multiple Sales Orders against a Supplier Purchase Order", 
+   "no_copy": 0, 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "read_only": 0, 
+   "report_hide": 0, 
+   "reqd": 0, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "unique": 0
   }
  ], 
  "hide_heading": 0, 
@@ -218,7 +258,7 @@
  "is_submittable": 0, 
  "issingle": 1, 
  "istable": 0, 
- "modified": "2015-08-03 12:59:51.829458", 
+ "modified": "2015-08-25 04:52:06.879614", 
  "modified_by": "Administrator", 
  "module": "Selling", 
  "name": "Selling Settings", 
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 5e11962..a17ee63 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -133,6 +133,9 @@
 
 	def validate_for_items(self):
 		check_list, chk_dupl_itm = [], []
+		if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
+			return
+
 		for d in self.get('items'):
 			e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
 			f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
@@ -249,11 +252,11 @@
 			if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 \
 					and d.warehouse and flt(d['qty']):
 				self.update_reserved_qty(d)
-				
+
 				incoming_rate = 0
 				if cint(self.is_return) and self.return_against and self.docstatus==1:
 					incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
-					
+
 				sl_entries.append(self.get_sl_entries(d, {
 					"actual_qty": -1*flt(d['qty']),
 					"incoming_rate": incoming_rate
@@ -374,8 +377,8 @@
 
 	return doclist
 
-	
+
 @frappe.whitelist()
 def make_sales_return(source_name, target_doc=None):
 	from erpnext.controllers.sales_and_purchase_return import make_return_doc
-	return make_return_doc("Delivery Note", source_name, target_doc)
\ No newline at end of file
+	return make_return_doc("Delivery Note", source_name, target_doc)