fixes to valuation control
diff --git a/erpnext/stock/doctype/valuation_control/valuation_control.py b/erpnext/stock/doctype/valuation_control/valuation_control.py
index 131c71b..27170ce 100644
--- a/erpnext/stock/doctype/valuation_control/valuation_control.py
+++ b/erpnext/stock/doctype/valuation_control/valuation_control.py
@@ -1,81 +1,113 @@
 # Please edit this list and import only required elements
-import webnotes
+import webnotes, unittest
 
-from webnotes.utils import add_days, add_months, add_years, cint, cstr, date_diff, default_fields, flt, fmt_money, formatdate, generate_hash, getTraceback, get_defaults, get_first_day, get_last_day, getdate, has_common, month_name, now, nowdate, replace_newlines, sendmail, set_default, str_esc_quote, user_format, validate_email_add
-from webnotes.model import db_exists
-from webnotes.model.doc import Document, addchild, removechild, getchildren, make_autoname, SuperDocType
-from webnotes.model.doclist import getlist, copy_doclist
-from webnotes.model.code import get_obj, get_server_obj, run_server_obj, updatedb, check_syntax
-from webnotes import session, form, is_testing, msgprint, errprint
+from webnotes.utils import flt
+from webnotes.model.code import get_obj
 
-set = webnotes.conn.set
-sql = webnotes.conn.sql
-get_value = webnotes.conn.get_value
-in_transaction = webnotes.conn.in_transaction
-convert_to_lists = webnotes.conn.convert_to_lists
+class TestValuationControl(unittest.TestCase):
+	def setUp(self):
+		webnotes.conn.begin()
+
+	def tearDown(self):
+		webnotes.conn.rollback()
+		
+	def test_fifo_rate(self):
+		"""test fifo rate"""
+		fcfs_stack = [[40,500.0], [12,400.0]]
+		self.assertTrue(DocType(None, None).get_fifo_rate(fcfs_stack)==((40*500.0 + 12*400.0)/52.0))
 	
-# -----------------------------------------------------------------------------------------
+	def test_serial_no_value(self):
+		"""test serial no value"""
+		from webnotes.model.doc import Document
+
+		Document(fielddata = {
+			'doctype': 'Item',
+			'docstatus': 0,
+			'name': 'it',
+			'item_name': 'it',
+			'item_code': 'it',
+			'item_group': 'Default',
+			'is_stock_item': 'Yes',
+			'has_serial_no': 'Yes',
+			'stock_uom': 'Nos',
+			'is_sales_item': 'Yes',
+			'is_purchase_item': 'Yes',
+			'is_service_item': 'No',
+			'is_sub_contracted_item': 'No',
+			'is_pro_applicable': 'Yes',
+			'is_manufactured_item': 'Yes'		
+		}).save(1)
+		
+		s1 = Document(fielddata= {
+			'doctype':'Serial No',
+			'serial_no':'s1',
+			'item_code':'it',
+			'purchase_rate': 100.0
+		})
+		s2 = Document(fielddata = s1.fields.copy())
+		s3 = Document(fielddata = s1.fields.copy())
+		s4 = Document(fielddata = s1.fields.copy())
+		s1.save(1)
+		s2.purchase_rate = 120.0
+		s2.serial_no = 's2'
+		s2.save(1)
+		s3.purchase_rate = 130.0
+		s3.serial_no = 's3'
+		s3.save(1)
+		s4.purchase_rate = 150.0
+		s4.serial_no = 's4'
+		s4.save(1)
+		
+		r = DocType(None, None).get_serializable_inventory_rate('s1,s2,s3')
+		self.assertTrue(flt(r) - (100.0+120.0+130.0)/3 < 0.0001)
 
 
 class DocType:
 	def __init__(self, d, dl):
 		self.doc, self.doclist = d, dl
 
-	# Get FIFO Rate from Stack
-	# -------------------------
-	def get_fifo_rate(self, fcfs_stack, qty):
-		fcfs_val = 0
-		withdraw = flt(qty)
-		while withdraw:
-			batch = fcfs_stack[0]				
-			if batch[0] <= withdraw:
-				# not enough or exactly same qty in current batch, clear batch
-				withdraw -= batch[0]
-				fcfs_val += (flt(batch[0]) * flt(batch[1]))
-				fcfs_stack.pop(0)
-			else:
-				# all from current batch
-				fcfs_val += (flt(withdraw) * flt(batch[1]))
-				batch[0] -= withdraw
-				withdraw = 0
-		fcfs_rate = flt(fcfs_val) / flt(qty)
-		return fcfs_rate
-
-	# --------------------------------
-	# get serializable inventory rate
-	# --------------------------------
+	def get_fifo_rate(self, fcfs_stack):
+		"""get FIFO (average) Rate from Stack"""
+		if not fcfs_stack:
+			return 0.0
+			
+		total = sum(f[0] for f in fcfs_stack)
+		if not total:
+			return 0.0
+		
+		return sum(f[0] * f[1] for f in fcfs_stack) / total
+			
 	def get_serializable_inventory_rate(self, serial_no):
+		"""get average value of serial numbers"""
+		
 		sr_nos = get_obj("Stock Ledger").get_sr_no_list(serial_no)
-		tot = 0
-		for s in sr_nos:
-			serial_no = s.strip()
-			tot += flt(get_value('Serial No', serial_no, 'purchase_rate'))
-		return tot / len(sr_nos)
+		return webnotes.conn.sql("""select avg(ifnull(purchase_rate, 0)) 
+			from `tabSerial No` where name in ("%s")""" % '", "'.join(sr_nos))[0][0] or 0.0
 
 
-	# ---------------------
-	# get valuation method
-	# ---------------------
 	def get_valuation_method(self, item_code):
+		"""get valuation method from item or default"""
 		val_method = webnotes.conn.get_value('Item', item_code, 'valuation_method')
 		if not val_method:
+			from webnotes.utils import get_defaults
 			val_method = get_defaults().get('valuation_method', 'FIFO')
 		return val_method
 		
 
-	# Get Incoming Rate based on valuation method
-	# --------------------------------------------
 	def get_incoming_rate(self, posting_date, posting_time, item, warehouse, qty = 0, serial_no = ''):
+		"""Get Incoming Rate based on valuation method"""
 		in_rate = 0
 		val_method = self.get_valuation_method(item)
 		bin_obj = get_obj('Warehouse',warehouse).get_bin(item)
 		if serial_no:
 			in_rate = self.get_serializable_inventory_rate(serial_no)
 		elif val_method == 'FIFO':
-			in_rate = 0
+			# get rate based on the last item value?
 			if qty:
 				prev_sle = bin_obj.get_prev_sle(posting_date, posting_time)
-				fcfs_stack = eval(prev_sle.get('fcfs_stack', '[]') or '[]')
+				if not prev_sle:
+					return 0.0
+				fcfs_stack = eval(prev_sle.get('fcfs_stack', '[]'))
 				in_rate = fcfs_stack and self.get_fifo_rate(fcfs_stack, qty) or 0
 		elif val_method == 'Moving Average':
 			prev_sle = bin_obj.get_prev_sle(posting_date, posting_time)