fix: requested qty for customer provided item and rate for sales (#21299)

* fix: requested qty for customer provided item and rate for sales

* fix: requested qty for material transfer

* fix: customer provided item can be sales item

* fix: requested qty test cases
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 0e54b62..a2819af 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1926,16 +1926,6 @@
 		item.taxes = []
 		item.save()
 
-	def test_customer_provided_parts_si(self):
-		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
-		si = create_sales_invoice(item_code='CUST-0987', rate=0)
-		self.assertEqual(si.get("items")[0].allow_zero_valuation_rate, 1)
-		self.assertEqual(si.get("items")[0].amount, 0)
-
-		# test if Sales Invoice with rate is allowed
-		si2 = create_sales_invoice(item_code='CUST-0987', do_not_save=True)
-		self.assertRaises(frappe.ValidationError, si2.save)
-
 def create_sales_invoice(**args):
 	si = frappe.new_doc("Sales Invoice")
 	args = frappe._dict(args)
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 4037f2f..55a2c43 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -383,9 +383,6 @@
 			# Customer Provided parts will have zero valuation rate
 			if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
 				d.allow_zero_valuation_rate = 1
-				if d.parenttype in ["Delivery Note", "Sales Invoice"] and d.rate:
-					frappe.throw(_("Row #{0}: {1} cannot have {2} as it is a Customer Provided Item")
-						.format(d.idx, frappe.bold(d.item_code), frappe.bold("Rate")))
 
 def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
 		warehouse_account=None, company=None):
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 47a72b2..d7a93fb 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -434,15 +434,6 @@
 		update_delivery_note_status(dn.name, "Closed")
 		self.assertEqual(frappe.db.get_value("Delivery Note", dn.name, "Status"), "Closed")
 
-	def test_customer_provided_parts_dn(self):
-		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
-		dn = create_delivery_note(item_code='CUST-0987', rate=0)
-		self.assertEqual(dn.get("items")[0].allow_zero_valuation_rate, 1)
-
-		# test if Delivery Note with rate is allowed against Customer Provided Item
-		dn2 = create_delivery_note(item_code='CUST-0987', do_not_save=True)
-		self.assertRaises(frappe.ValidationError, dn2.save)
-
 	def test_dn_billing_status_case1(self):
 		# SO -> DN -> SI
 		so = make_sales_order()
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 30c47c3..19924b1 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -7,7 +7,8 @@
 from __future__ import unicode_literals
 import frappe, unittest, erpnext
 from frappe.utils import flt, today
-from erpnext.stock.doctype.material_request.material_request import raise_work_orders
+from erpnext.stock.doctype.material_request.material_request \
+	import raise_work_orders, make_stock_entry, make_purchase_order, make_supplier_quotation
 from erpnext.stock.doctype.item.test_item import create_item
 
 class TestMaterialRequest(unittest.TestCase):
@@ -15,8 +16,6 @@
 		erpnext.set_perpetual_inventory(0)
 
 	def test_make_purchase_order(self):
-		from erpnext.stock.doctype.material_request.material_request import make_purchase_order
-
 		mr = frappe.copy_doc(test_records[0]).insert()
 
 		self.assertRaises(frappe.ValidationError, make_purchase_order,
@@ -30,8 +29,6 @@
 		self.assertEqual(len(po.get("items")), len(mr.get("items")))
 
 	def test_make_supplier_quotation(self):
-		from erpnext.stock.doctype.material_request.material_request import make_supplier_quotation
-
 		mr = frappe.copy_doc(test_records[0]).insert()
 
 		self.assertRaises(frappe.ValidationError, make_supplier_quotation, mr.name)
@@ -45,12 +42,9 @@
 
 
 	def test_make_stock_entry(self):
-		from erpnext.stock.doctype.material_request.material_request import make_stock_entry
-
 		mr = frappe.copy_doc(test_records[0]).insert()
 
-		self.assertRaises(frappe.ValidationError, make_stock_entry,
-			mr.name)
+		self.assertRaises(frappe.ValidationError, make_stock_entry, mr.name)
 
 		mr = frappe.get_doc("Material Request", mr.name)
 		mr.material_request_type = "Material Transfer"
@@ -62,40 +56,40 @@
 
 	def _insert_stock_entry(self, qty1, qty2, warehouse = None ):
 		se = frappe.get_doc({
-				"company": "_Test Company",
-				"doctype": "Stock Entry",
-				"posting_date": "2013-03-01",
-				"posting_time": "00:00:00",
-				"purpose": "Material Receipt",
-				"items": [
-					{
-						"conversion_factor": 1.0,
-						"doctype": "Stock Entry Detail",
-						"item_code": "_Test Item Home Desktop 100",
-						"parentfield": "items",
-						"basic_rate": 100,
-						"qty": qty1,
-						"stock_uom": "_Test UOM 1",
-						"transfer_qty": qty1,
-						"uom": "_Test UOM 1",
-						"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
-						"cost_center": "_Test Cost Center - _TC"
-					},
-					{
-						"conversion_factor": 1.0,
-						"doctype": "Stock Entry Detail",
-						"item_code": "_Test Item Home Desktop 200",
-						"parentfield": "items",
-						"basic_rate": 100,
-						"qty": qty2,
-						"stock_uom": "_Test UOM 1",
-						"transfer_qty": qty2,
-						"uom": "_Test UOM 1",
-						"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
-						"cost_center": "_Test Cost Center - _TC"
-					}
-				]
-			})
+			"company": "_Test Company",
+			"doctype": "Stock Entry",
+			"posting_date": "2013-03-01",
+			"posting_time": "00:00:00",
+			"purpose": "Material Receipt",
+			"items": [
+				{
+					"conversion_factor": 1.0,
+					"doctype": "Stock Entry Detail",
+					"item_code": "_Test Item Home Desktop 100",
+					"parentfield": "items",
+					"basic_rate": 100,
+					"qty": qty1,
+					"stock_uom": "_Test UOM 1",
+					"transfer_qty": qty1,
+					"uom": "_Test UOM 1",
+					"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
+					"cost_center": "_Test Cost Center - _TC"
+				},
+				{
+					"conversion_factor": 1.0,
+					"doctype": "Stock Entry Detail",
+					"item_code": "_Test Item Home Desktop 200",
+					"parentfield": "items",
+					"basic_rate": 100,
+					"qty": qty2,
+					"stock_uom": "_Test UOM 1",
+					"transfer_qty": qty2,
+					"uom": "_Test UOM 1",
+					"t_warehouse": warehouse or "_Test Warehouse 1 - _TC",
+					"cost_center": "_Test Cost Center - _TC"
+				}
+			]
+		})
 
 		se.set_stock_entry_type()
 		se.insert()
@@ -198,14 +192,7 @@
 		mr.insert()
 		mr.submit()
 
-		# check if per complete is None
-		mr.load_from_db()
-		self.assertEqual(mr.per_ordered, 0)
-		self.assertEqual(mr.get("items")[0].ordered_qty, 0)
-		self.assertEqual(mr.get("items")[1].ordered_qty, 0)
-
 		# map a purchase order
-		from erpnext.stock.doctype.material_request.material_request import make_purchase_order
 		po_doc = make_purchase_order(mr.name)
 		po_doc.supplier = "_Test Supplier"
 		po_doc.transaction_date = "2013-07-07"
@@ -276,10 +263,8 @@
 		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
 		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
 
-		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0)
-		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
-
-		from erpnext.stock.doctype.material_request.material_request import make_stock_entry
+		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
+		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
 
 		# map a stock entry
 		se_doc = make_stock_entry(mr.name)
@@ -331,8 +316,8 @@
 		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
 		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
 
-		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 27.0)
-		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 1.5)
+		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
+		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
 
 		# check if per complete is as expected for Stock Entry cancelled
 		se.cancel()
@@ -344,8 +329,8 @@
 		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
 		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
 
-		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0)
-		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
+		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
+		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
 
 	def test_completed_qty_for_over_transfer(self):
 		existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
@@ -357,14 +342,7 @@
 		mr.insert()
 		mr.submit()
 
-		# check if per complete is None
-		mr.load_from_db()
-		self.assertEqual(mr.per_ordered, 0)
-		self.assertEqual(mr.get("items")[0].ordered_qty, 0)
-		self.assertEqual(mr.get("items")[1].ordered_qty, 0)
-
 		# map a stock entry
-		from erpnext.stock.doctype.material_request.material_request import make_stock_entry
 
 		se_doc = make_stock_entry(mr.name)
 		se_doc.update({
@@ -425,8 +403,8 @@
 		current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
 		current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
 
-		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0)
-		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
+		self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
+		self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
 
 	def test_incorrect_mapping_of_stock_entry(self):
 		# submit material request of type Transfer
@@ -435,9 +413,6 @@
 		mr.insert()
 		mr.submit()
 
-		# map a stock entry
-		from erpnext.stock.doctype.material_request.material_request import make_stock_entry
-
 		se_doc = make_stock_entry(mr.name)
 		se_doc.update({
 			"posting_date": "2013-03-01",
@@ -468,8 +443,6 @@
 		mr.insert()
 		mr.submit()
 
-		# map a stock entry
-		from erpnext.stock.doctype.material_request.material_request import make_stock_entry
 		se_doc = make_stock_entry(mr.name)
 		self.assertEqual(se_doc.get("items")[0].s_warehouse, "_Test Warehouse - _TC")
 
@@ -483,8 +456,6 @@
 		return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "indented_qty"))
 
 	def test_make_stock_entry_for_material_issue(self):
-		from erpnext.stock.doctype.material_request.material_request import make_stock_entry
-
 		mr = frappe.copy_doc(test_records[0]).insert()
 
 		self.assertRaises(frappe.ValidationError, make_stock_entry,
@@ -503,8 +474,6 @@
 			return flt(frappe.db.get_value("Bin", {"item_code": "_Test Item Home Desktop 100",
 				"warehouse": "_Test Warehouse - _TC"}, "indented_qty"))
 
-		from erpnext.stock.doctype.material_request.material_request import make_stock_entry
-
 		existing_requested_qty = _get_requested_qty()
 
 		mr = frappe.copy_doc(test_records[0])
@@ -594,8 +563,6 @@
 
 
 	def test_multi_uom_for_purchase(self):
-		from erpnext.stock.doctype.material_request.material_request import make_purchase_order
-
 		mr = frappe.copy_doc(test_records[0])
 		mr.material_request_type = 'Purchase'
 		item = mr.items[0]
@@ -637,7 +604,6 @@
 		self.assertEqual(mr.per_ordered, 100)
 
 	def test_customer_provided_parts_mr(self):
-		from erpnext.stock.doctype.material_request.material_request import make_stock_entry
 		create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
 		existing_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC")
 
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index d9434e3..5697315 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -113,30 +113,28 @@
 	return flt(reserved_qty[0][0]) if reserved_qty else 0
 
 def get_indented_qty(item_code, warehouse):
-	# Ordered Qty is maintained in purchase UOM
-	requested_qty_for_purchase_and_manufacture = frappe.db.sql("""
+	# Ordered Qty is always maintained in stock UOM
+	inward_qty = frappe.db.sql("""
 		select sum(mr_item.stock_qty - mr_item.ordered_qty)
 		from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
 		where mr_item.item_code=%s and mr_item.warehouse=%s
-			and mr.material_request_type in ('Purchase', 'Manufacture')
+			and mr.material_request_type in ('Purchase', 'Manufacture', 'Customer Provided', 'Material Transfer')
 			and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name
 			and mr.status!='Stopped' and mr.docstatus=1
 	""", (item_code, warehouse))
-	requested_qty_for_purchase_and_manufacture = flt(requested_qty_for_purchase_and_manufacture[0][0]) \
-		if requested_qty_for_purchase_and_manufacture else 0
+	inward_qty = flt(inward_qty[0][0]) if inward_qty else 0
 
-	requested_qty_for_issue_and_transfer = frappe.db.sql("""
+	outward_qty = frappe.db.sql("""
 		select sum(mr_item.stock_qty - mr_item.ordered_qty)
 		from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
 		where mr_item.item_code=%s and mr_item.warehouse=%s
-			and mr.material_request_type in ('Material Issue', 'Material Transfer')
+			and mr.material_request_type = 'Material Issue'
 			and mr_item.stock_qty > mr_item.ordered_qty and mr_item.parent=mr.name
 			and mr.status!='Stopped' and mr.docstatus=1
 	""", (item_code, warehouse))
-	requested_qty_for_issue_and_transfer = flt(requested_qty_for_issue_and_transfer[0][0]) \
-		if requested_qty_for_issue_and_transfer else 0
+	outward_qty = flt(outward_qty[0][0]) if outward_qty else 0
 
-	requested_qty = requested_qty_for_purchase_and_manufacture - requested_qty_for_issue_and_transfer
+	requested_qty = inward_qty - outward_qty
 
 	return requested_qty