[status updater] managed by controllers and commonified for sales and purchase
diff --git a/accounts/doctype/journal_voucher/journal_voucher.py b/accounts/doctype/journal_voucher/journal_voucher.py
index a5a4f10..2c5cd4f 100644
--- a/accounts/doctype/journal_voucher/journal_voucher.py
+++ b/accounts/doctype/journal_voucher/journal_voucher.py
@@ -158,7 +158,7 @@
 		if r:
 			self.doc.remark = ("\n").join(r)
 		else:
-			webnotes.msgprint("Remarks is mandatory", raise_exception=1)
+			webnotes.msgprint("User Remarks is mandatory", raise_exception=1)
 
 	def set_aging_date(self):
 		if self.doc.is_opening != 'Yes':
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py
index 95b56dc..cff7c53 100644
--- a/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -33,6 +33,28 @@
 		self.doc, self.doclist = d, dl 
 		self.tname = 'Purchase Invoice Item'
 		self.fname = 'entries'
+		self.status_updater = [{
+			'source_dt': 'Purchase Invoice Item',
+			'target_dt': 'Purchase Order Item',
+			'join_field': 'po_detail',
+			'target_field': 'billed_amt',
+			'target_parent_dt': 'Purchase Order',
+			'target_parent_field': 'per_billed',
+			'target_ref_field': 'import_amount',
+			'source_field': 'import_amount',
+			'percent_join_field': 'purchase_order',
+		},
+		{
+			'source_dt': 'Purchase Invoice Item',
+			'target_dt': 'Purchase Receipt Item',
+			'join_field': 'pr_detail',
+			'target_field': 'billed_amt',
+			'target_parent_dt': 'Purchase Receipt',
+			'target_parent_field': 'per_billed',
+			'target_ref_field': 'import_amount',
+			'source_field': 'import_amount',
+			'percent_join_field': 'purchase_receipt',
+		}]
 		
 	def validate(self):
 		super(DocType, self).validate()
@@ -411,8 +433,8 @@
 		self.make_gl_entries()
 				
 		self.update_against_document_in_jv()
-		purchase_controller.update_prevdoc_detail(self, is_submit = 1)
-
+		
+		self.update_prevdoc_status()
 
 	def make_gl_entries(self):
 		from accounts.general_ledger import make_gl_entries
@@ -523,7 +545,8 @@
 	def on_cancel(self):
 		from accounts.utils import remove_against_link_from_jv
 		remove_against_link_from_jv(self.doc.doctype, self.doc.name, "against_voucher")
-		get_obj(dt = 'Purchase Common').update_prevdoc_detail(self, is_submit = 0)
+		
+		self.update_prevdoc_status()
 		
 		self.make_cancel_gl_entries()
 		
diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py
index 6871b1e..120aba8 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/accounts/doctype/sales_invoice/sales_invoice.py
@@ -38,6 +38,35 @@
 		self.log = []
 		self.tname = 'Sales Invoice Item'
 		self.fname = 'entries'
+		self.status_updater = [{
+			'source_dt': 'Sales Invoice Item',
+			'target_field': 'billed_amt',
+			'target_ref_field': 'export_amount',
+			'target_dt': 'Sales Order Item',
+			'join_field': 'so_detail',
+			'target_parent_dt': 'Sales Order',
+			'target_parent_field': 'per_billed',
+			'source_field': 'export_amount',
+			'join_field': 'so_detail',
+			'percent_join_field': 'sales_order',
+			'status_field': 'billing_status',
+			'keyword': 'Billed'
+		}, 
+		{
+			'source_dt': 'Sales Invoice Item',
+			'target_dt': 'Delivery Note Item',
+			'join_field': 'dn_detail',
+			'target_field': 'billed_amt',
+			'target_parent_dt': 'Delivery Note',
+			'target_parent_field': 'per_billed',
+			'target_ref_field': 'export_amount',
+			'source_field': 'export_amount',
+			'percent_join_field': 'delivery_note',
+			'status_field': 'billing_status',
+			'keyword': 'Billed',
+			'no_tolerance': True,
+		}]
+		
 
 	def validate(self):
 		super(DocType, self).validate()
@@ -98,7 +127,9 @@
 				
 		self.set_buying_amount()
 		self.check_prev_docstatus()
-		get_obj("Sales Common").update_prevdoc_detail(1,self)
+		
+		self.update_status_updater_args()
+		self.update_prevdoc_status()
 		
 		# this sequence because outstanding may get -ve
 		self.make_gl_entries()
@@ -128,10 +159,30 @@
 		from accounts.utils import remove_against_link_from_jv
 		remove_against_link_from_jv(self.doc.doctype, self.doc.name, "against_invoice")
 
-		sales_com_obj.update_prevdoc_detail(0, self)
+		self.update_status_updater_args()
+		self.update_prevdoc_status()
 		
 		self.make_cancel_gl_entries()
 		
+	def update_status_updater_args(self):
+		if cint(self.doc.is_pos) and cint(self.doc.update_stock):
+			self.status_updater.append({
+				'source_dt':'Sales Invoice Item',
+				'target_dt':'Sales Order Item',
+				'target_parent_dt':'Sales Order',
+				'target_parent_field':'per_delivered',
+				'target_field':'delivered_qty',
+				'target_ref_field':'qty',
+				'source_field':'qty',
+				'join_field':'so_detail',
+				'percent_join_field':'sales_order',
+				'status_field':'delivery_status',
+				'keyword':'Delivered',
+				'second_source_dt': 'Delivery Note Item',
+				'second_source_field': 'qty',
+				'second_join_field': 'prevdoc_detail_docname'
+			})
+		
 	def on_update_after_submit(self):
 		self.validate_recurring_invoice()
 		self.convert_to_recurring()
@@ -351,7 +402,7 @@
 			
 			if ret.get("warehouse"):
 				ret["actual_qty"] = flt(webnotes.conn.get_value("Bin",
-					{"item_code": args.get("item_code"), "warehouse": args.get("warehouse")},
+					{"item_code": args.get("item_code"), "warehouse": ret.get("warehouse")},
 					"actual_qty"))
 		return ret
 
@@ -435,13 +486,15 @@
 			
 	def validate_customer(self):
 		"""	Validate customer name with SO and DN"""
-		for d in getlist(self.doclist,'entries'):
-			dt = d.delivery_note and 'Delivery Note' or d.sales_order and 'Sales Order' or ''
-			if dt:
-				dt_no = d.delivery_note or d.sales_order
-				cust = webnotes.conn.sql("select customer from `tab%s` where name = %s" % (dt, '%s'), dt_no)
-				if cust and cstr(cust[0][0]) != cstr(self.doc.customer):
-					msgprint("Customer %s does not match with customer of %s: %s." %(self.doc.customer, dt, dt_no), raise_exception=1)
+		if self.doc.customer:
+			for d in getlist(self.doclist,'entries'):
+				dt = d.delivery_note and 'Delivery Note' or d.sales_order and 'Sales Order' or ''
+				if dt:
+					dt_no = d.delivery_note or d.sales_order
+					cust = webnotes.conn.get_value(dt, dt_no, "customer")
+					if cust and cstr(cust) != cstr(self.doc.customer):
+						msgprint("Customer %s does not match with customer of %s: %s." 
+							%(self.doc.customer, dt, dt_no), raise_exception=1)
 			
 
 	def validate_customer_account(self):
@@ -1084,4 +1137,4 @@
 		webnotes.msgprint("Default Bank / Cash Account not set in Mode of Payment: %s. Please add a Default Account in Mode of Payment master." % mode_of_payment)
 	return {
 		"cash_bank_account": val
-	}
+	}
\ No newline at end of file
diff --git a/buying/doctype/purchase_common/purchase_common.py b/buying/doctype/purchase_common/purchase_common.py
index d5b563b..a0761cc 100644
--- a/buying/doctype/purchase_common/purchase_common.py
+++ b/buying/doctype/purchase_common/purchase_common.py
@@ -213,10 +213,11 @@
 					msgprint("Please check Item %s is not present in %s %s ." % (d.item_code, d.prevdoc_doctype, d.prevdoc_docname))
 					raise Exception
 				
-				# Check if Warehouse has been modified.
-				if not cstr(data[0]['warehouse']) == cstr(d.warehouse):
-					msgprint("Please check warehouse %s of Item %s which is not present in %s %s ." % (d.warehouse, d.item_code, d.prevdoc_doctype, d.prevdoc_docname))
-					raise Exception
+				if cstr(data[0]['warehouse']) and \
+						not cstr(data[0]['warehouse']) == cstr(d.warehouse):
+					msgprint("""Please check warehouse %s of Item %s 
+						which is not present in %s %s""" % (d.warehouse, d.item_code, 
+						d.prevdoc_doctype, d.prevdoc_docname), raise_exception=1)
 				
 				#	Check if UOM has been modified.
 				if not cstr(data[0]['uom']) == cstr(d.uom) and not cstr(d.prevdoc_doctype) == 'Material Request':
diff --git a/buying/doctype/purchase_order_item/purchase_order_item.txt b/buying/doctype/purchase_order_item/purchase_order_item.txt
index 01a144a..d941312 100755
--- a/buying/doctype/purchase_order_item/purchase_order_item.txt
+++ b/buying/doctype/purchase_order_item/purchase_order_item.txt
@@ -2,7 +2,7 @@
  {
   "creation": "2013-03-07 11:42:55", 
   "docstatus": 0, 
-  "modified": "2013-05-22 11:59:52", 
+  "modified": "2013-05-31 14:26:22", 
   "modified_by": "Administrator", 
   "owner": "Administrator"
  }, 
@@ -377,15 +377,12 @@
   "read_only": 1
  }, 
  {
-  "default": "0.00", 
   "doctype": "DocField", 
-  "fieldname": "billed_qty", 
-  "fieldtype": "Float", 
-  "hidden": 0, 
-  "label": "Billed Quantity", 
+  "fieldname": "billed_amt", 
+  "fieldtype": "Currency", 
+  "label": "Billed Amt", 
   "no_copy": 1, 
-  "oldfieldname": "billed_qty", 
-  "oldfieldtype": "Currency", 
+  "options": "currency", 
   "print_hide": 1, 
   "read_only": 1
  }, 
diff --git a/controllers/status_updater.py b/controllers/status_updater.py
new file mode 100644
index 0000000..ac06b2e
--- /dev/null
+++ b/controllers/status_updater.py
@@ -0,0 +1,177 @@
+# ERPNext - web based ERP (http://erpnext.com)
+# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes.utils import flt, cstr
+from webnotes import msgprint
+
+from webnotes.model.controller import DocListController
+
+class StatusUpdater(DocListController):
+	"""
+		Updates the status of the calling records
+		Delivery Note: Update Delivered Qty, Update Percent and Validate over delivery
+		Sales Invoice: Update Billed Amt, Update Percent and Validate over billing
+		Installation Note: Update Installed Qty, Update Percent Qty and Validate over installation
+	"""
+
+	def update_prevdoc_status(self):
+		self.update_qty()
+		self.validate_qty()
+	
+	def validate_qty(self):
+		"""
+			Validates qty at row level
+		"""
+		self.tolerance = {}
+		self.global_tolerance = None
+		
+		for args in self.status_updater:
+			# get unique transactions to update
+			for d in self.doclist:
+				if d.doctype == args['source_dt'] and d.fields.get(args["join_field"]):
+					args['name'] = d.fields[args['join_field']]
+
+					# get all qty where qty > target_field
+					item = webnotes.conn.sql("""select item_code, `%(target_ref_field)s`, 
+						`%(target_field)s`, parenttype, parent from `tab%(target_dt)s` 
+						where `%(target_ref_field)s` < `%(target_field)s` 
+						and name="%(name)s" and docstatus=1""" % args, as_dict=1)
+					if item:
+						item = item[0]
+						item['idx'] = d.idx
+						item['target_ref_field'] = args['target_ref_field'].replace('_', ' ')
+
+						if not item[args['target_ref_field']]:
+							msgprint("""As %(target_ref_field)s for item: %(item_code)s in \
+							%(parenttype)s: %(parent)s is zero, system will not check \
+							over-delivery or over-billed""" % item)
+						elif args.get('no_tolerance'):
+							item['reduce_by'] = item[args['target_field']] - \
+								item[args['target_ref_field']]
+							if item['reduce_by'] > .01:
+								msgprint("""
+									Row #%(idx)s: Max %(target_ref_field)s allowed for <b>Item \
+									%(item_code)s</b> against <b>%(parenttype)s %(parent)s</b> \
+									is <b>""" % item + cstr(item[args['target_ref_field']]) +
+									 """</b>.<br>You must reduce the %(target_ref_field)s by \
+									%(reduce_by)s""" % item, raise_exception=1)
+					
+						else:
+							self.check_overflow_with_tolerance(item, args)
+						
+	def check_overflow_with_tolerance(self, item, args):
+		"""
+			Checks if there is overflow condering a relaxation tolerance
+		"""
+	
+		# check if overflow is within tolerance
+		tolerance = self.get_tolerance_for(item['item_code'])
+		overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) / 
+		 	item[args['target_ref_field']]) * 100
+	
+		if overflow_percent - tolerance > 0.01:
+			item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100)
+			item['reduce_by'] = item[args['target_field']] - item['max_allowed']
+		
+			msgprint("""
+				Row #%(idx)s: Max %(target_ref_field)s allowed for <b>Item %(item_code)s</b> \
+				against <b>%(parenttype)s %(parent)s</b> is <b>%(max_allowed)s</b>. 
+				
+				If you want to increase your overflow tolerance, please increase tolerance %% in \
+				Global Defaults or Item master. 
+				
+				Or, you must reduce the %(target_ref_field)s by %(reduce_by)s
+				
+				Also, please check if the order item has already been billed in the Sales Order""" % 
+				item, raise_exception=1)
+				
+	def get_tolerance_for(self, item_code):
+		"""
+			Returns the tolerance for the item, if not set, returns global tolerance
+		"""
+		if self.tolerance.get(item_code): return self.tolerance[item_code]
+		
+		tolerance = flt(webnotes.conn.get_value('Item',item_code,'tolerance') or 0)
+
+		if not tolerance:
+			if self.global_tolerance == None:
+				self.global_tolerance = flt(webnotes.conn.get_value('Global Defaults', None, 
+					'tolerance'))
+			tolerance = self.global_tolerance
+		
+		self.tolerance[item_code] = tolerance
+		return tolerance
+	
+
+	def update_qty(self, change_modified=True):
+		"""
+			Updates qty at row level
+		"""
+		for args in self.status_updater:
+			# condition to include current record (if submit or no if cancel)
+			if self.doc.docstatus == 1:
+				args['cond'] = ' or parent="%s"' % self.doc.name
+			else:
+				args['cond'] = ' and parent!="%s"' % self.doc.name
+			
+			args['modified_cond'] = ''
+			if change_modified:
+				args['modified_cond'] = ', modified = now()'
+		
+			# update quantities in child table
+			for d in self.doclist:
+				if d.doctype == args['source_dt']:
+					# updates qty in the child table
+					args['detail_id'] = d.fields.get(args['join_field'])
+					
+					args['second_source_condition'] = ""
+					if args.get('second_source_dt') and args.get('second_source_field') \
+							and args.get('second_join_field'):
+						args['second_source_condition'] = """ + (select sum(%(second_source_field)s) 
+							from `tab%(second_source_dt)s` 
+							where `%(second_join_field)s`="%(detail_id)s" 
+							and (docstatus=1))""" % args
+			
+					if args['detail_id']:
+						webnotes.conn.sql("""update `tab%(target_dt)s` 
+							set %(target_field)s = (select sum(%(source_field)s) 
+								from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s" 
+								and (docstatus=1 %(cond)s)) %(second_source_condition)s
+							where name='%(detail_id)s'""" % args)
+		
+			# get unique transactions to update
+			for name in set([d.fields.get(args['percent_join_field']) for d in self.doclist 
+					if d.doctype == args['source_dt']]):
+				if name:
+					args['name'] = name
+				
+					# update percent complete in the parent table
+					webnotes.conn.sql("""update `tab%(target_parent_dt)s` 
+						set %(target_parent_field)s = (select sum(if(%(target_ref_field)s > 
+							ifnull(%(target_field)s, 0), %(target_field)s, 
+							%(target_ref_field)s))/sum(%(target_ref_field)s)*100 
+							from `tab%(target_dt)s` where parent="%(name)s") %(modified_cond)s
+						where name='%(name)s'""" % args)
+
+					# update field
+					if args.get('status_field'):
+						webnotes.conn.sql("""update `tab%(target_parent_dt)s` 
+							set %(status_field)s = if(ifnull(%(target_parent_field)s,0)<0.001, 
+								'Not %(keyword)s', if(%(target_parent_field)s>=99.99, 
+								'Fully %(keyword)s', 'Partly %(keyword)s'))
+							where name='%(name)s'""" % args)
\ No newline at end of file
diff --git a/patches/may_2013/p06_update_billed_amt_po_pr.py b/patches/may_2013/p06_update_billed_amt_po_pr.py
new file mode 100644
index 0000000..5084e98
--- /dev/null
+++ b/patches/may_2013/p06_update_billed_amt_po_pr.py
@@ -0,0 +1,24 @@
+# ERPNext - web based ERP (http://erpnext.com)
+# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import unicode_literals
+def execute():
+	import webnotes
+	webnotes.reload_doc("buying", "doctype", "purchase_order_item")
+	webnotes.reload_doc("stock", "doctype", "purchase_receipt_item")
+	for pi in webnotes.conn.sql("""select name from `tabPurchase Invoice` where docstatus = 1"""):
+		webnotes.get_obj("Purchase Invoice", pi[0], 
+			with_children=1).update_qty(change_modified=False)
\ No newline at end of file
diff --git a/patches/patch_list.py b/patches/patch_list.py
index ea61a04..d5b26a5 100644
--- a/patches/patch_list.py
+++ b/patches/patch_list.py
@@ -252,4 +252,5 @@
 	"patches.may_2013.p03_update_support_ticket",
 	"patches.may_2013.p04_reorder_level",
 	"patches.may_2013.p05_update_cancelled_gl_entries",
+	"patches.may_2013.p06_update_billed_amt_po_pr",
 ]
\ No newline at end of file
diff --git a/selling/doctype/installation_note/installation_note.py b/selling/doctype/installation_note/installation_note.py
index ea20d51..9b7344b 100644
--- a/selling/doctype/installation_note/installation_note.py
+++ b/selling/doctype/installation_note/installation_note.py
@@ -21,10 +21,7 @@
 from webnotes.model.bean import getlist
 from webnotes.model.code import get_obj
 from webnotes import msgprint
-from stock.utils import get_valid_serial_nos
-
-sql = webnotes.conn.sql
-	
+from stock.utils import get_valid_serial_nos	
 
 from utilities.transaction_base import TransactionBase
 
@@ -34,6 +31,19 @@
 		self.doclist = doclist
 		self.tname = 'Installation Note Item'
 		self.fname = 'installed_item_details'
+		self.status_updater = [{
+			'source_dt': 'Installation Note Item',
+			'target_dt': 'Delivery Note Item',
+			'target_field': 'installed_qty',
+			'target_ref_field': 'qty',
+			'join_field': 'prevdoc_detail_docname',
+			'target_parent_dt': 'Delivery Note',
+			'target_parent_field': 'per_installed',
+			'source_field': 'qty',
+			'percent_join_field': 'prevdoc_docname',
+			'status_field': 'installation_status',
+			'keyword': 'Installed'
+		}]
 
 	def validate(self):
 		self.validate_fiscal_year()
@@ -45,153 +55,114 @@
 		self.validate_mandatory()
 		self.validate_reference_value()
  
-	
-	#fetch delivery note details
-	#====================================
 	def pull_delivery_note_details(self):
 		self.validate_prev_docname()
-		self.doclist = get_obj('DocType Mapper', 'Delivery Note-Installation Note').dt_map('Delivery Note', 'Installation Note', self.doc.delivery_note_no, self.doc, self.doclist, "[['Delivery Note', 'Installation Note'],['Delivery Note Item', 'Installation Note Item']]")
+		self.doclist = get_obj('DocType Mapper', 'Delivery Note-Installation Note').dt_map(
+			'Delivery Note', 'Installation Note', self.doc.delivery_note_no, 
+			self.doc, self.doclist, "[['Delivery Note', 'Installation Note'], \
+			['Delivery Note Item', 'Installation Note Item']]")
 	
-	# Validates that Delivery Note is not pulled twice 
-	#============================================
 	def validate_prev_docname(self):
 		for d in getlist(self.doclist, 'installed_item_details'): 
 			if self.doc.delivery_note_no == d.prevdoc_docname:
-				msgprint(cstr(self.doc.delivery_note_no) + " delivery note details have already been pulled. ")
-				raise Exception, "Validation Error. "
-	
-	#Fiscal Year Validation
-	#================================
+				msgprint(cstr(self.doc.delivery_note_no) + 
+					" delivery note details have already been pulled", raise_exception=1)
+
 	def validate_fiscal_year(self):
-		get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.inst_date,'Installation Date')
-	
-	#	Validate Mandatory 
-	#===============================
+		get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year, self.doc.inst_date, 
+			'Installation Date')
+
 	def validate_mandatory(self):
-		# Amendment Date
 		if self.doc.amended_from and not self.doc.amendment_date:
 			msgprint("Please Enter Amendment Date")
 			raise Exception, "Validation Error. "
-	
-	# Validate values with reference document
-	#----------------------------------------
+
 	def validate_reference_value(self):
-		get_obj('DocType Mapper', 'Delivery Note-Installation Note', with_children = 1).validate_reference_value(self, self.doc.name)
+		mapper = get_obj('DocType Mapper', 'Delivery Note-Installation Note', with_children = 1)
+		mapper.validate_reference_value(self, self.doc.name)
 	
-	#check if serial no added
-	#-----------------------------
-	def is_serial_no_added(self,item_code,serial_no):
-		ar_required = sql("select has_serial_no from tabItem where name = '%s'" % item_code)
-		ar_required = ar_required and ar_required[0][0] or ''
+	def is_serial_no_added(self, item_code, serial_no):
+		ar_required = webnotes.conn.get_value("Item", item_code, "has_serial_no")
 		if ar_required == 'Yes' and not serial_no:
-			msgprint("Serial No is mandatory for item: "+ item_code)
-			raise Exception
+			msgprint("Serial No is mandatory for item: " + item_code, raise_exception=1)
 		elif ar_required != 'Yes' and cstr(serial_no).strip():
-			msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :"+item_code)
-			raise Exception
+			msgprint("If serial no required, please select 'Yes' in 'Has Serial No' in Item :" + 
+				item_code, raise_exception=1)
 	
-	#check if serial no exist in system
-	#-------------------------------------
 	def is_serial_no_exist(self, item_code, serial_no):
 		for x in serial_no:
-			chk = sql("select name from `tabSerial No` where name =%s", x)
-			if not chk:
-				msgprint("Serial No "+x+" does not exist in the system")
-				raise Exception
+			if not webnotes.conn.exists("Serial No", x):
+				msgprint("Serial No " + x + " does not exist in the system", raise_exception=1)
 	
-	#check if serial no already installed
-	#------------------------------------------
 	def is_serial_no_installed(self,cur_s_no,item_code):
 		for x in cur_s_no:
-			status = sql("select status from `tabSerial No` where name = %s", x)
+			status = webnotes.conn.sql("select status from `tabSerial No` where name = %s", x)
 			status = status and status[0][0] or ''
 			
 			if status == 'Installed':
-				msgprint("Item "+item_code+" with serial no. "+x+" already installed")
-				raise Exception, "Validation Error."
+				msgprint("Item "+item_code+" with serial no. " + x + " already installed", 
+					raise_exception=1)
 	
-	#get list of serial no from previous_doc
-	#----------------------------------------------
-	def get_prevdoc_serial_no(self, prevdoc_detail_docname, prevdoc_docname):
-		res = sql("select serial_no from `tabDelivery Note Item` where name = '%s' and parent ='%s'" % (prevdoc_detail_docname, prevdoc_docname))
-		return get_valid_serial_nos(res[0][0])
+	def get_prevdoc_serial_no(self, prevdoc_detail_docname):
+		serial_nos = webnotes.conn.get_value("Delivery Note Item", 
+			prevdoc_detail_docname, "serial_no")
+		return get_valid_serial_nos(serial_nos)
 		
-	#check if all serial nos from current record exist in resp delivery note
-	#---------------------------------------------------------------------------------
 	def is_serial_no_match(self, cur_s_no, prevdoc_s_no, prevdoc_docname):
-		for x in cur_s_no:
-			if not(x in prevdoc_s_no):
-				msgprint("Serial No. "+x+" not present in the Delivery Note "+prevdoc_docname, raise_exception = 1)
-				raise Exception, "Validation Error."
-	
-	#validate serial number
-	#----------------------------------------
+		for sr in cur_s_no:
+			if sr not in prevdoc_s_no:
+				msgprint("Serial No. " + sr + " is not matching with the Delivery Note " + 
+					prevdoc_docname, raise_exception = 1)
+
 	def validate_serial_no(self):
 		cur_s_no, prevdoc_s_no, sr_list = [], [], []
 		for d in getlist(self.doclist, 'installed_item_details'):
 			self.is_serial_no_added(d.item_code, d.serial_no)
-			
 			if d.serial_no:
-
 				sr_list = get_valid_serial_nos(d.serial_no, d.qty, d.item_code)
 				self.is_serial_no_exist(d.item_code, sr_list)
 				
-				prevdoc_s_no = self.get_prevdoc_serial_no(d.prevdoc_detail_docname, d.prevdoc_docname)
+				prevdoc_s_no = self.get_prevdoc_serial_no(d.prevdoc_detail_docname)
 				if prevdoc_s_no:
 					self.is_serial_no_match(sr_list, prevdoc_s_no, d.prevdoc_docname)
 				
 				self.is_serial_no_installed(sr_list, d.item_code)
 		return sr_list
-	
-	#validate installation date
-	#-------------------------------
+
 	def validate_installation_date(self):
 		for d in getlist(self.doclist, 'installed_item_details'):
 			if d.prevdoc_docname:
-				d_date = sql("select posting_date from `tabDelivery Note` where name=%s", d.prevdoc_docname)
-				d_date = d_date and d_date[0][0] or ''
-				
+				d_date = webnotes.conn.get_value("Delivery Note", d.prevdoc_docname, "posting_date")				
 				if d_date > getdate(self.doc.inst_date):
-					msgprint("Installation Date can not be before Delivery Date "+cstr(d_date)+" for item "+d.item_code)
-					raise Exception
+					msgprint("Installation Date can not be before Delivery Date " + cstr(d_date) + 
+						" for item "+d.item_code, raise_exception=1)
 	
 	def check_item_table(self):
 		if not(getlist(self.doclist, 'installed_item_details')):
-			msgprint("Please fetch items from Delivery Note selected")
-			raise Exception
+			msgprint("Please fetch items from Delivery Note selected", raise_exception=1)
 	
 	def on_update(self):
+		get_obj("Stock Ledger").scrub_serial_nos(self, 'installed_item_details')
 		webnotes.conn.set(self.doc, 'status', 'Draft')
 	
 	def on_submit(self):
 		valid_lst = []
 		valid_lst = self.validate_serial_no()
 		
-		get_obj("Sales Common").update_prevdoc_detail(1,self)
-		
 		for x in valid_lst:
-			wp = sql("select warranty_period from `tabSerial No` where name = '%s'"% x)
-			wp = wp and wp[0][0] or 0
-			if wp:
-				sql("update `tabSerial No` set maintenance_status = 'Under Warranty' where name = '%s'" % x)
-			
-			sql("update `tabSerial No` set status = 'Installed' where name = '%s'" % x)
-		
-		webnotes.conn.set(self.doc, 'status', 'Submitted')
+			if webnotes.conn.get_value("Serial No", x, "warranty_period"):
+				webnotes.conn.set_value("Serial No", x, "maintenance_status", "Under Warranty")
+			webnotes.conn.set_value("Serial No", x, "status", "Installed")
 
+		self.update_prevdoc_status()
+		webnotes.conn.set(self.doc, 'status', 'Submitted')
 	
 	def on_cancel(self):
-		cur_s_no = []
-		sales_com_obj = get_obj(dt = 'Sales Common')
-		sales_com_obj.update_prevdoc_detail(0,self)
-		
 		for d in getlist(self.doclist, 'installed_item_details'):
 			if d.serial_no:
-				#get current list of serial no
-				cur_serial_no = d.serial_no.replace(' ', '')
-				cur_s_no = cur_serial_no.split(',')
-		
-		for x in cur_s_no:
-			sql("update `tabSerial No` set status = 'Delivered' where name = '%s'" % x)
-			
+				d.serial_no = d.serial_no.replace(",", "\n")
+				for sr_no in d.serial_no.split("\n"):
+					webnotes.conn.set_value("Serial No", sr_no, "status", "Delivered")
+
+		self.update_prevdoc_status()
 		webnotes.conn.set(self.doc, 'status', 'Cancelled')
diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py
index 6ffd960..4902680 100644
--- a/stock/doctype/delivery_note/delivery_note.py
+++ b/stock/doctype/delivery_note/delivery_note.py
@@ -17,7 +17,7 @@
 from __future__ import unicode_literals
 import webnotes
 
-from webnotes.utils import cstr, flt, getdate, cint
+from webnotes.utils import cstr, flt, cint
 from webnotes.model.bean import getlist
 from webnotes.model.code import get_obj
 from webnotes import msgprint, _
@@ -34,6 +34,19 @@
 		self.doclist = doclist
 		self.tname = 'Delivery Note Item'
 		self.fname = 'delivery_note_details'
+		self.status_updater = [{
+			'source_dt': 'Delivery Note Item',
+			'target_dt': 'Sales Order Item',
+			'join_field': 'prevdoc_detail_docname',
+			'target_field': 'delivered_qty',
+			'target_parent_dt': 'Sales Order',
+			'target_parent_field': 'per_delivered',
+			'target_ref_field': 'qty',
+			'source_field': 'qty',
+			'percent_join_field': 'prevdoc_docname',
+			'status_field': 'delivery_status',
+			'keyword': 'Delivered'
+		}]
 
 	def validate_fiscal_year(self):
 		get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Posting Date')
@@ -261,8 +274,8 @@
 		sl_obj.update_serial_record(self, 'delivery_note_details', is_submit = 1, is_incoming = 0)
 		sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0)
 		
-		# update delivered qty in sales order
-		get_obj("Sales Common").update_prevdoc_detail(1,self)
+		# update delivered qty in sales order	
+		self.update_prevdoc_status()
 		
 		# create stock ledger entry
 		self.update_stock_ledger(update_stock = 1)
@@ -309,7 +322,8 @@
 		sl.update_serial_record(self, 'delivery_note_details', is_submit = 0, is_incoming = 0)
 		sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0)
 		
-		sales_com_obj.update_prevdoc_detail(0,self)
+		self.update_prevdoc_status()
+		
 		self.update_stock_ledger(update_stock = -1)
 		webnotes.conn.set(self.doc, 'status', 'Cancelled')
 		self.cancel_packing_slips()
diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py
index 8e81fa5..bc726c9 100644
--- a/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -33,6 +33,17 @@
 		self.tname = 'Purchase Receipt Item'
 		self.fname = 'purchase_receipt_details'
 		self.count = 0
+		self.status_updater = [{
+			'source_dt': 'Purchase Receipt Item',
+			'target_dt': 'Purchase Order Item',
+			'join_field': 'prevdoc_detail_docname',
+			'target_field': 'received_qty',
+			'target_parent_dt': 'Purchase Order',
+			'target_parent_field': 'per_received',
+			'target_ref_field': 'qty',
+			'source_field': 'qty',
+			'percent_join_field': 'prevdoc_docname',
+		}]
 
 	def validate_fiscal_year(self):
 		get_obj(dt = 'Purchase Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Transaction Date')
@@ -243,8 +254,7 @@
 		# Set status as Submitted
 		webnotes.conn.set(self.doc,'status', 'Submitted')
 
-		# Update Previous Doc i.e. update pending_qty and Status accordingly
-		purchase_controller.update_prevdoc_detail(self, is_submit = 1)
+		self.update_prevdoc_status()
 
 		# Update Serial Record
 		get_obj('Stock Ledger').update_serial_record(self, 'purchase_receipt_details', is_submit = 1, is_incoming = 1)
@@ -285,8 +295,7 @@
 		# 4.Update Bin
 		self.update_stock(is_submit = 0)
 
-		# 5.Update Material Requests Pending Qty and accordingly it's Status
-		pc_obj.update_prevdoc_detail(self, is_submit = 0)
+		self.update_prevdoc_status()
 
 		# 6. Update last purchase rate
 		pc_obj.update_last_purchase_rate(self, 0)
diff --git a/stock/doctype/purchase_receipt/purchase_receipt_list.js b/stock/doctype/purchase_receipt/purchase_receipt_list.js
index d20c352..c80f6ae 100644
--- a/stock/doctype/purchase_receipt/purchase_receipt_list.js
+++ b/stock/doctype/purchase_receipt/purchase_receipt_list.js
@@ -6,7 +6,7 @@
 	group_by: "`tabPurchase Receipt`.name",
 	prepare_data: function(data) {
 		if(data.purchase_order_no) {
-			data.purchase_order_no = data.purchase_order_no.split(",");
+			data.purchase_order_no = $.unique(data.purchase_order_no.split(","));
 			var po_list = [];
 			$.each(data.purchase_order_no, function(i, v){
 				if(po_list.indexOf(v)==-1) po_list.push(
diff --git a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt
index 8cef6a3..42bd929 100755
--- a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt
+++ b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt
@@ -2,7 +2,7 @@
  {
   "creation": "2013-03-07 11:42:59", 
   "docstatus": 0, 
-  "modified": "2013-05-22 12:01:08", 
+  "modified": "2013-05-31 14:26:41", 
   "modified_by": "Administrator", 
   "owner": "Administrator"
  }, 
@@ -453,18 +453,14 @@
   "width": "150px"
  }, 
  {
-  "default": "0.00", 
   "doctype": "DocField", 
-  "fieldname": "billed_qty", 
-  "fieldtype": "Float", 
-  "label": "Billed Quantity", 
+  "fieldname": "billed_amt", 
+  "fieldtype": "Currency", 
+  "label": "Billed Amt", 
   "no_copy": 1, 
-  "oldfieldname": "billed_qty", 
-  "oldfieldtype": "Currency", 
+  "options": "currency", 
   "print_hide": 1, 
-  "print_width": "100px", 
-  "read_only": 1, 
-  "width": "100px"
+  "read_only": 1
  }, 
  {
   "doctype": "DocField", 
diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py
index 5d7d1a8..4609429 100644
--- a/utilities/transaction_base.py
+++ b/utilities/transaction_base.py
@@ -19,9 +19,9 @@
 from webnotes.utils import load_json, cstr, flt, now_datetime
 from webnotes.model.doc import addchild
 
-from webnotes.model.controller import DocListController
+from controllers.status_updater import StatusUpdater
 
-class TransactionBase(DocListController):
+class TransactionBase(StatusUpdater):
 	def get_default_address_and_contact(self, party_type):
 		"""get a dict of default field values of address and contact for a given party type
 			party_type can be one of: customer, supplier"""