feat: Trigger rule application from client side

- Table is reset and overwritten with applied rules on checkbox trigger
- Sider fixes
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index 1cb68a6..0c6bcad 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -516,27 +516,4 @@
 	});
 
 	dialog.show();
-}
-
-erpnext.apply_putaway_rule = (frm) => {
-	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: {
-			items: frm.doc.items,
-			company: frm.doc.company
-		},
-		callback: (result) => {
-			if(!result.exc) {
-				if(result.message) {
-					frm.doc.items = result.message;
-					frm.get_field("items").refresh();
-				}
-			}
-		}
-	});
 }
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 7f08cd1..31efb6a 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -2029,3 +2029,33 @@
 		}, show_dialog);
 	});
 }
+
+erpnext.apply_putaway_rule = (frm) => {
+	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
+		},
+		callback: (result) => {
+			if (!result.exc && result.message) {
+				frm.clear_table("items");
+
+				let items =  result.message;
+				items.forEach((row) => {
+					delete row["name"];
+					let child = frm.add_child("items");
+					Object.assign(child, row);
+				});
+				frm.get_field("items").grid.refresh();
+			}
+		}
+	});
+};
\ No newline at end of file
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index abc286f..070589b 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -30,8 +30,8 @@
 			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);
-			});
+					frappe.set_route("Form", "Putaway Rule", r.name);
+				});
 		});
 
 		function handle_move_add(element, action) {
@@ -88,7 +88,7 @@
 			start: this.start,
 			sort_by: this.sort_by,
 			sort_order: this.sort_order
-		}
+		};
 
 		var me = this;
 		frappe.call({
@@ -104,10 +104,12 @@
 			this.max_count = 0;
 			this.result.empty();
 		}
+
+		let context = "";
 		if (this.page_name === "warehouse-capacity-summary") {
-			var context = this.get_capacity_dashboard_data(data);
+			context = this.get_capacity_dashboard_data(data);
 		} else {
-			var context = this.get_item_dashboard_data(data, this.max_count, true);
+			context = this.get_item_dashboard_data(data, this.max_count, true);
 		}
 
 		this.max_count = this.max_count;
@@ -152,7 +154,7 @@
 		});
 
 		let can_write = 0;
-		if(frappe.boot.user.can_write.indexOf("Stock Entry")>=0){
+		if (frappe.boot.user.can_write.indexOf("Stock Entry") >= 0) {
 			can_write = 1;
 		}
 
@@ -165,23 +167,23 @@
 	},
 
 	get_capacity_dashboard_data: function(data) {
-		if(!data) 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){
+		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/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 45eb646..218fb9e 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -214,7 +214,7 @@
 	},
 
 	apply_putaway_rule: function() {
-		// if (this.frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(this.frm);
+		if (this.frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(this.frm);
 	}
 
 });
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index b1ad610..4372bdc 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -87,7 +87,7 @@
 		from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
 
 		if self.get("items") and self.apply_putaway_rule:
-			self.items = apply_putaway_rule(self.doctype, self.get("items"), self.company)
+			apply_putaway_rule(self.doctype, self.get("items"), self.company)
 
 	def validate(self):
 		self.validate_posting_time()
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
index 8838bb7..4ed4daf 100644
--- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
@@ -64,7 +64,7 @@
 	return free_space if free_space > 0 else 0
 
 @frappe.whitelist()
-def apply_putaway_rule(doctype, items, company):
+def apply_putaway_rule(doctype, items, company, sync=None):
 	""" Applies Putaway Rule on line items.
 
 		items: List of Purchase Receipt Item objects
@@ -82,7 +82,7 @@
 
 		source_warehouse = item.get("s_warehouse")
 		serial_nos = get_serial_nos(item.get("serial_no"))
-		conversion = flt(item.conversion_factor) or 1
+		item.conversion_factor = flt(item.conversion_factor) or 1
 		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)
 		if not pending_qty or not item_code:
@@ -109,11 +109,11 @@
 		for rule in item_wise_rules[item_code]:
 			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 / (conversion)
+				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 * conversion
+					stock_qty_to_allocate = qty_to_allocate * item.conversion_factor
 
 				if not qty_to_allocate: break
 
@@ -124,17 +124,19 @@
 				pending_qty -= qty_to_allocate
 				rule["free_space"] -= stock_qty_to_allocate
 
-				if not pending_stock_qty: break
+				if not pending_stock_qty > 0: break
 
 		# if pending qty after applying all rules, add row without warehouse
 		if pending_stock_qty > 0:
-			# updated_table = add_row(item, pending_qty, '', updated_table, serial_nos=serial_nos)
 			items_not_accomodated.append([item.item_code, pending_qty])
 
 	if items_not_accomodated:
 		show_unassigned_items_message(items_not_accomodated)
 
-	return updated_table if updated_table else items
+	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."""
@@ -174,12 +176,14 @@
 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 = "New " + str(item.doctype) + " " + str(new_updated_table_row.idx)
+	new_updated_table_row.name = None
 	new_updated_table_row.qty = to_allocate
-	new_updated_table_row.stock_qty = flt(to_allocate) * flt(new_updated_table_row.conversion_factor)
+
 	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
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 2be70f3..fd920a5 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -573,10 +573,10 @@
 		}
 	},
 
-	apply_putaway_rule: function(frm) {
-		// if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm);
+	apply_putaway_rule: function (frm) {
+		if (frm.doc.apply_putaway_rule) erpnext.apply_putaway_rule(frm);
 	}
-})
+});
 
 frappe.ui.form.on('Stock Entry Detail', {
 	qty: function(frm, cdt, cdn) {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index aa3425c..f07039f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -47,7 +47,7 @@
 		apply_rule = self.apply_putaway_rule and (self.purpose in ["Material Transfer", "Material Receipt"])
 
 		if self.get("items") and apply_rule:
-			self.items = apply_putaway_rule(self.doctype, self.get("items"), self.company)
+			apply_putaway_rule(self.doctype, self.get("items"), self.company)
 
 	def validate(self):
 		self.pro_doc = frappe._dict()
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
index c3b3b5d..b610e7d 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
@@ -10,8 +10,8 @@
 	page.company_field = page.add_field({
 		fieldname: 'company',
 		label: __('Company'),
-		fieldtype:'Link',
-		options:'Company',
+		fieldtype: 'Link',
+		options: 'Company',
 		reqd: 1,
 		default: frappe.defaults.get_default("company"),
 		change: function() {
@@ -23,8 +23,8 @@
 	page.warehouse_field = page.add_field({
 		fieldname: 'warehouse',
 		label: __('Warehouse'),
-		fieldtype:'Link',
-		options:'Warehouse',
+		fieldtype: 'Link',
+		options: 'Warehouse',
 		change: function() {
 			page.capacity_dashboard.start = 0;
 			page.capacity_dashboard.refresh();
@@ -34,8 +34,8 @@
 	page.item_field = page.add_field({
 		fieldname: 'item_code',
 		label: __('Item'),
-		fieldtype:'Link',
-		options:'Item',
+		fieldtype: 'Link',
+		options: 'Item',
 		change: function() {
 			page.capacity_dashboard.start = 0;
 			page.capacity_dashboard.refresh();
@@ -45,8 +45,8 @@
 	page.parent_warehouse_field = page.add_field({
 		fieldname: 'parent_warehouse',
 		label: __('Parent Warehouse'),
-		fieldtype:'Link',
-		options:'Warehouse',
+		fieldtype: 'Link',
+		options: 'Warehouse',
 		get_query: function() {
 			return {
 				filters: {
@@ -67,8 +67,8 @@
 			sort_order: 'desc',
 			options: [
 				{fieldname: 'stock_capacity', label: __('Capacity (Stock UOM)')},
-				{fieldname: 'percent_occupied', label:__('% Occupied')},
-				{fieldname: 'actual_qty', label:__('Balance Qty (Stock ')}
+				{fieldname: 'percent_occupied', label: __('% Occupied')},
+				{fieldname: 'actual_qty', label: __('Balance Qty (Stock ')}
 			]
 		},
 		change: function(sort_by, sort_order) {
@@ -90,14 +90,14 @@
 			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();
 
@@ -105,16 +105,16 @@
 			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)
+				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
+};
\ No newline at end of file