Merge pull request #26814 from noahjacob/mr_allowance_feat_v13

feat: over transfer allowance for material transfers (bp #26264)
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 3ad9909..026b85e 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -162,8 +162,15 @@
 						from `tabStock Entry Detail` where material_request = %s
 						and material_request_item = %s and docstatus = 1""",
 						(self.name, d.name))[0][0])
+					mr_qty_allowance = frappe.db.get_single_value('Stock Settings', 'mr_qty_allowance')
 
-					if d.ordered_qty and d.ordered_qty > d.stock_qty:
+					if mr_qty_allowance:
+						allowed_qty = d.qty + (d.qty * (mr_qty_allowance/100))
+						if d.ordered_qty and d.ordered_qty > allowed_qty:
+							frappe.throw(_("The total Issue / Transfer quantity {0} in Material Request {1}  \
+								cannot be greater than allowed requested quantity {2} for Item {3}").format(d.ordered_qty, d.parent, allowed_qty, d.item_code))
+
+					elif d.ordered_qty and d.ordered_qty > d.stock_qty:
 						frappe.throw(_("The total Issue / Transfer quantity {0} in Material Request {1}  \
 							cannot be greater than requested quantity {2} for Item {3}").format(d.ordered_qty, d.parent, d.qty, d.item_code))
 
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 72a3a5e..b4776ba 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -329,6 +329,58 @@
 		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_over_transfer_qty_allowance(self):
+		mr = frappe.new_doc('Material Request')
+		mr.company = "_Test Company"
+		mr.scheduled_date = today()
+		mr.append('items',{
+			"item_code": "_Test FG Item",
+			"item_name": "_Test FG Item",
+			"qty": 10,
+			"schedule_date": today(),
+			"uom": "_Test UOM 1",
+			"warehouse": "_Test Warehouse - _TC"
+		})
+
+		mr.material_request_type = "Material Transfer"
+		mr.insert()
+		mr.submit()
+
+		frappe.db.set_value('Stock Settings', None, 'mr_qty_allowance', 20)
+
+		# map a stock entry
+
+		se_doc = make_stock_entry(mr.name)
+		se_doc.update({
+			"posting_date": today(),
+			"posting_time": "00:00",
+		})
+		se_doc.get("items")[0].update({
+			"qty": 13,
+			"transfer_qty": 12.0,
+			"s_warehouse": "_Test Warehouse - _TC",
+			"t_warehouse": "_Test Warehouse 1 - _TC",
+			"basic_rate": 1.0
+		})
+
+		# make available the qty in _Test Warehouse 1 before transfer
+		sr = frappe.new_doc("Stock Reconciliation")
+		sr.company = "_Test Company"
+		sr.purpose = "Opening Stock"
+		sr.append('items', {
+			"item_code": "_Test FG Item",
+			"warehouse": "_Test Warehouse - _TC",
+			"qty": 20,
+			"valuation_rate": 0.01
+		})
+		sr.insert()
+		sr.submit()
+		se = frappe.copy_doc(se_doc)
+		se.insert()
+		self.assertRaises(frappe.ValidationError)
+		se.items[0].qty = 12
+		se.submit()
+
 	def test_completed_qty_for_over_transfer(self):
 		existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
 		existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index 2a9dcfb..f75cb56 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -18,6 +18,7 @@
   "section_break_9",
   "over_delivery_receipt_allowance",
   "role_allowed_to_over_deliver_receive",
+  "mr_qty_allowance",
   "column_break_12",
   "auto_insert_price_list_rate_if_missing",
   "allow_negative_stock",
@@ -283,6 +284,12 @@
    "fieldtype": "Select",
    "label": "Action If Quality Inspection Is Rejected",
    "options": "Stop\nWarn"
+  },
+  {
+   "description": "The percentage you are allowed to transfer more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed transfer 110 units.",
+   "fieldname": "mr_qty_allowance",
+   "fieldtype": "Float",
+   "label": "Over Transfer Allowance"
   }
  ],
  "icon": "icon-cog",
@@ -290,7 +297,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-07-10 16:17:42.159829",
+ "modified": "2021-06-28 17:02:26.683002",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
@@ -310,4 +317,4 @@
  "sort_field": "modified",
  "sort_order": "ASC",
  "track_changes": 1
-}
\ No newline at end of file
+}