Merge branch 'develop' of https://github.com/frappe/erpnext into lcv_multicurrency
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 0b98274..14534ef 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -353,7 +353,8 @@
 	make_purchase_receipt: function() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
-			frm: cur_frm
+			frm: cur_frm,
+			freeze_message: __("Creating Purchase Receipt ...")
 		})
 	},
 
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index c7efb8a..d32e98e 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -123,8 +123,8 @@
 		if self.is_subcontracted == "Yes":
 			for item in self.items:
 				if not item.bom:
-					frappe.throw(_("BOM is not specified for subcontracting item {0} at row {1}"\
-						.format(item.item_code, item.idx)))
+					frappe.throw(_("BOM is not specified for subcontracting item {0} at row {1}")
+						.format(item.item_code, item.idx))
 
 	def get_schedule_dates(self):
 		for d in self.get('items'):
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 4399976..e0fcf47 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -6,6 +6,7 @@
 from frappe.utils import cint, flt, cstr, get_link_to_form, today, getdate
 from frappe import _
 import frappe.defaults
+from collections import defaultdict
 from erpnext.accounts.utils import get_fiscal_year, check_if_stock_and_account_balance_synced
 from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
 from erpnext.controllers.accounts_controller import AccountsController
@@ -23,6 +24,7 @@
 			self.validate_inspection()
 		self.validate_serialized_batch()
 		self.validate_customer_provided_item()
+		self.validate_putaway_capacity()
 
 	def make_gl_entries(self, gl_entries=None, from_repost=False):
 		if self.docstatus == 2:
@@ -391,6 +393,58 @@
 			if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
 				d.allow_zero_valuation_rate = 1
 
+	def validate_putaway_capacity(self):
+		# if over receipt is attempted while 'apply putaway rule' is disabled
+		# and if rule was applied on the transaction, validate it.
+		from erpnext.stock.doctype.putaway_rule.putaway_rule import get_available_putaway_capacity
+		valid_doctype = self.doctype in ("Purchase Receipt", "Stock Entry", "Purchase Invoice",
+			"Stock Reconciliation")
+
+		if self.doctype == "Purchase Invoice" and self.get("update_stock") == 0:
+			valid_doctype = False
+
+		if valid_doctype:
+			rule_map = defaultdict(dict)
+			for item in self.get("items"):
+				warehouse_field = "t_warehouse" if self.doctype == "Stock Entry" else "warehouse"
+				rule = frappe.db.get_value("Putaway Rule",
+					{
+						"item_code": item.get("item_code"),
+						"warehouse": item.get(warehouse_field)
+					},
+					["name", "disable"], as_dict=True)
+				if rule:
+					if rule.get("disabled"): continue # dont validate for disabled rule
+
+					if self.doctype == "Stock Reconciliation":
+						stock_qty = flt(item.qty)
+					else:
+						stock_qty = flt(item.transfer_qty) if self.doctype == "Stock Entry" else flt(item.stock_qty)
+
+					rule_name = rule.get("name")
+					if not rule_map[rule_name]:
+						rule_map[rule_name]["warehouse"] = item.get(warehouse_field)
+						rule_map[rule_name]["item"] = item.get("item_code")
+						rule_map[rule_name]["qty_put"] = 0
+						rule_map[rule_name]["capacity"] = get_available_putaway_capacity(rule_name)
+					rule_map[rule_name]["qty_put"] += flt(stock_qty)
+
+			for rule, values in rule_map.items():
+				if flt(values["qty_put"]) > flt(values["capacity"]):
+					message = self.prepare_over_receipt_message(rule, values)
+					frappe.throw(msg=message, title=_("Over Receipt"))
+
+	def prepare_over_receipt_message(self, rule, values):
+		message = _("{0} qty of Item {1} is being received into Warehouse {2} with capacity {3}.") \
+			.format(
+				frappe.bold(values["qty_put"]), frappe.bold(values["item"]),
+				frappe.bold(values["warehouse"]), frappe.bold(values["capacity"])
+			)
+		message += "<br><br>"
+		rule_link = frappe.utils.get_link_to_form("Putaway Rule", rule)
+		message += _(" Please adjust the qty or edit {0} to proceed.").format(rule_link)
+		return message
+
 	def repost_future_sle_and_gle(self):
 		args = frappe._dict({
 			"posting_date": self.posting_date,
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 2f15cbc..f0212db 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -55,6 +55,8 @@
 	"js/item-dashboard.min.js": [
 		"stock/dashboard/item_dashboard.html",
 		"stock/dashboard/item_dashboard_list.html",
-		"stock/dashboard/item_dashboard.js"
+		"stock/dashboard/item_dashboard.js",
+		"stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html",
+		"stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html"
 	]
 }
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index db85a3e..0c6bcad 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -516,4 +516,4 @@
 	});
 
 	dialog.show();
-}
+}
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index f144c29..2628099 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -2025,3 +2025,35 @@
 		}, show_dialog);
 	});
 }
+
+erpnext.apply_putaway_rule = (frm, purpose=null) => {
+	if (!frm.doc.company) {
+		frappe.throw({message: __("Please select a Company first."), title: __("Mandatory")});
+	}
+	if (!frm.doc.items.length) return;
+
+	frappe.call({
+		method: "erpnext.stock.doctype.putaway_rule.putaway_rule.apply_putaway_rule",
+		args: {
+			doctype: frm.doctype,
+			items: frm.doc.items,
+			company: frm.doc.company,
+			sync: true,
+			purpose: purpose
+		},
+		callback: (result) => {
+			if (!result.exc && result.message) {
+				frm.clear_table("items");
+
+				let items =  result.message;
+				items.forEach((row) => {
+					delete row["name"]; // dont overwrite name from server side
+					let child = frm.add_child("items");
+					Object.assign(child, row);
+					frm.script_manager.trigger("qty", child.doctype, child.name);
+				});
+				frm.get_field("items").grid.refresh();
+			}
+		}
+	});
+};
\ No newline at end of file
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.json b/erpnext/regional/doctype/datev_settings/datev_settings.json
index 713e8e3..f60de4c 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.json
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.json
@@ -7,13 +7,14 @@
  "engine": "InnoDB",
  "field_order": [
   "client",
-  "account_number_length",
-  "column_break_2",
   "client_number",
-  "section_break_4",
+  "column_break_2",
+  "consultant_number",
   "consultant",
+  "section_break_4",
+  "account_number_length",
   "column_break_6",
-  "consultant_number"
+  "temporary_against_account_number"
  ],
  "fields": [
   {
@@ -66,10 +67,17 @@
    "fieldtype": "Int",
    "label": "Account Number Length",
    "reqd": 1
+  },
+  {
+   "allow_in_quick_entry": 1,
+   "fieldname": "temporary_against_account_number",
+   "fieldtype": "Data",
+   "label": "Temporary Against Account Number",
+   "reqd": 1
   }
  ],
  "links": [],
- "modified": "2020-11-05 17:52:11.674329",
+ "modified": "2020-11-19 19:00:09.088816",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "DATEV Settings",
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
index 1e39c57..cbc9478 100644
--- a/erpnext/regional/report/datev/datev.py
+++ b/erpnext/regional/report/datev/datev.py
@@ -96,6 +96,8 @@
 	"""Entry point for frappe."""
 	data = []
 	if filters and validate(filters):
+		fn = 'temporary_against_account_number'
+		filters[fn] = frappe.get_value('DATEV Settings', filters.get('company'), fn)
 		data = get_transactions(filters, as_dict=0)
 
 	return COLUMNS, data
@@ -156,11 +158,11 @@
 			case gl.debit when 0 then 'H' else 'S' end as 'Soll/Haben-Kennzeichen',
 
 			/* account number or, if empty, party account number */
-			coalesce(acc.account_number, acc_pa.account_number) as 'Konto',
+			acc.account_number as 'Konto',
 
 			/* against number or, if empty, party against number */
-			coalesce(acc_against.account_number, acc_against_pa.account_number) as 'Gegenkonto (ohne BU-Schlüssel)',
-			
+			%(temporary_against_account_number)s as 'Gegenkonto (ohne BU-Schlüssel)',
+
 			gl.posting_date as 'Belegdatum',
 			gl.voucher_no as 'Belegfeld 1',
 			LEFT(gl.remarks, 60) as 'Buchungstext',
@@ -171,27 +173,10 @@
 
 		FROM `tabGL Entry` gl
 
-			/* Statistisches Konto (Debitoren/Kreditoren) */
-			left join `tabParty Account` pa
-			on gl.against = pa.parent
-			and gl.company = pa.company
-
 			/* Kontonummer */
 			left join `tabAccount` acc 
 			on gl.account = acc.name
 
-			/* Gegenkonto-Nummer */
-			left join `tabAccount` acc_against 
-			on gl.against = acc_against.name
-
-			/* Statistische Kontonummer */
-			left join `tabAccount` acc_pa
-			on pa.account = acc_pa.name
-
-			/* Statistische Gegenkonto-Nummer */
-			left join `tabAccount` acc_against_pa 
-			on pa.account = acc_against_pa.name
-
 		WHERE gl.company = %(company)s 
 		AND DATE(gl.posting_date) >= %(from_date)s
 		AND DATE(gl.posting_date) <= %(to_date)s
@@ -347,7 +332,9 @@
 	coa = frappe.get_value('Company', company, 'chart_of_accounts')
 	filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
 
-	filters['account_number_length'] = frappe.get_value('DATEV Settings', company, 'account_number_length')
+	datev_settings = frappe.get_doc('DATEV Settings', company)
+	filters['account_number_length'] = datev_settings.account_number_length
+	filters['temporary_against_account_number'] = datev_settings.temporary_against_account_number
 
 	transactions = get_transactions(filters)
 	account_names = get_account_names(filters)
diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py
index 9529923..59b878e 100644
--- a/erpnext/regional/report/datev/test_datev.py
+++ b/erpnext/regional/report/datev/test_datev.py
@@ -126,7 +126,8 @@
 			"doctype": "DATEV Settings",
 			"client": company.name,
 			"client_number": "12345",
-			"consultant_number": "67890"
+			"consultant_number": "67890",
+			"temporary_against_account_number": "9999"
 		}).insert()
 
 
@@ -137,7 +138,8 @@
 		self.filters = {
 			"company": self.company.name,
 			"from_date": today(),
-			"to_date": today()
+			"to_date": today(),
+			"temporary_against_account_number": "9999"
 		}
 
 		make_datev_settings(self.company)
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index 9bd03d4..d3c442d 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -24,6 +24,16 @@
 			handle_move_add($(this), "Add")
 		});
 
+		this.content.on('click', '.btn-edit', function() {
+			let item = unescape($(this).attr('data-item'));
+			let warehouse = unescape($(this).attr('data-warehouse'));
+			let company = unescape($(this).attr('data-company'));
+			frappe.db.get_value('Putaway Rule',
+				{'item_code': item, 'warehouse': warehouse, 'company': company}, 'name', (r) => {
+					frappe.set_route("Form", "Putaway Rule", r.name);
+				});
+		});
+
 		function handle_move_add(element, action) {
 			let item = unescape(element.attr('data-item'));
 			let warehouse = unescape(element.attr('data-warehouse'));
@@ -59,7 +69,7 @@
 
 		// more
 		this.content.find('.btn-more').on('click', function() {
-			me.start += 20;
+			me.start += me.page_length;
 			me.refresh();
 		});
 
@@ -69,33 +79,43 @@
 			this.before_refresh();
 		}
 
+		let args = {
+			item_code: this.item_code,
+			warehouse: this.warehouse,
+			parent_warehouse: this.parent_warehouse,
+			item_group: this.item_group,
+			company: this.company,
+			start: this.start,
+			sort_by: this.sort_by,
+			sort_order: this.sort_order
+		};
+
 		var me = this;
 		frappe.call({
-			method: 'erpnext.stock.dashboard.item_dashboard.get_data',
-			args: {
-				item_code: this.item_code,
-				warehouse: this.warehouse,
-				item_group: this.item_group,
-				start: this.start,
-				sort_by: this.sort_by,
-				sort_order: this.sort_order,
-			},
+			method: this.method,
+			args: args,
 			callback: function(r) {
 				me.render(r.message);
 			}
 		});
 	},
 	render: function(data) {
-		if(this.start===0) {
+		if (this.start===0) {
 			this.max_count = 0;
 			this.result.empty();
 		}
 
-		var context = this.get_item_dashboard_data(data, this.max_count, true);
+		let context = "";
+		if (this.page_name === "warehouse-capacity-summary") {
+			context = this.get_capacity_dashboard_data(data);
+		} else {
+			context = this.get_item_dashboard_data(data, this.max_count, true);
+		}
+
 		this.max_count = this.max_count;
 
 		// show more button
-		if(data && data.length===21) {
+		if (data && data.length===(this.page_length + 1)) {
 			this.content.find('.more').removeClass('hidden');
 
 			// remove the last element
@@ -106,12 +126,17 @@
 
 		// If not any stock in any warehouses provide a message to end user
 		if (context.data.length > 0) {
-			$(frappe.render_template('item_dashboard_list', context)).appendTo(this.result);
+			this.content.find('.result').css('text-align', 'unset');
+			$(frappe.render_template(this.template, context)).appendTo(this.result);
 		} else {
-			var message = __("Currently no stock available in any warehouse");
-			$(`<span class='text-muted small'>  ${message} </span>`).appendTo(this.result);
+			var message = __("No Stock Available Currently");
+			this.content.find('.result').css('text-align', 'center');
+
+			$(`<div class='text-muted' style='margin: 20px 5px; font-weight: lighter;'>
+				${message} </div>`).appendTo(this.result);
 		}
 	},
+
 	get_item_dashboard_data: function(data, max_count, show_item) {
 		if(!max_count) max_count = 0;
 		if(!data) data = [];
@@ -128,8 +153,8 @@
 				d.total_reserved, max_count);
 		});
 
-		var can_write = 0;
-		if(frappe.boot.user.can_write.indexOf("Stock Entry")>=0){
+		let can_write = 0;
+		if (frappe.boot.user.can_write.indexOf("Stock Entry") >= 0) {
 			can_write = 1;
 		}
 
@@ -138,9 +163,27 @@
 			max_count: max_count,
 			can_write:can_write,
 			show_item: show_item || false
+		};
+	},
+
+	get_capacity_dashboard_data: function(data) {
+		if (!data) data = [];
+
+		data.forEach(function(d) {
+			d.color =  d.percent_occupied >=80 ? "#f8814f" : "#2490ef";
+		});
+
+		let can_write = 0;
+		if (frappe.boot.user.can_write.indexOf("Putaway Rule") >= 0) {
+			can_write = 1;
 		}
+
+		return {
+			data: data,
+			can_write: can_write,
+		};
 	}
-})
+});
 
 erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callback) {
 	var dialog = new frappe.ui.Dialog({
diff --git a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
new file mode 100644
index 0000000..ab573e5
--- /dev/null
+++ b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py
@@ -0,0 +1,69 @@
+from __future__ import unicode_literals
+
+import frappe
+from frappe.model.db_query import DatabaseQuery
+from frappe.utils import nowdate
+from frappe.utils import flt
+from erpnext.stock.utils import get_stock_balance
+
+@frappe.whitelist()
+def get_data(item_code=None, warehouse=None, parent_warehouse=None,
+	company=None, start=0, sort_by="stock_capacity", sort_order="desc"):
+	"""Return data to render the warehouse capacity dashboard."""
+	filters = get_filters(item_code, warehouse, parent_warehouse, company)
+
+	no_permission, filters = get_warehouse_filter_based_on_permissions(filters)
+	if no_permission:
+		return []
+
+	capacity_data = get_warehouse_capacity_data(filters, start)
+
+	asc_desc = -1 if sort_order == "desc" else 1
+	capacity_data = sorted(capacity_data, key = lambda i: (i[sort_by] * asc_desc))
+
+	return capacity_data
+
+def get_filters(item_code=None, warehouse=None, parent_warehouse=None,
+	company=None):
+	filters = [['disable', '=', 0]]
+	if item_code:
+		filters.append(['item_code', '=', item_code])
+	if warehouse:
+		filters.append(['warehouse', '=', warehouse])
+	if company:
+		filters.append(['company', '=', company])
+	if parent_warehouse:
+		lft, rgt = frappe.db.get_value("Warehouse", parent_warehouse, ["lft", "rgt"])
+		warehouses = frappe.db.sql_list("""
+			select name from `tabWarehouse`
+			where lft >=%s and rgt<=%s
+		""", (lft, rgt))
+		filters.append(['warehouse', 'in', warehouses])
+	return filters
+
+def get_warehouse_filter_based_on_permissions(filters):
+	try:
+		# check if user has any restrictions based on user permissions on warehouse
+		if DatabaseQuery('Warehouse', user=frappe.session.user).build_match_conditions():
+			filters.append(['warehouse', 'in', [w.name for w in frappe.get_list('Warehouse')]])
+		return False, filters
+	except frappe.PermissionError:
+		# user does not have access on warehouse
+		return True, []
+
+def get_warehouse_capacity_data(filters, start):
+	capacity_data = frappe.db.get_all('Putaway Rule',
+		fields=['item_code', 'warehouse','stock_capacity', 'company'],
+		filters=filters,
+		limit_start=start,
+		limit_page_length='11'
+	)
+
+	for entry in capacity_data:
+		balance_qty = get_stock_balance(entry.item_code, entry.warehouse, nowdate()) or 0
+		entry.update({
+			'actual_qty': balance_qty,
+			'percent_occupied': flt((flt(balance_qty) / flt(entry.stock_capacity)) * 100, 0)
+		})
+
+	return capacity_data
\ No newline at end of file
diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json
index 9068e33..74cc42d 100644
--- a/erpnext/stock/desk_page/stock/stock.json
+++ b/erpnext/stock/desk_page/stock/stock.json
@@ -8,12 +8,12 @@
   {
    "hidden": 0,
    "label": "Stock Transactions",
-   "links": "[\n     {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Material Request\",\n        \"name\": \"Material Request\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Entry\",\n        \"name\": \"Stock Entry\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Customer\"\n        ],\n        \"label\": \"Delivery Note\",\n        \"name\": \"Delivery Note\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Supplier\"\n        ],\n        \"label\": \"Purchase Receipt\",\n        \"name\": \"Purchase Receipt\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Pick List\",\n        \"name\": \"Pick List\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Shipment\",\n        \"name\": \"Shipment\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Delivery Trip\",\n        \"name\": \"Delivery Trip\",\n        \"type\": \"doctype\"\n    }\n]"
+   "links": "[\n     {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Material Request\",\n        \"name\": \"Material Request\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Entry\",\n        \"name\": \"Stock Entry\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Customer\"\n        ],\n        \"label\": \"Delivery Note\",\n        \"name\": \"Delivery Note\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Supplier\"\n        ],\n        \"label\": \"Purchase Receipt\",\n        \"name\": \"Purchase Receipt\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Pick List\",\n        \"name\": \"Pick List\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Putaway Rule\",\n        \"name\": \"Putaway Rule\",\n        \"onboard\": 1,\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Shipment\",\n        \"name\": \"Shipment\",\n        \"type\": \"doctype\"\n    },\n    {\n        \"label\": \"Delivery Trip\",\n        \"name\": \"Delivery Trip\",\n        \"type\": \"doctype\"\n    }\n]"
   },
   {
    "hidden": 0,
    "label": "Stock Reports",
-   "links": "[\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Stock Ledger Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Ledger\",\n        \"name\": \"Stock Ledger\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Stock Ledger Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Balance\",\n        \"name\": \"Stock Balance\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Projected Qty\",\n        \"name\": \"Stock Projected Qty\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Summary\",\n        \"name\": \"stock-balance\",\n        \"type\": \"page\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Ageing\",\n        \"name\": \"Stock Ageing\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Item Price Stock\",\n        \"name\": \"Item Price Stock\",\n        \"type\": \"report\"\n    }\n]"
+   "links": "[\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Stock Ledger Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Ledger\",\n        \"name\": \"Stock Ledger\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Stock Ledger Entry\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Balance\",\n        \"name\": \"Stock Balance\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Projected Qty\",\n        \"name\": \"Stock Projected Qty\",\n        \"onboard\": 1,\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"label\": \"Stock Summary\",\n        \"name\": \"stock-balance\",\n        \"type\": \"page\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Stock Ageing\",\n        \"name\": \"Stock Ageing\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\"\n        ],\n        \"doctype\": \"Item\",\n        \"is_query_report\": true,\n        \"label\": \"Item Price Stock\",\n        \"name\": \"Item Price Stock\",\n        \"type\": \"report\"\n    },\n    {\n        \"dependencies\": [\n            \"Item\",\n            \"Putaway Rule\"\n        ],\n        \"label\": \"Warehouse Capacity Summary\",\n        \"name\": \"warehouse-capacity-summary\",\n        \"type\": \"page\"\n    }\n]"
   },
   {
    "hidden": 0,
@@ -58,7 +58,7 @@
  "idx": 0,
  "is_standard": 1,
  "label": "Stock",
- "modified": "2020-12-02 15:47:41.532942",
+ "modified": "2020-12-08 15:47:41.532942",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock",
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index faeeb57..ec32b0f 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -384,7 +384,10 @@
 					<a href="#stock-balance">' + __("Stock Levels") + '</a></h5>');
 				erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({
 					parent: section,
-					item_code: frm.doc.name
+					item_code: frm.doc.name,
+					page_length: 20,
+					method: 'erpnext.stock.dashboard.item_dashboard.get_data',
+					template: 'item_dashboard_list'
 				});
 				erpnext.item.item_dashboard.refresh();
 			});
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index d998729..57cc350 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -216,6 +216,10 @@
 		});
 	},
 
+	apply_putaway_rule: function() {
+		if (this.frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(this.frm);
+	}
+
 });
 
 // for backward compatibility: combine new and previous states
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 5bb3095..55f0f0c 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -21,6 +21,7 @@
   "posting_date",
   "posting_time",
   "set_posting_time",
+  "apply_putaway_rule",
   "is_return",
   "return_against",
   "section_addresses",
@@ -1107,6 +1108,12 @@
    "read_only": 1
   },
   {
+   "default": "0",
+   "fieldname": "apply_putaway_rule",
+   "fieldtype": "Check",
+   "label": "Apply Putaway Rule"
+  },
+  {
    "depends_on": "eval:!doc.__islocal",
    "fieldname": "per_returned",
    "fieldtype": "Percent",
@@ -1120,7 +1127,7 @@
  "idx": 261,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-11-30 12:54:23.278500",
+ "modified": "2020-12-08 18:31:32.234503",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 3c8bd37..fd9f5b8 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -83,6 +83,12 @@
 				}
 			])
 
+	def before_validate(self):
+		from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
+
+		if self.get("items") and self.apply_putaway_rule and not self.get("is_return"):
+			apply_putaway_rule(self.doctype, self.get("items"), self.company)
+
 	def validate(self):
 		self.validate_posting_time()
 		super(PurchaseReceipt, self).validate()
@@ -103,6 +109,7 @@
 		if getdate(self.posting_date) > getdate(nowdate()):
 			throw(_("Posting Date cannot be future date"))
 
+
 	def validate_cwip_accounts(self):
 		for item in self.get('items'):
 			if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
@@ -411,7 +418,7 @@
 		if warehouse_with_no_account:
 			frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
 				"\n".join(warehouse_with_no_account))
-		
+
 		return process_gl_map(gl_entries)
 
 	def get_asset_gl_entry(self, gl_entries):
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index f99ca89..ca58ab2 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1011,6 +1011,7 @@
 	pr.currency = args.currency or "INR"
 	pr.is_return = args.is_return
 	pr.return_against = args.return_against
+	pr.apply_putaway_rule = args.apply_putaway_rule
 	qty = args.qty or 5
 	received_qty = args.received_qty or qty
 	rejected_qty = args.rejected_qty or flt(received_qty) - flt(qty)
@@ -1026,6 +1027,7 @@
 		"rejected_warehouse": args.rejected_warehouse or "_Test Rejected Warehouse - _TC" if rejected_qty != 0 else "",
 		"rate": args.rate if args.rate != None else 50,
 		"conversion_factor": args.conversion_factor or 1.0,
+		"stock_qty": flt(qty) * (flt(args.conversion_factor) or 1.0),
 		"serial_no": args.serial_no,
 		"stock_uom": args.stock_uom or "_Test UOM",
 		"uom": uom,
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 871b255..662e50c 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -76,6 +76,7 @@
   "purchase_order_item",
   "material_request_item",
   "purchase_receipt_item",
+  "putaway_rule",
   "section_break_45",
   "allow_zero_valuation_rate",
   "bom",
@@ -840,6 +841,15 @@
    "fieldtype": "Column Break"
   },
   {
+   "fieldname": "putaway_rule",
+   "fieldtype": "Link",
+   "label": "Putaway Rule",
+   "no_copy": 1,
+   "options": "Putaway Rule",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
    "fieldname": "tracking_section",
    "fieldtype": "Section Break"
   },
@@ -866,7 +876,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-07 10:00:38.204294",
+ "modified": "2020-12-09 10:00:38.204294",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/putaway_rule/__init__.py b/erpnext/stock/doctype/putaway_rule/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/__init__.py
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.js b/erpnext/stock/doctype/putaway_rule/putaway_rule.js
new file mode 100644
index 0000000..e056920
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.js
@@ -0,0 +1,43 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Putaway Rule', {
+	setup: function(frm) {
+		frm.set_query("warehouse", function() {
+			return {
+				"filters": {
+					"company": frm.doc.company,
+					"is_group": 0
+				}
+			};
+		});
+	},
+
+	uom: function(frm) {
+		if (frm.doc.item_code && frm.doc.uom) {
+			return frm.call({
+				method: "erpnext.stock.get_item_details.get_conversion_factor",
+				args: {
+					item_code: frm.doc.item_code,
+					uom: frm.doc.uom
+				},
+				callback: function(r) {
+					if (!r.exc) {
+						let stock_capacity = flt(frm.doc.capacity) * flt(r.message.conversion_factor);
+						frm.set_value('conversion_factor', r.message.conversion_factor);
+						frm.set_value('stock_capacity', stock_capacity);
+					}
+				}
+			});
+		}
+	},
+
+	capacity: function(frm) {
+		let stock_capacity = flt(frm.doc.capacity) * flt(frm.doc.conversion_factor);
+		frm.set_value('stock_capacity', stock_capacity);
+	}
+
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.json b/erpnext/stock/doctype/putaway_rule/putaway_rule.json
new file mode 100644
index 0000000..a003f49
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.json
@@ -0,0 +1,160 @@
+{
+ "actions": [],
+ "autoname": "PUT-.####",
+ "creation": "2020-11-09 11:39:46.489501",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "disable",
+  "item_code",
+  "item_name",
+  "warehouse",
+  "priority",
+  "col_break_capacity",
+  "company",
+  "capacity",
+  "uom",
+  "conversion_factor",
+  "stock_uom",
+  "stock_capacity"
+ ],
+ "fields": [
+  {
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Item",
+   "options": "Item",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "item_code.item_name",
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "warehouse",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Warehouse",
+   "options": "Warehouse",
+   "reqd": 1
+  },
+  {
+   "fieldname": "col_break_capacity",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "capacity",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Capacity",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "item_code.stock_uom",
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "label": "Stock UOM",
+   "options": "UOM",
+   "read_only": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "priority",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "Priority"
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:!doc.__islocal",
+   "fieldname": "disable",
+   "fieldtype": "Check",
+   "label": "Disable"
+  },
+  {
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "label": "UOM",
+   "no_copy": 1,
+   "options": "UOM"
+  },
+  {
+   "fieldname": "stock_capacity",
+   "fieldtype": "Float",
+   "label": "Capacity in Stock UOM",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "default": "1",
+   "fieldname": "conversion_factor",
+   "fieldtype": "Float",
+   "label": "Conversion Factor",
+   "no_copy": 1,
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-11-25 20:39:19.973437",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Putaway Rule",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock User",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "permlevel": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "item_code",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
new file mode 100644
index 0000000..ea26cac
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
@@ -0,0 +1,235 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+import copy
+import json
+from collections import defaultdict
+from six import string_types
+from frappe import _
+from frappe.utils import flt, floor, nowdate, cint
+from frappe.model.document import Document
+from erpnext.stock.utils import get_stock_balance
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+class PutawayRule(Document):
+	def validate(self):
+		self.validate_duplicate_rule()
+		self.validate_warehouse_and_company()
+		self.validate_capacity()
+		self.validate_priority()
+		self.set_stock_capacity()
+
+	def validate_duplicate_rule(self):
+		existing_rule = frappe.db.exists("Putaway Rule", {"item_code": self.item_code, "warehouse": self.warehouse})
+		if existing_rule and existing_rule != self.name:
+			frappe.throw(_("Putaway Rule already exists for Item {0} in Warehouse {1}.")
+				.format(frappe.bold(self.item_code), frappe.bold(self.warehouse)),
+				title=_("Duplicate"))
+
+	def validate_priority(self):
+		if self.priority < 1:
+			frappe.throw(_("Priority cannot be lesser than 1."), title=_("Invalid Priority"))
+
+	def validate_warehouse_and_company(self):
+		company = frappe.db.get_value("Warehouse", self.warehouse, "company")
+		if company != self.company:
+			frappe.throw(_("Warehouse {0} does not belong to Company {1}.")
+				.format(frappe.bold(self.warehouse), frappe.bold(self.company)),
+				title=_("Invalid Warehouse"))
+
+	def validate_capacity(self):
+		stock_uom = frappe.db.get_value("Item", self.item_code, "stock_uom")
+		balance_qty = get_stock_balance(self.item_code, self.warehouse, nowdate())
+
+		if flt(self.stock_capacity) < flt(balance_qty):
+			frappe.throw(_("Warehouse Capacity for Item '{0}' must be greater than the existing stock level of {1} {2}.")
+				.format(self.item_code, frappe.bold(balance_qty), stock_uom),
+				title=_("Insufficient Capacity"))
+
+		if not self.capacity:
+			frappe.throw(_("Capacity must be greater than 0"), title=_("Invalid"))
+
+	def set_stock_capacity(self):
+		self.stock_capacity = (flt(self.conversion_factor) or 1) * flt(self.capacity)
+
+@frappe.whitelist()
+def get_available_putaway_capacity(rule):
+	stock_capacity, item_code, warehouse = frappe.db.get_value("Putaway Rule", rule,
+		["stock_capacity", "item_code", "warehouse"])
+	balance_qty = get_stock_balance(item_code, warehouse, nowdate())
+	free_space = flt(stock_capacity) - flt(balance_qty)
+	return free_space if free_space > 0 else 0
+
+@frappe.whitelist()
+def apply_putaway_rule(doctype, items, company, sync=None, purpose=None):
+	""" Applies Putaway Rule on line items.
+
+		items: List of Purchase Receipt/Stock Entry Items
+		company: Company in the Purchase Receipt/Stock Entry
+		doctype: Doctype to apply rule on
+		purpose: Purpose of Stock Entry
+		sync (optional): Sync with client side only for client side calls
+	"""
+	if isinstance(items, string_types):
+		items = json.loads(items)
+
+	items_not_accomodated, updated_table = [], []
+	item_wise_rules = defaultdict(list)
+
+	for item in items:
+		if isinstance(item, dict):
+			item = frappe._dict(item)
+
+		source_warehouse = item.get("s_warehouse")
+		serial_nos = get_serial_nos(item.get("serial_no"))
+		item.conversion_factor = flt(item.conversion_factor) or 1.0
+		pending_qty, item_code = flt(item.qty), item.item_code
+		pending_stock_qty = flt(item.transfer_qty) if doctype == "Stock Entry" else flt(item.stock_qty)
+		uom_must_be_whole_number = frappe.db.get_value('UOM', item.uom, 'must_be_whole_number')
+
+		if not pending_qty or not item_code:
+			updated_table = add_row(item, pending_qty, source_warehouse or item.warehouse, updated_table)
+			continue
+
+		at_capacity, rules = get_ordered_putaway_rules(item_code, company, source_warehouse=source_warehouse)
+
+		if not rules:
+			warehouse = source_warehouse or item.warehouse
+			if at_capacity:
+				# rules available, but no free space
+				items_not_accomodated.append([item_code, pending_qty])
+			else:
+				updated_table = add_row(item, pending_qty, warehouse, updated_table)
+			continue
+
+		# maintain item/item-warehouse wise rules, to handle if item is entered twice
+		# in the table, due to different price, etc.
+		key = item_code
+		if doctype == "Stock Entry" and purpose == "Material Transfer" and source_warehouse:
+			key = (item_code, source_warehouse)
+
+		if not item_wise_rules[key]:
+			item_wise_rules[key] = rules
+
+		for rule in item_wise_rules[key]:
+			if pending_stock_qty > 0 and rule.free_space:
+				stock_qty_to_allocate = flt(rule.free_space) if pending_stock_qty >= flt(rule.free_space) else pending_stock_qty
+				qty_to_allocate = stock_qty_to_allocate / item.conversion_factor
+
+				if uom_must_be_whole_number:
+					qty_to_allocate = floor(qty_to_allocate)
+					stock_qty_to_allocate = qty_to_allocate * item.conversion_factor
+
+				if not qty_to_allocate: break
+
+				updated_table = add_row(item, qty_to_allocate, rule.warehouse, updated_table,
+					rule.name, serial_nos=serial_nos)
+
+				pending_stock_qty -= stock_qty_to_allocate
+				pending_qty -= qty_to_allocate
+				rule["free_space"] -= stock_qty_to_allocate
+
+				if not pending_stock_qty > 0: break
+
+		# if pending qty after applying all rules, add row without warehouse
+		if pending_stock_qty > 0:
+			items_not_accomodated.append([item.item_code, pending_qty])
+
+	if items_not_accomodated:
+		show_unassigned_items_message(items_not_accomodated)
+
+	items[:] = updated_table if updated_table else items # modify items table
+
+	if sync and json.loads(sync): # sync with client side
+		return items
+
+def get_ordered_putaway_rules(item_code, company, source_warehouse=None):
+	"""Returns an ordered list of putaway rules to apply on an item."""
+	filters = {
+		"item_code": item_code,
+		"company": company,
+		"disable": 0
+	}
+	if source_warehouse:
+		filters.update({"warehouse": ["!=", source_warehouse]})
+
+	rules = frappe.get_all("Putaway Rule",
+		fields=["name", "item_code", "stock_capacity", "priority", "warehouse"],
+		filters=filters,
+		order_by="priority asc, capacity desc")
+
+	if not rules:
+		return False, None
+
+	vacant_rules = []
+	for rule in rules:
+		balance_qty = get_stock_balance(rule.item_code, rule.warehouse, nowdate())
+		free_space = flt(rule.stock_capacity) - flt(balance_qty)
+		if free_space > 0:
+			rule["free_space"] = free_space
+			vacant_rules.append(rule)
+
+	if not vacant_rules:
+		# After iterating through rules, if no rules are left
+		# then there is not enough space left in any rule
+		return True, None
+
+	vacant_rules = sorted(vacant_rules, key = lambda i: (i['priority'], -i['free_space']))
+
+	return False, vacant_rules
+
+def add_row(item, to_allocate, warehouse, updated_table, rule=None, serial_nos=None):
+	new_updated_table_row = copy.deepcopy(item)
+	new_updated_table_row.idx = 1 if not updated_table else cint(updated_table[-1].idx) + 1
+	new_updated_table_row.name = None
+	new_updated_table_row.qty = to_allocate
+
+	if item.doctype == "Stock Entry Detail":
+		new_updated_table_row.t_warehouse = warehouse
+		new_updated_table_row.transfer_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor)
+	else:
+		new_updated_table_row.stock_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor)
+		new_updated_table_row.warehouse = warehouse
+		new_updated_table_row.rejected_qty = 0
+		new_updated_table_row.received_qty = to_allocate
+
+	if rule:
+		new_updated_table_row.putaway_rule = rule
+	if serial_nos:
+		new_updated_table_row.serial_no = get_serial_nos_to_allocate(serial_nos, to_allocate)
+
+	updated_table.append(new_updated_table_row)
+	return updated_table
+
+def show_unassigned_items_message(items_not_accomodated):
+	msg = _("The following Items, having Putaway Rules, could not be accomodated:") + "<br><br>"
+	formatted_item_rows = ""
+
+	for entry in items_not_accomodated:
+		item_link = frappe.utils.get_link_to_form("Item", entry[0])
+		formatted_item_rows += """
+			<td>{0}</td>
+			<td>{1}</td>
+		</tr>""".format(item_link, frappe.bold(entry[1]))
+
+	msg += """
+		<table class="table">
+			<thead>
+				<td>{0}</td>
+				<td>{1}</td>
+			</thead>
+			{2}
+		</table>
+	""".format(_("Item"), _("Unassigned Qty"), formatted_item_rows)
+
+	frappe.msgprint(msg, title=_("Insufficient Capacity"), is_minimizable=True, wide=True)
+
+def get_serial_nos_to_allocate(serial_nos, to_allocate):
+	if serial_nos:
+		allocated_serial_nos = serial_nos[0: cint(to_allocate)]
+		serial_nos[:] = serial_nos[cint(to_allocate):] # pop out allocated serial nos and modify list
+		return "\n".join(allocated_serial_nos) if allocated_serial_nos else ""
+	else: return ""
\ No newline at end of file
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js
new file mode 100644
index 0000000..725e91e
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js
@@ -0,0 +1,18 @@
+frappe.listview_settings['Putaway Rule'] = {
+	add_fields: ["disable"],
+	get_indicator: (doc) => {
+		if (doc.disable) {
+			return [__("Disabled"), "darkgrey", "disable,=,1"];
+		} else {
+			return [__("Active"), "blue", "disable,=,0"];
+		}
+	},
+
+	reports: [
+		{
+			name: 'Warehouse Capacity Summary',
+			report_type: 'Page',
+			route: 'warehouse-capacity-summary'
+		}
+	]
+};
diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
new file mode 100644
index 0000000..86f7dc3
--- /dev/null
+++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
@@ -0,0 +1,389 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+import frappe
+import unittest
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.get_item_details import get_conversion_factor
+from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+from erpnext.stock.doctype.batch.test_batch import make_new_batch
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+class TestPutawayRule(unittest.TestCase):
+	def setUp(self):
+		if not frappe.db.exists("Item", "_Rice"):
+			make_item("_Rice", {
+				'is_stock_item': 1,
+				'has_batch_no' : 1,
+				'create_new_batch': 1,
+				'stock_uom': 'Kg'
+			})
+
+		if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 1"}):
+			create_warehouse("Rack 1")
+		if not frappe.db.exists("Warehouse", {"warehouse_name": "Rack 2"}):
+			create_warehouse("Rack 2")
+
+		self.warehouse_1 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 1"})
+		self.warehouse_2 = frappe.db.get_value("Warehouse", {"warehouse_name": "Rack 2"})
+
+		if not frappe.db.exists("UOM", "Bag"):
+			new_uom = frappe.new_doc("UOM")
+			new_uom.uom_name = "Bag"
+			new_uom.save()
+
+	def test_putaway_rules_priority(self):
+		"""Test if rule is applied by priority, irrespective of free space."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg")
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=300,
+			uom="Kg", priority=2)
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1,
+			do_not_submit=1)
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 200)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
+		self.assertEqual(pr.items[1].qty, 100)
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_2)
+
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_with_same_priority(self):
+		"""Test if rule with more free space is applied,
+		among two rules with same priority and capacity."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=500,
+			uom="Kg")
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500,
+			uom="Kg")
+
+		# out of 500 kg capacity, occupy 100 kg in warehouse_1
+		stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=100, basic_rate=50)
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=700, apply_putaway_rule=1,
+			do_not_submit=1)
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 500)
+		# warehouse_2 has 500 kg free space, it is given priority
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
+		self.assertEqual(pr.items[1].qty, 200)
+		# warehouse_1 has 400 kg free space, it is given less priority
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
+
+		stock_receipt.cancel()
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_with_insufficient_capacity(self):
+		"""Test if qty exceeding capacity, is handled."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=100,
+			uom="Kg")
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=200,
+			uom="Kg")
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=350, apply_putaway_rule=1,
+			do_not_submit=1)
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 200)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
+		self.assertEqual(pr.items[1].qty, 100)
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
+		# total 300 assigned, 50 unassigned
+
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_multi_uom(self):
+		"""Test rules applied on uom other than stock uom."""
+		item = frappe.get_doc("Item", "_Rice")
+		if not frappe.db.get_value("UOM Conversion Detail", {"parent": "_Rice", "uom": "Bag"}):
+			item.append("uoms", {
+				"uom": "Bag",
+				"conversion_factor": 1000
+			})
+			item.save()
+
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=3,
+			uom="Bag")
+		self.assertEqual(rule_1.stock_capacity, 3000)
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=4,
+			uom="Bag")
+		self.assertEqual(rule_2.stock_capacity, 4000)
+
+		# populate 'Rack 1' with 1 Bag, making the free space 2 Bags
+		stock_receipt = make_stock_entry(item_code="_Rice", target=self.warehouse_1, qty=1000, basic_rate=50)
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=6, uom="Bag", stock_uom="Kg",
+			conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1)
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 4)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_2)
+		self.assertEqual(pr.items[1].qty, 2)
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
+
+		stock_receipt.cancel()
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_multi_uom_whole_uom(self):
+		"""Test if whole UOMs are handled."""
+		item = frappe.get_doc("Item", "_Rice")
+		if not frappe.db.get_value("UOM Conversion Detail", {"parent": "_Rice", "uom": "Bag"}):
+			item.append("uoms", {
+				"uom": "Bag",
+				"conversion_factor": 1000
+			})
+			item.save()
+
+		frappe.db.set_value("UOM", "Bag", "must_be_whole_number", 1)
+
+		# Putaway Rule in different UOM
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=1,
+			uom="Bag")
+		self.assertEqual(rule_1.stock_capacity, 1000)
+		# Putaway Rule in Stock UOM
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=500)
+		self.assertEqual(rule_2.stock_capacity, 500)
+		# total capacity is 1500 Kg
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=2, uom="Bag", stock_uom="Kg",
+			conversion_factor=1000, apply_putaway_rule=1, do_not_submit=1)
+		self.assertEqual(len(pr.items), 1)
+		self.assertEqual(pr.items[0].qty, 1)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
+		# leftover space was for 500 kg (0.5 Bag)
+		# Since Bag is a whole UOM, 1(out of 2) Bag will be unassigned
+
+		pr.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rules_with_reoccurring_item(self):
+		"""Test rules on same item entered multiple times with different rate."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg")
+		# total capacity is 200 Kg
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=100, apply_putaway_rule=1,
+			do_not_submit=1)
+		pr.append("items", {
+			"item_code": "_Rice",
+			"warehouse": "_Test Warehouse - _TC",
+			"qty": 200,
+			"uom": "Kg",
+			"stock_uom": "Kg",
+			"stock_qty": 200,
+			"received_qty": 200,
+			"rate": 100,
+			"conversion_factor": 1.0,
+		}) # same item entered again in PR but with different rate
+		pr.save()
+		self.assertEqual(len(pr.items), 2)
+		self.assertEqual(pr.items[0].qty, 100)
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
+		self.assertEqual(pr.items[0].putaway_rule, rule_1.name)
+		# same rule applied to second item row
+		# with previous assignment considered
+		self.assertEqual(pr.items[1].qty, 100) # 100 unassigned in second row from 200
+		self.assertEqual(pr.items[1].warehouse, self.warehouse_1)
+		self.assertEqual(pr.items[1].putaway_rule, rule_1.name)
+
+		pr.delete()
+		rule_1.delete()
+
+	def test_validate_over_receipt_in_warehouse(self):
+		"""Test if overreceipt is blocked in the presence of putaway rules."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg")
+
+		pr = make_purchase_receipt(item_code="_Rice", qty=300, apply_putaway_rule=1,
+			do_not_submit=1)
+		self.assertEqual(len(pr.items), 1)
+		self.assertEqual(pr.items[0].qty, 200) # 100 is unassigned fro 300 Kg
+		self.assertEqual(pr.items[0].warehouse, self.warehouse_1)
+		self.assertEqual(pr.items[0].putaway_rule, rule_1.name)
+
+		# force overreceipt and disable apply putaway rule in PR
+		pr.items[0].qty = 300
+		pr.items[0].stock_qty = 300
+		pr.apply_putaway_rule = 0
+		self.assertRaises(frappe.ValidationError, pr.save)
+
+		pr.delete()
+		rule_1.delete()
+
+	def test_putaway_rule_on_stock_entry_material_transfer(self):
+		"""Test if source warehouse is considered while applying rules."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg") # higher priority
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100,
+			uom="Kg", priority=2)
+
+		stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_1, qty=200,
+			target="_Test Warehouse - _TC", purpose="Material Transfer",
+			apply_putaway_rule=1, do_not_submit=1)
+
+		stock_entry_item = stock_entry.get("items")[0]
+
+		# since source warehouse is Rack 1, rule 1 (for Rack 1) will be avoided
+		# even though it has more free space and higher priority
+		self.assertEqual(stock_entry_item.t_warehouse, self.warehouse_2)
+		self.assertEqual(stock_entry_item.qty, 100) # unassigned 100 out of 200 Kg
+		self.assertEqual(stock_entry_item.putaway_rule, rule_2.name)
+
+		stock_entry.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rule_on_stock_entry_material_transfer_reoccuring_item(self):
+		"""Test if reoccuring item is correctly considered."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=300,
+			uom="Kg")
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=600,
+			uom="Kg", priority=2)
+
+		# create SE with first row having source warehouse as Rack 2
+		stock_entry = make_stock_entry(item_code="_Rice", source=self.warehouse_2, qty=200,
+			target="_Test Warehouse - _TC", purpose="Material Transfer",
+			apply_putaway_rule=1, do_not_submit=1)
+
+		# Add rows with source warehouse as Rack 1
+		stock_entry.extend("items", [
+			{
+				"item_code": "_Rice",
+				"s_warehouse": self.warehouse_1,
+				"t_warehouse": "_Test Warehouse - _TC",
+				"qty": 100,
+				"basic_rate": 50,
+				"conversion_factor": 1.0,
+				"transfer_qty": 100
+			},
+			{
+				"item_code": "_Rice",
+				"s_warehouse": self.warehouse_1,
+				"t_warehouse": "_Test Warehouse - _TC",
+				"qty": 200,
+				"basic_rate": 60,
+				"conversion_factor": 1.0,
+				"transfer_qty": 200
+			}
+		])
+
+		stock_entry.save()
+
+		# since source warehouse was Rack 2, exclude rule_2
+		self.assertEqual(stock_entry.items[0].t_warehouse, self.warehouse_1)
+		self.assertEqual(stock_entry.items[0].qty, 200)
+		self.assertEqual(stock_entry.items[0].putaway_rule, rule_1.name)
+
+		# since source warehouse was Rack 1, exclude rule_1 even though it has
+		# higher priority
+		self.assertEqual(stock_entry.items[1].t_warehouse, self.warehouse_2)
+		self.assertEqual(stock_entry.items[1].qty, 100)
+		self.assertEqual(stock_entry.items[1].putaway_rule, rule_2.name)
+
+		self.assertEqual(stock_entry.items[2].t_warehouse, self.warehouse_2)
+		self.assertEqual(stock_entry.items[2].qty, 200)
+		self.assertEqual(stock_entry.items[2].putaway_rule, rule_2.name)
+
+		stock_entry.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rule_on_stock_entry_material_transfer_batch_serial_item(self):
+		"""Test if batch and serial items are split correctly."""
+		if not frappe.db.exists("Item", "Water Bottle"):
+			make_item("Water Bottle", {
+				"is_stock_item": 1,
+				"has_batch_no" : 1,
+				"create_new_batch": 1,
+				"has_serial_no": 1,
+				"serial_no_series": "BOTTL-.####",
+				"stock_uom": "Nos"
+			})
+
+		rule_1 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_1, capacity=3,
+			uom="Nos")
+		rule_2 = create_putaway_rule(item_code="Water Bottle", warehouse=self.warehouse_2, capacity=2,
+		uom="Nos")
+
+		make_new_batch(batch_id="BOTTL-BATCH-1", item_code="Water Bottle")
+
+		pr = make_purchase_receipt(item_code="Water Bottle", qty=5, do_not_submit=1)
+		pr.items[0].batch_no = "BOTTL-BATCH-1"
+		pr.save()
+		pr.submit()
+
+		serial_nos = frappe.get_list("Serial No", filters={"purchase_document_no": pr.name, "status": "Active"})
+		serial_nos = [d.name for d in serial_nos]
+
+		stock_entry = make_stock_entry(item_code="Water Bottle", source="_Test Warehouse - _TC", qty=5,
+			target="Finished Goods - _TC", purpose="Material Transfer",
+			apply_putaway_rule=1, do_not_save=1)
+		stock_entry.items[0].batch_no = "BOTTL-BATCH-1"
+		stock_entry.items[0].serial_no = "\n".join(serial_nos)
+		stock_entry.save()
+
+		self.assertEqual(stock_entry.items[0].t_warehouse, self.warehouse_1)
+		self.assertEqual(stock_entry.items[0].qty, 3)
+		self.assertEqual(stock_entry.items[0].putaway_rule, rule_1.name)
+		self.assertEqual(stock_entry.items[0].serial_no, "\n".join(serial_nos[:3]))
+		self.assertEqual(stock_entry.items[0].batch_no, "BOTTL-BATCH-1")
+
+		self.assertEqual(stock_entry.items[1].t_warehouse, self.warehouse_2)
+		self.assertEqual(stock_entry.items[1].qty, 2)
+		self.assertEqual(stock_entry.items[1].putaway_rule, rule_2.name)
+		self.assertEqual(stock_entry.items[1].serial_no, "\n".join(serial_nos[3:]))
+		self.assertEqual(stock_entry.items[1].batch_no, "BOTTL-BATCH-1")
+
+		stock_entry.delete()
+		pr.cancel()
+		rule_1.delete()
+		rule_2.delete()
+
+	def test_putaway_rule_on_stock_entry_material_receipt(self):
+		"""Test if rules are applied in Stock Entry of type Receipt."""
+		rule_1 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_1, capacity=200,
+			uom="Kg") # more capacity
+		rule_2 = create_putaway_rule(item_code="_Rice", warehouse=self.warehouse_2, capacity=100,
+			uom="Kg")
+
+		stock_entry = make_stock_entry(item_code="_Rice", qty=100,
+			target="_Test Warehouse - _TC", purpose="Material Receipt",
+			apply_putaway_rule=1, do_not_submit=1)
+
+		stock_entry_item = stock_entry.get("items")[0]
+
+		self.assertEqual(stock_entry_item.t_warehouse, self.warehouse_1)
+		self.assertEqual(stock_entry_item.qty, 100)
+		self.assertEqual(stock_entry_item.putaway_rule, rule_1.name)
+
+		stock_entry.delete()
+		rule_1.delete()
+		rule_2.delete()
+
+def create_putaway_rule(**args):
+	args = frappe._dict(args)
+	putaway = frappe.new_doc("Putaway Rule")
+
+	putaway.disable = args.disable or 0
+	putaway.company = args.company or "_Test Company"
+	putaway.item_code = args.item or args.item_code or "_Test Item"
+	putaway.warehouse = args.warehouse
+	putaway.priority = args.priority or 1
+	putaway.capacity = args.capacity or 1
+	putaway.stock_uom = frappe.db.get_value("Item", putaway.item_code, "stock_uom")
+	putaway.uom = args.uom or putaway.stock_uom
+	putaway.conversion_factor = get_conversion_factor(putaway.item_code, putaway.uom)['conversion_factor']
+
+	if not args.do_not_save:
+		putaway.save()
+
+	return putaway
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index d619641..9aaa669 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -577,8 +577,12 @@
 				}
 			});
 		}
+	},
+
+	apply_putaway_rule: function (frm) {
+		if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm, frm.doc.purpose);
 	}
-})
+});
 
 frappe.ui.form.on('Stock Entry Detail', {
 	qty: function(frm, cdt, cdn) {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index 5aed081..98c047a 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -27,6 +27,7 @@
   "set_posting_time",
   "inspection_required",
   "from_bom",
+  "apply_putaway_rule",
   "sb1",
   "bom_no",
   "fg_completed_qty",
@@ -640,6 +641,13 @@
    "fieldtype": "Check",
    "label": "Add to Transit",
    "no_copy": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:in_list([\"Material Transfer\", \"Material Receipt\"], doc.purpose)",
+   "fieldname": "apply_putaway_rule",
+   "fieldtype": "Check",
+   "label": "Apply Putaway Rule"
   }
  ],
  "icon": "fa fa-file-text",
@@ -647,7 +655,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-09-09 12:59:02.508943",
+ "modified": "2020-12-09 14:58:13.267321",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 3115401..d623d5c 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -43,6 +43,14 @@
 		for item in self.get("items"):
 			item.update(get_bin_details(item.item_code, item.s_warehouse))
 
+	def before_validate(self):
+		from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
+		apply_rule = self.apply_putaway_rule and (self.purpose in ["Material Transfer", "Material Receipt"])
+
+		if self.get("items") and apply_rule:
+			apply_putaway_rule(self.doctype, self.get("items"), self.company,
+				purpose=self.purpose)
+
 	def validate(self):
 		self.pro_doc = frappe._dict()
 		if self.work_order:
@@ -80,6 +88,7 @@
 		self.validate_serialized_batch()
 		self.set_actual_qty()
 		self.calculate_rate_and_amount()
+		self.validate_putaway_capacity()
 
 	def on_submit(self):
 		self.update_stock_ledger()
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index b78c6be..b12a854 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -53,6 +53,8 @@
 		args.target = args.to_warehouse
 	if args.item_code:
 		args.item = args.item_code
+	if args.apply_putaway_rule:
+		s.apply_putaway_rule = args.apply_putaway_rule
 
 	if isinstance(args.qty, string_types):
 		if '.' in args.qty:
@@ -118,7 +120,8 @@
 		"t_warehouse": args.target,
 		"qty": args.qty,
 		"basic_rate": args.rate or args.basic_rate,
-		"conversion_factor": 1.0,
+		"conversion_factor": args.conversion_factor or 1.0,
+		"transfer_qty": flt(args.qty) * (flt(args.conversion_factor) or 1.0),
 		"serial_no": args.serial_no,
 		'batch_no': args.batch_no,
 		'cost_center': args.cost_center,
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index b4c2284..988ae92 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "hash",
  "creation": "2013-03-29 18:22:12",
  "doctype": "DocType",
@@ -65,6 +66,7 @@
   "against_stock_entry",
   "ste_detail",
   "po_detail",
+  "putaway_rule",
   "column_break_51",
   "reference_purchase_receipt",
   "quality_inspection"
@@ -496,6 +498,16 @@
    "label": "Set Basic Rate Manually"
   },
   {
+   "depends_on": "eval:in_list([\"Material Transfer\", \"Material Receipt\"], parent.purpose)",
+   "fieldname": "putaway_rule",
+   "fieldtype": "Link",
+   "label": "Putaway Rule",
+   "no_copy": 1,
+   "options": "Putaway Rule",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
    "fieldname": "quantity_section",
    "fieldtype": "Section Break",
    "label": "Quantity"
@@ -526,7 +538,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-09-25 17:55:03.384138",
+ "modified": "2020-12-30 15:00:44.489442",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 5b40292..f0a90f9 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -30,6 +30,7 @@
 		self.validate_data()
 		self.validate_expense_account()
 		self.set_total_qty_and_amount()
+		self.validate_putaway_capacity()
 
 		if self._action=="submit":
 			self.make_batches('warehouse')
diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js
index da21c6b..bddffd4 100644
--- a/erpnext/stock/page/stock_balance/stock_balance.js
+++ b/erpnext/stock/page/stock_balance/stock_balance.js
@@ -65,6 +65,9 @@
 	frappe.require('assets/js/item-dashboard.min.js', function() {
 		page.item_dashboard = new erpnext.stock.ItemDashboard({
 			parent: page.main,
+			page_length: 20,
+			method: 'erpnext.stock.dashboard.item_dashboard.get_data',
+			template: 'item_dashboard_list'
 		})
 
 		page.item_dashboard.before_refresh = function() {
diff --git a/erpnext/stock/page/warehouse_capacity_summary/__init__.py b/erpnext/stock/page/warehouse_capacity_summary/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/page/warehouse_capacity_summary/__init__.py
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html
new file mode 100644
index 0000000..90112c7
--- /dev/null
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html
@@ -0,0 +1,40 @@
+{% for d in data %}
+	<div class="dashboard-list-item" style="padding: 7px 15px;">
+		<div class="row">
+			<div class="col-sm-2 small" style="margin-top: 8px;">
+				<a data-type="warehouse" data-name="{{ d.warehouse }}">{{ d.warehouse }}</a>
+			</div>
+			<div class="col-sm-2 small" style="margin-top: 8px; ">
+				<a data-type="item" data-name="{{ d.item_code }}">{{ d.item_code }}</a>
+			</div>
+			<div class="col-sm-1 small" style="margin-top: 8px; ">
+				{{ d.stock_capacity }}
+			</div>
+			<div class="col-sm-2 small" style="margin-top: 8px; ">
+				{{ d.actual_qty }}
+			</div>
+			<div class="col-sm-2 small">
+				<div class="progress" title="Occupied Qty: {{ d.actual_qty }}" style="margin-bottom: 4px; height: 7px; margin-top: 14px;">
+					<div class="progress-bar" role="progressbar"
+						aria-valuenow="{{ d.percent_occupied }}"
+						aria-valuemin="0" aria-valuemax="100"
+						style="width:{{ d.percent_occupied }}%;
+						background-color: {{ d.color }}">
+					</div>
+				</div>
+			</div>
+			<div class="col-sm-1 small" style="margin-top: 8px;">
+				{{ d.percent_occupied }}%
+			</div>
+			{% if can_write %}
+			<div class="col-sm-1 text-right" style="margin-top: 2px;">
+				<button class="btn btn-default btn-xs btn-edit"
+				style="margin-top: 4px;margin-bottom: 4px;"
+				data-warehouse="{{ d.warehouse }}"
+				data-item="{{ escape(d.item_code) }}"
+				data-company="{{ escape(d.company) }}">{{ __("Edit Capacity") }}</a>
+			</div>
+			{% endif %}
+		</div>
+	</div>
+{% endfor %}
\ No newline at end of file
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
new file mode 100644
index 0000000..b610e7d
--- /dev/null
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
@@ -0,0 +1,120 @@
+frappe.pages['warehouse-capacity-summary'].on_page_load = function(wrapper) {
+	var page = frappe.ui.make_app_page({
+		parent: wrapper,
+		title: 'Warehouse Capacity Summary',
+		single_column: true
+	});
+	page.set_secondary_action('Refresh', () => page.capacity_dashboard.refresh(), 'octicon octicon-sync');
+	page.start = 0;
+
+	page.company_field = page.add_field({
+		fieldname: 'company',
+		label: __('Company'),
+		fieldtype: 'Link',
+		options: 'Company',
+		reqd: 1,
+		default: frappe.defaults.get_default("company"),
+		change: function() {
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	page.warehouse_field = page.add_field({
+		fieldname: 'warehouse',
+		label: __('Warehouse'),
+		fieldtype: 'Link',
+		options: 'Warehouse',
+		change: function() {
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	page.item_field = page.add_field({
+		fieldname: 'item_code',
+		label: __('Item'),
+		fieldtype: 'Link',
+		options: 'Item',
+		change: function() {
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	page.parent_warehouse_field = page.add_field({
+		fieldname: 'parent_warehouse',
+		label: __('Parent Warehouse'),
+		fieldtype: 'Link',
+		options: 'Warehouse',
+		get_query: function() {
+			return {
+				filters: {
+					"is_group": 1
+				}
+			};
+		},
+		change: function() {
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	page.sort_selector = new frappe.ui.SortSelector({
+		parent: page.wrapper.find('.page-form'),
+		args: {
+			sort_by: 'stock_capacity',
+			sort_order: 'desc',
+			options: [
+				{fieldname: 'stock_capacity', label: __('Capacity (Stock UOM)')},
+				{fieldname: 'percent_occupied', label: __('% Occupied')},
+				{fieldname: 'actual_qty', label: __('Balance Qty (Stock ')}
+			]
+		},
+		change: function(sort_by, sort_order) {
+			page.capacity_dashboard.sort_by = sort_by;
+			page.capacity_dashboard.sort_order = sort_order;
+			page.capacity_dashboard.start = 0;
+			page.capacity_dashboard.refresh();
+		}
+	});
+
+	frappe.require('assets/js/item-dashboard.min.js', function() {
+		$(frappe.render_template('warehouse_capacity_summary_header')).appendTo(page.main);
+
+		page.capacity_dashboard = new erpnext.stock.ItemDashboard({
+			page_name: "warehouse-capacity-summary",
+			page_length: 10,
+			parent: page.main,
+			sort_by: 'stock_capacity',
+			sort_order: 'desc',
+			method: 'erpnext.stock.dashboard.warehouse_capacity_dashboard.get_data',
+			template: 'warehouse_capacity_summary'
+		});
+
+		page.capacity_dashboard.before_refresh = function() {
+			this.item_code = page.item_field.get_value();
+			this.warehouse = page.warehouse_field.get_value();
+			this.parent_warehouse = page.parent_warehouse_field.get_value();
+			this.company = page.company_field.get_value();
+		};
+
+		page.capacity_dashboard.refresh();
+
+		let setup_click = function(doctype) {
+			page.main.on('click', 'a[data-type="'+ doctype.toLowerCase() +'"]', function() {
+				var name = $(this).attr('data-name');
+				var field = page[doctype.toLowerCase() + '_field'];
+				if (field.get_value()===name) {
+					frappe.set_route('Form', doctype, name);
+				} else {
+					field.set_input(name);
+					page.capacity_dashboard.refresh();
+				}
+			});
+		};
+
+		setup_click('Item');
+		setup_click('Warehouse');
+	});
+};
\ No newline at end of file
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json
new file mode 100644
index 0000000..a6e5b45
--- /dev/null
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.json
@@ -0,0 +1,26 @@
+{
+ "content": null,
+ "creation": "2020-11-25 12:07:54.056208",
+ "docstatus": 0,
+ "doctype": "Page",
+ "idx": 0,
+ "modified": "2020-11-25 11:07:54.056208",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "warehouse-capacity-summary",
+ "owner": "Administrator",
+ "page_name": "Warehouse Capacity Summary",
+ "roles": [
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Stock Manager"
+  }
+ ],
+ "script": null,
+ "standard": "Yes",
+ "style": null,
+ "system_page": 0,
+ "title": "Warehouse Capacity Summary"
+}
\ No newline at end of file
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
new file mode 100644
index 0000000..acaf180
--- /dev/null
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
@@ -0,0 +1,19 @@
+<div class="dashboard-list-item" style="padding: 12px 15px;">
+	<div class="row">
+		<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+			Warehouse
+		</div>
+		<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+			Item
+		</div>
+		<div class="col-sm-1 small text-muted" style="margin-top: 8px;">
+			Stock Capacity
+		</div>
+		<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+			Balance Stock Qty
+		</div>
+		<div class="col-sm-2 small text-muted" style="margin-top: 8px;">
+			% Occupied
+		</div>
+	</div>
+</div>
\ No newline at end of file