Batch Stock Items, having serial number can't be moved without inserting Serial Number (#11792) (#11813)

* if doctype is batch, add extra information to args

* automatically fetch serial numbers if possible

* take advantage of changes in make_stock_entry

* code clean up

* PEP 8 compliance

* fix bug that clears serial number
diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js
index 1822a06..d299ed1 100644
--- a/erpnext/stock/doctype/batch/batch.js
+++ b/erpnext/stock/doctype/batch/batch.js
@@ -65,31 +65,38 @@
 					// move - ask for target warehouse and make stock entry
 					rows.find('.btn-move').on('click', function() {
 						var $btn = $(this);
-						frappe.prompt({
-							fieldname: 'to_warehouse',
-							label: __('To Warehouse'),
-							fieldtype: 'Link',
-							options: 'Warehouse'
-						},
-						(data) => {
-							frappe.call({
-								method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry',
-								args: {
-									item_code: frm.doc.item,
-									batch_no: frm.doc.name,
-									qty: $btn.attr('data-qty'),
-									from_warehouse: $btn.attr('data-warehouse'),
-									to_warehouse: data.to_warehouse
-								},
-								callback: (r) => {
-									frappe.show_alert(__('Stock Entry {0} created',
-										['<a href="#Form/Stock Entry/'+r.message.name+'">' + r.message.name+ '</a>']));
-									frm.refresh();
-								},
-							});
-						},
-						__('Select Target Warehouse'),
-						__('Move')
+						const fields = [
+							{
+								fieldname: 'to_warehouse',
+								label: __('To Warehouse'),
+								fieldtype: 'Link',
+								options: 'Warehouse'
+							}
+						];
+
+						frappe.prompt(
+							fields,
+							(data) => {
+								frappe.call({
+									method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry',
+									args: {
+										item_code: frm.doc.item,
+										batch_no: frm.doc.name,
+										qty: $btn.attr('data-qty'),
+										from_warehouse: $btn.attr('data-warehouse'),
+										to_warehouse: data.to_warehouse,
+										source_document: frm.doc.reference_name,
+										reference_doctype: frm.doc.reference_doctype
+									},
+									callback: (r) => {
+										frappe.show_alert(__('Stock Entry {0} created',
+											['<a href="#Form/Stock Entry/'+r.message.name+'">' + r.message.name+ '</a>']));
+										frm.refresh();
+									},
+								});
+							},
+							__('Select Target Warehouse'),
+							__('Move')
 						);
 					});
 
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 6af8c09..b656c3f 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -12,6 +12,7 @@
 from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
 
+
 class TestPurchaseReceipt(unittest.TestCase):
 	def setUp(self):
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
@@ -259,7 +260,7 @@
 
 		item_code = frappe.db.get_value('Item', {'has_serial_no': 1})
 		if not item_code:
-			item = make_item("Test Serial Item 1", dict(has_serial_no = 1))
+			item = make_item("Test Serial Item 1", dict(has_serial_no=1))
 			item_code = item.name
 
 		serial_no = random_string(5)
@@ -273,11 +274,13 @@
 			serial_no=serial_no, basic_rate=100, do_not_submit=True)
 		self.assertRaises(SerialNoDuplicateError, se.submit)
 
+
 def get_gl_entries(voucher_type, voucher_no):
 	return frappe.db.sql("""select account, debit, credit
 		from `tabGL Entry` where voucher_type=%s and voucher_no=%s
 		order by account desc""", (voucher_type, voucher_no), as_dict=1)
 
+
 def make_purchase_receipt(**args):
 	frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 	pr = frappe.new_doc("Purchase Receipt")
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index e1ec3ee..446f718 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -20,6 +20,16 @@
 	:do_not_save: Optional flag
 	:do_not_submit: Optional flag
 	'''
+
+	def process_serial_numbers(serial_nos_list):
+		serial_nos_list = [
+			'\n'.join(serial_num['serial_no'] for serial_num in serial_nos_list)
+		]
+
+		uniques = list(set(serial_nos_list[0].split('\n')))
+
+		return '\n'.join(uniques)
+
 	s = frappe.new_doc("Stock Entry")
 	args = frappe._dict(args)
 
@@ -77,6 +87,25 @@
 	if not args.cost_center:
 		args.cost_center = frappe.get_value('Company', s.company, 'cost_center')
 
+	if not args.expense_account:
+		args.expense_account = frappe.get_value('Company', s.company, 'stock_adjustment_account')
+
+	# We can find out the serial number using the batch source document
+	serial_number = args.serial_no
+
+	if not args.serial_no and args.qty and args.batch_no:
+		serial_number_list = frappe.get_list(
+			doctype='Stock Ledger Entry',
+			fields=['serial_no'],
+			filters={
+				'batch_no': args.batch_no,
+				'warehouse': args.from_warehouse
+			}
+		)
+		serial_number = process_serial_numbers(serial_number_list)
+
+	args.serial_no = serial_number
+
 	s.append("items", {
 		"item_code": args.item,
 		"s_warehouse": args.source,