batch item distribution by oldest, serial no check
diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json
index b4a1405..5050541 100644
--- a/erpnext/stock/doctype/batch/batch.json
+++ b/erpnext/stock/doctype/batch/batch.json
@@ -14,6 +14,7 @@
  "engine": "InnoDB", 
  "fields": [
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -39,12 +40,13 @@
    "read_only": 0, 
    "remember_last_selected_value": 0, 
    "report_hide": 0, 
-   "reqd": 0, 
+   "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -76,6 +78,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -105,6 +108,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -136,6 +140,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -164,6 +169,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -194,6 +200,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -223,6 +230,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -253,6 +261,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -281,6 +290,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -311,6 +321,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -341,6 +352,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -369,6 +381,7 @@
    "unique": 0
   }, 
   {
+   "allow_bulk_edit": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -412,8 +425,8 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 5, 
- "modified": "2017-04-20 03:22:19.888058", 
- "modified_by": "Administrator", 
+ "modified": "2017-05-21 21:00:11.096038", 
+ "modified_by": "prateeksha@erpnext.com", 
  "module": "Stock", 
  "name": "Batch", 
  "owner": "harshada@webnotestech.com", 
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index 992d8fc..2ee8c62 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -66,13 +66,14 @@
 	return out
 
 @frappe.whitelist()
-def get_oldest_batch_qty(item_code, warehouse):
+def get_batches_by_oldest(item_code, warehouse):
 	'''Returns the oldest batch and qty for the given item_code and warehouse'''
 	batches = get_batch_qty(item_code = item_code, warehouse = warehouse)
-	oldest_date = min([frappe.get_value('Batch', batch.batch_no, 'expiry_date') for batch in batches])
-	for batch in batches:
-		if (frappe.get_value('Batch', batch.batch_no, 'expiry_date') == oldest_date):
-			return batch
+	batches_dates = [[batch, frappe.get_value('Batch', batch.batch_no, 'expiry_date')] for batch in batches]
+	batches_dates.sort(key=lambda tup: tup[1])
+
+	sorted_batches = [tup[0] for tup in batches_dates]
+	return sorted_batches
 
 @frappe.whitelist()
 def split_batch(batch_no, item_code, warehouse, qty, new_batch_id = None):
@@ -132,10 +133,4 @@
 		frappe.msgprint(_('Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement').format(frappe.bold(item_code)))
 		if throw: raise UnableToSelectBatchError
 
-	# oldest_expiry_date = min([batch.expiry_date for batch in batches])
-
-	# for batch in batches:
-	# 	if batch.expiry_date == oldest_expiry_date:
-	# 		batch_no = batch.batch_no
-
 	return batch_no
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 53fac4d..8f82b6a 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -76,7 +76,7 @@
 		frm.fields_dict.items.grid.refresh();
 		frm.cscript.toggle_related_fields(frm.doc);
 	},
-	validate: function(frm) {
+	after_save: function(frm) {
 		erpnext.select_batch_and_serial_no(frm);
 	},
 	company: function(frm) {
@@ -547,6 +547,63 @@
 });
 
 erpnext.select_batch_and_serial_no = (frm) => {
+	let get_warehouse = (item) => {
+		return cstr(item.s_warehouse) ? cstr(item.s_warehouse) : cstr(item.t_warehouse);
+	}
+
+	let show_modal_with_oldest_batch = (item, item_code, total_qty, warehouse, has_batch) => {
+		frappe.call({
+			method: 'erpnext.stock.doctype.batch.batch.get_batches_by_oldest',
+			args: {
+				warehouse: warehouse,
+				item_code: item_code
+			},
+			callback: (r) => {
+				if (r.message) {
+					batch_rows_by_oldest = [];
+					if(cstr(item.s_warehouse)) {
+						qty = total_qty;
+						for(var i = 0; i < r.message.length; i++) {
+							batch_row = {name: 'batch 1'};
+							batch_row.batch_no = r.message[i].batch_no;
+							batch_row.available_qty = r.message[i].qty;
+							if (parseInt(qty) <= parseInt(r.message[i].qty)) {
+								batch_row.selected_qty = qty;
+								batch_rows_by_oldest.push(batch_row);
+								break;
+							} else {
+								batch_row.selected_qty = r.message[i].qty;
+								qty -= r.message[i].qty;
+								batch_rows_by_oldest.push(batch_row);
+							}
+						}
+					}
+					erpnext.show_batch_serial_modal(frm, item, item_code, total_qty, warehouse, has_batch, batch_rows_by_oldest);
+				}
+			}
+		});
+	}
+
+	frm.doc.items.forEach(function(d) {
+		if(d.has_batch_no && !d.batch_no) {
+			show_modal_with_oldest_batch(d, d.item_code, d.qty, get_warehouse(d), 1);
+		} else if(d.has_serial_no && !d.serial_no) {
+			erpnext.show_batch_serial_modal(frm, d, d.item_code, d.qty, get_warehouse(d), 0);
+		}
+	});
+
+
+}
+
+erpnext.show_batch_serial_modal = (frm, item, item_code, qty, warehouse, has_batch, oldest = undefined) => {
+	let data = oldest ? oldest : []
+	let title = "";
+	let fields = [
+		{fieldname: 'item_code', read_only: 1, fieldtype:'Link', options: 'Item',
+			label: __('Item Code'), 'default': item_code},
+		{fieldtype:'Column Break'},
+		{fieldname: 'qty', read_only: 1, fieldtype:'Float', label: __('Qty'), 'default': qty},
+	];
 
 	let set_available_qty = (item_code, batch_no, warehouse, field) => {
 		frappe.call({
@@ -565,163 +622,170 @@
 		});
 	}
 
-	let get_warehouse = (i) => {
-		// console.log("i", i);
-		// console.log("cstr(i.s_warehouse) || cstr(i.t_warehouse)", cstr(i.s_warehouse) || cstr(i.t_warehouse));
-		return cstr(i.s_warehouse) ? cstr(i.s_warehouse) : cstr(i.t_warehouse);
+	if(has_batch) {
+		title = __("Select Batch Numbers");
+		fields = fields.concat([
+			{fieldtype:'Section Break', label: __('Batch No')},
+			{fieldname: 'batches', fieldtype: 'Table',
+				fields: [
+					{fieldtype:'Link', fieldname:'batch_no', options: 'Batch',
+						label: __('Select Batch'), in_list_view:1, get_query: function(doc) {
+							return {filters: {item: item_code }};
+						}},
+					{fieldtype:'Float', read_only: 1, fieldname:'available_qty',
+						label: __('Available'), in_list_view:1},
+					{fieldtype:'Float', fieldname:'selected_qty',
+						label: __('Qty'), in_list_view:1},
+				],
+				data: data,
+				get_data: function() {
+					return this.data;
+				},
+				on_setup: function(grid) {
+					var me = this;
+					grid.wrapper.on('change', 'input[data-fieldname="batch_no"]', function(e) {
+						let $row = $(this).closest('.grid-row');
+						let name = $row.attr('data-name');
+						let row = grid.grid_rows_by_docname[name];
+						row.on_grid_fields[2].set_value('0');
+						row.on_grid_fields[2].$input.trigger('change');
+						set_available_qty(item_code, row.doc.batch_no, warehouse, row.on_grid_fields[1]);
+					});
+				}
+			}
+		]);
+	} else {
+		title = __("Select Serial Numbers");
+		fields = fields.concat([
+			{fieldtype: 'Section Break', label: __('Serial No')},
+			{
+				fieldtype: 'Link', fieldname: 'serial_no_select', options: 'Serial No',
+				label: __('Select'),
+				get_query: function(doc) {
+					return { filters: {item_code: item_code}};
+				}
+			},
+			{fieldtype: 'Column Break'},
+			{fieldname: 'serial_no', fieldtype: 'Small Text'}
+		])
 	}
-	// console.log("available_qty", get_available_qty("_Test FG Item", 'tfi', 'Stores - A'));
 
-	// get_warehouse();
+	let dialog = new frappe.ui.Dialog({
+		title: title,
+		fields: fields
+	});
 
-	let show_modal = (item, item_code, qty, warehouse, has_batch, has_serial, oldest = undefined) => {
-		let data = oldest ? [oldest] : []
-		let fields = [
-			{fieldname: 'item_code', read_only: 1, fieldtype:'Link', options: 'Item',
-				label: __('Item Code'), 'default': item_code},
-			{fieldtype:'Column Break'},
-			{fieldname: 'qty', fieldtype:'Float', label: __('Qty'), 'default': qty},
-			{fieldtype:'Section Break'}
-		];
-		if(has_batch) {
-			fields.push(
-				{fieldname: 'batches', fieldtype: 'Table',
-					fields: [
-						{fieldtype:'Link', fieldname:'batch_no', options: 'Batch',
-							label: __('Select Batch'), in_list_view:1, get_query: function(doc) {
-								return {filters: {item: item_code }};
-							}},
-						{fieldtype:'Float', read_only: 1, fieldname:'available_qty',
-							label: __('Available'), in_list_view:1},
-						{fieldtype:'Float', fieldname:'selected_qty',
-							label: __('Qty'), in_list_view:1},
-					],
-					data: data,
-					get_data: function() {
-						return this.data;
-					},
-					bind_events: function(grid) {
-						var me = this;
-						grid.wrapper.on('change', 'input[data-fieldname="batch_no"]', function(e) {
-							let $row = $(this).closest('.grid-row');
-							let name = $row.attr('data-name');
-							let row = grid.grid_rows_by_docname[name];
-							// console.log("bind changed: ", $(this), name, row.doc.batch_no, row.doc.idx);
-							// console.log(grid.grid_rows_by_docname);
-							set_available_qty(item_code, row.doc.batch_no, warehouse, row.on_grid_fields[1]);
-						});
-					}
-			});
-		}
-		if(has_serial) {
-			fields.push(
-				{fieldname: 'serial_no', fieldtype: 'Small Text', label: __('Serial No')}
-			)
-		}
-		let dialog = new frappe.ui.Dialog({
-			title: __("Select Batches"),
-			fields: fields
+	let serial_no_link = dialog.fields_dict.serial_no_select;
+	if(serial_no_link) {
+		serial_no_link.$input.on('change', function(e) {
+			let serial_no_list = dialog.fields_dict.serial_no;
+			if(serial_no_link.get_value()) {
+				let new_line = '\n';
+				if(!serial_no_list.get_value()) {
+					new_line = '';
+				}
+				serial_no_list.set_value(serial_no_list.get_value() + new_line + serial_no_link.get_value());
+				serial_no_link.$input.val('');
+				serial_no_link.set_value('');
+			}
+		});
+	}
+
+	item_temp = {};
+	Object.assign(item_temp, item);
+	delete item_temp['batch_no'];
+	delete item_temp['qty'];
+	delete item_temp['idx'];
+
+	let validate_batch_dialog = (values) => {
+		var sum = 0;
+
+		values.batches.map((batch) => {
+			sum += parseInt(batch.selected_qty);
+			if(batch.batch_no && batch.selected_qty) {
+				if(parseInt(batch.selected_qty) > parseInt(batch.available_qty)
+					&& cstr(item.s_warehouse)) {
+					frappe.throw(__("Cannot select more than the available qty"));
+					return false;
+				}
+			} else {
+				if(!batch.batch_no) {
+					frappe.throw(__("Please select batch_no"));
+					return false;
+				} else {
+					frappe.throw(__("Please select qty"));
+					return false;
+				}
+			}
 		});
 
-		item_temp = {};
-		Object.assign(item_temp, item);
-
-		let validate_dialog_values = () => {
-			var values = dialog.get_values();
-
-			values.batches.map((batch) => {
-				if (batch.batch_no && batch.selected_qty) {
-					if (parseInt(batch.selected_qty) > parseInt(batch.available_qty)) {
-						frappe.throw(__("Cannot select more than the available qty"));
-						return false;
-					}
-				} else {
-					if (!batch.batch_no) {
-						frappe.throw(__("Please select batch_no"));
-						return false;
-					} else {
-						frappe.throw(__("Please select qty"));
-						return false;
-					}
-				}
+		// validate total_qty
+		if(sum > values.qty) {
+			frappe.confirm(__(`Total of selected quantities is greater than the previously
+				set item quantity. Update?`), function(){
+				return true;
 			});
-
-			// validate total_qty
+		} else if (sum < values.qty){
+			frappe.confirm(__(`Total of selected quantities is less than the previously
+				set item quantity. Update?`), function(){
+				return true;
+			});
+		} else {
 			return true;
 		}
-
-		dialog.set_primary_action(__('Get Items'), function() {
-
-			if (!validate_dialog_values()) {
-				return;
-			}
-
-			var values = dialog.get_values();
-
-			values.batches.map((batch, i) => {
-				if (i === 0) {
-					item.batch_no = batch.batch_no;
-					item.qty = batch.selected_qty;
-					console.log("item", item, frm.doc.items);
-				} else {
-					var row = frappe.model.add_child(frm.doc, "Stock Entry Detail", "items");
-					Object.assign(row, item_temp);
-					row.idx = i+1;
-					row.batch_no = batch.batch_no;
-					row.qty = batch.selected_qty;
-					console.log("row", row, frm.doc.items);
-				}
-				refresh_field("items");
-			});
-
-
-			refresh_field("items");
-			dialog.hide();
-		})
-
-		dialog.show();
 	}
 
-	// show_modal("_Test FG Item", 10, 'Stores - A', 1, 0);
+	let set_batched_items = () => {
+		var values = dialog.get_values();
+		if(!validate_batch_dialog(values)) {
+			return;
+		}
 
-	let show_modal_with_oldest_batch = (item, item_code, qty, warehouse, has_batch, has_serial_no) => {
-		frappe.call({
-			method: 'erpnext.stock.doctype.batch.batch.get_oldest_batch_qty',
-			args: {
-				warehouse: warehouse,
-				item_code: item_code
-			},
-			callback: (r) => {
-				if (r.message) {
-					oldest = {name: 'batch 1'};
-					oldest.batch_no = r.message.batch_no;
-					oldest.available_qty = r.message.qty;
-					// if required overall qty is less than available qty in oldest batch, preselect
-					if (parseInt(qty) < parseInt(r.message.qty)) {
-						oldest.selected_qty = qty;
-					}
-					show_modal(item, item_code, qty, warehouse, has_batch, has_serial_no, oldest);
-				}
+		values.batches.map((batch, i) => {
+			if(i === 0) {
+				item.batch_no = batch.batch_no;
+				item.qty = batch.selected_qty;
+				console.log("item", item, frm.doc.items);
+			} else {
+				var row = frm.add_child("items");
+				Object.assign(row, item_temp);
+				console.log("item", item_temp);
+
+				row.item_code = item_temp.item_code;
+				row.batch_no = batch.batch_no;
+				row.qty = batch.selected_qty;
+				console.log("row", row, frm.doc.items);
 			}
+			refresh_field("items");
 		});
 	}
 
-	frm.doc.items.forEach(function(d){
-		if(d.has_batch_no && !d.batch_no) {
-			console.log("item code, ");
+	let validate_serial_no_dialog = (values) => {
+		var serial_nos = values.serial_no || '';
+		if (!serial_nos || !serial_nos.replace(/\s/g, '').length) {
+			frappe.throw(__("Please enter serial numbers"));
+			return false;
 		}
-	});
+		return true;
+	}
 
-	frm.doc.items.forEach(function(d) {
-		if(d.has_batch_no && !d.batch_no) {
-			// console.log(d.item_code, get_warehouse(d));
-			show_modal_with_oldest_batch(d, d.item_code, d.qty, get_warehouse(d), 1, 0);
-			// show_modal(d, d.item_code, d.qty, get_warehouse(d), 1, 0 , oldest);
+	let set_serialized_items = () => {
+		var values = dialog.get_values();
+		if (!validate_serial_no_dialog(values)) {
+			return;
 		}
-		if(d.has_serial_no && !d.serial_no) {
-			show_modal(d, d.item_code, d.qty, get_warehouse(d), 0, 1);
+		item.serial_no = values.serial_no;
+	}
+
+	dialog.set_primary_action(__('Get Items'), function() {
+		if(has_batch) {
+			set_batched_items();
+		} else {
+			set_serialized_items();
 		}
-	});
 
-
-}
\ No newline at end of file
+		refresh_field("items");
+		dialog.hide();
+	})
+	dialog.show();
+}