[cleanup] [minor] period closing voucher inherited from accounts_controller with testcases
diff --git a/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index c214a21..99282f5 100644
--- a/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -3,179 +3,102 @@
 
 from __future__ import unicode_literals
 import webnotes
-
 from webnotes.utils import cstr, flt, getdate
-from webnotes.model import db_exists
-from webnotes.model.doc import Document
-from webnotes.model.bean import copy_doclist
-from webnotes.model.code import get_obj
-from webnotes import msgprint
+from webnotes import msgprint, _
+from controllers.accounts_controller import AccountsController
 
-sql = webnotes.conn.sql
-	
-
-
-class DocType:
+class DocType(AccountsController):
 	def __init__(self,d,dl):
 		self.doc, self.doclist = d, dl
-		self.td, self.tc = 0, 0
 		self.year_start_date = ''
-		self.year_end_date = ''
 
+	def validate(self):
+		self.validate_account_head()
+		self.validate_posting_date()
+		self.validate_pl_balances()
+
+	def on_submit(self):
+		self.make_gl_entries()
+
+	def on_cancel(self):
+		webnotes.conn.sql("""delete from `tabGL Entry` 
+			where voucher_type = 'Period Closing Voucher' and voucher_no=%s""", self.doc.name)
 
 	def validate_account_head(self):
-		acc_det = sql("select debit_or_credit, is_pl_account, group_or_ledger, company \
-			from `tabAccount` where name = '%s'" % (self.doc.closing_account_head))
-
-		# Account should be under liability 
-		if cstr(acc_det[0][0]) != 'Credit' or cstr(acc_det[0][1]) != 'No':
-			msgprint("Account: %s must be created under 'Source of Funds'" % self.doc.closing_account_head)
-			raise Exception
-	 
-		# Account must be a ledger
-		if cstr(acc_det[0][2]) != 'Ledger':
-			msgprint("Account %s must be a ledger" % self.doc.closing_account_head)
-			raise Exception 
-		
-		# Account should belong to company selected 
-		if cstr(acc_det[0][3]) != self.doc.company:
-			msgprint("Account %s does not belong to Company %s ." % (self.doc.closing_account_head, self.doc.company))
-			raise Exception 
-
+		debit_or_credit, is_pl_account = webnotes.conn.get_value("Account", 
+			self.doc.closing_account_head, ["debit_or_credit", "is_pl_account"])
+			
+		if debit_or_credit != 'Credit' or is_pl_account != 'No':
+			webnotes.throw(_("Account") + ": " + self.doc.closing_account_head + 
+				_("must be a Liability account"))
 
 	def validate_posting_date(self):
-		yr = sql("""select year_start_date, adddate(year_start_date, interval 1 year)
-			from `tabFiscal Year` where name=%s""", (self.doc.fiscal_year, ))
-		self.year_start_date = yr and yr[0][0] or ''
-		self.year_end_date = yr and yr[0][1] or ''
-		
-		# Posting Date should be within closing year
-		if getdate(self.doc.posting_date) < getdate(self.year_start_date) or getdate(self.doc.posting_date) > getdate(self.year_end_date):
-			msgprint("Posting Date should be within Closing Fiscal Year")
-			raise Exception
+		from accounts.utils import get_fiscal_year
+		self.year_start_date = get_fiscal_year(self.doc.posting_date)[1]
 
-		# Period Closing Entry
-		pce = sql("select name from `tabPeriod Closing Voucher` \
-			where posting_date > '%s' and fiscal_year = '%s' and docstatus = 1" \
-			% (self.doc.posting_date, self.doc.fiscal_year))
+		pce = webnotes.conn.sql("""select name from `tabPeriod Closing Voucher`
+			where posting_date > %s and fiscal_year = %s and docstatus = 1""", 
+			(self.doc.posting_date, self.doc.fiscal_year))
 		if pce and pce[0][0]:
-			msgprint("Another Period Closing Entry: %s has been made after posting date: %s"\
-			 % (cstr(pce[0][0]), self.doc.posting_date))
-			raise Exception
+			webnotes.throw(_("Another Period Closing Entry") + ": " + cstr(pce[0][0]) + 
+				  _("has been made after posting date") + ": " + self.doc.posting_date)
 		 
-		
 	def validate_pl_balances(self):
-		income_bal = sql("select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) \
-			from `tabGL Entry` t1, tabAccount t2 where t1.account = t2.name \
-			and t1.posting_date between '%s' and '%s' and t2.debit_or_credit = 'Credit' \
-			and t2.group_or_ledger = 'Ledger' and t2.is_pl_account = 'Yes' and t2.docstatus < 2 \
-			and t2.company = '%s'" % (self.year_start_date, self.doc.posting_date, self.doc.company))
+		income_bal = webnotes.conn.sql("""
+			select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) 
+			from `tabGL Entry` t1, tabAccount t2 
+			where t1.account = t2.name and t1.posting_date between %s and %s 
+			and t2.debit_or_credit = 'Credit' and t2.is_pl_account = 'Yes' 
+			and t2.docstatus < 2 and t2.company = %s""", 
+			(self.year_start_date, self.doc.posting_date, self.doc.company))
 			
-		expense_bal = sql("select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) \
-			from `tabGL Entry` t1, tabAccount t2 where t1.account = t2.name \
-			and t1.posting_date between '%s' and '%s' and t2.debit_or_credit = 'Debit' \
-			and t2.group_or_ledger = 'Ledger' and t2.is_pl_account = 'Yes' and t2.docstatus < 2 \
-			and t2.company = '%s'" % (self.year_start_date, self.doc.posting_date, self.doc.company))
+		expense_bal = webnotes.conn.sql("""
+			select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0))
+			from `tabGL Entry` t1, tabAccount t2 
+			where t1.account = t2.name and t1.posting_date between %s and %s
+			and t2.debit_or_credit = 'Debit' and t2.is_pl_account = 'Yes' 
+			and t2.docstatus < 2 and t2.company=%s""", 
+			(self.year_start_date, self.doc.posting_date, self.doc.company))
 		
 		income_bal = income_bal and income_bal[0][0] or 0
 		expense_bal = expense_bal and expense_bal[0][0] or 0
 		
 		if not income_bal and not expense_bal:
-			msgprint("Both Income and Expense balances are zero. No Need to make Period Closing Entry.")
-			raise Exception
+			webnotes.throw(_("Both Income and Expense balances are zero. \
+				No Need to make Period Closing Entry."))
 		
+	def get_pl_balances(self):
+		"""Get balance for pl accounts"""
 		
-	def get_pl_balances(self, d_or_c):
-		"""Get account (pl) specific balance"""
-		acc_bal = sql("select	t1.account, sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) \
-			from `tabGL Entry` t1, `tabAccount` t2 where t1.account = t2.name and t2.group_or_ledger = 'Ledger' \
- 			and ifnull(t2.is_pl_account, 'No') = 'Yes' and ifnull(is_cancelled, 'No') = 'No' \
-			and t2.debit_or_credit = '%s' and t2.docstatus < 2 and t2.company = '%s' \
-			and t1.posting_date between '%s' and '%s' group by t1.account " \
-			% (d_or_c, self.doc.company, self.year_start_date, self.doc.posting_date))
-		return acc_bal
-
+		return webnotes.conn.sql("""
+			select t1.account, sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance
+			from `tabGL Entry` t1, `tabAccount` t2 
+			where t1.account = t2.name and ifnull(t2.is_pl_account, 'No') = 'Yes'
+			and t2.docstatus < 2 and t2.company = %s 
+			and t1.posting_date between %s and %s 
+			group by t1.account
+		""", (self.doc.company, self.year_start_date, self.doc.posting_date), as_dict=1)
 	 
-	def make_gl_entries(self, acc_det):
-		for a in acc_det:
-			if flt(a[1]):
-				fdict = {
-					'account': a[0], 
-					'cost_center': '', 
-					'against': '', 
-					'debit': flt(a[1]) < 0 and -1*flt(a[1]) or 0,
-					'credit': flt(a[1]) > 0 and flt(a[1]) or 0,
-					'remarks': self.doc.remarks, 
-					'voucher_type': self.doc.doctype, 
-					'voucher_no': self.doc.name, 
-					'transaction_date': self.doc.transaction_date, 
-					'posting_date': self.doc.posting_date, 
-					'fiscal_year': self.doc.fiscal_year, 
-					'against_voucher': '', 
-					'against_voucher_type': '', 
-					'company': self.doc.company, 
-					'is_opening': 'No', 
-					'aging_date': self.doc.posting_date
-				}
+	def make_gl_entries(self):
+		gl_entries = []
+		net_pl_balance = 0
+		pl_accounts = self.get_pl_balances()
+		for acc in pl_accounts:
+			if flt(acc.balance):
+				gl_entries.append(self.get_gl_dict({
+					"account": acc.account,
+					"debit": abs(flt(acc.balance)) if flt(acc.balance) < 0 else 0,
+					"credit": abs(flt(acc.balance)) if flt(acc.balance) > 0 else 0,
+				}))
 			
-				self.save_entry(fdict)
-	 
+				net_pl_balance += flt(acc.balance)
 
-	def save_entry(self, fdict, is_cancel = 'No'):
-		# Create new GL entry object and map values
-		le = Document('GL Entry')
-		for k in fdict:
-			le.fields[k] = fdict[k]
-		
-		le_obj = get_obj(doc=le)
-		# validate except on_cancel
-		if is_cancel == 'No':
-			le_obj.validate()
+		if net_pl_balance:
+			gl_entries.append(self.get_gl_dict({
+				"account": self.doc.closing_account_head,
+				"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
+				"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0
+			}))
 			
-			# update total debit / credit except on_cancel
-			self.td += flt(le.credit)
-			self.tc += flt(le.debit)
-
-		# save
-		le.save(1)
-		le_obj.on_update(adv_adj = '', cancel = '')
- 
-		 	
-	def validate(self):
-		# validate account head
-		self.validate_account_head()
-
-		# validate posting date
-		self.validate_posting_date()
-
-		# check if pl balance:
-		self.validate_pl_balances()
-
-
-	def on_submit(self):
-		
-		# Makes closing entries for Expense Account
-		in_acc_det = self.get_pl_balances('Credit')
-		self.make_gl_entries(in_acc_det)
-
-		# Makes closing entries for Expense Account
-		ex_acc_det = self.get_pl_balances('Debit')
-		self.make_gl_entries(ex_acc_det)
-
-
-		# Makes Closing entry for Closing Account Head
-		bal = self.tc - self.td
-		self.make_gl_entries([[self.doc.closing_account_head, flt(bal)]])
-
-
-	def on_cancel(self):
-		# get all submit entries of current closing entry voucher
-		gl_entries = sql("select account, debit, credit from `tabGL Entry` where voucher_type = 'Period Closing Voucher' and voucher_no = '%s' and ifnull(is_cancelled, 'No') = 'No'" % (self.doc.name))
-
-		# Swap Debit & Credit Column and make gl entry
-		for gl in gl_entries:
-			fdict = {'account': gl[0], 'cost_center': '', 'against': '', 'debit': flt(gl[2]), 'credit' : flt(gl[1]), 'remarks': "cancelled", 'voucher_type': self.doc.doctype, 'voucher_no': self.doc.name, 'transaction_date': self.doc.transaction_date, 'posting_date': self.doc.posting_date, 'fiscal_year': self.doc.fiscal_year, 'against_voucher': '', 'against_voucher_type': '', 'company': self.doc.company, 'is_opening': 'No', 'aging_date': 'self.doc.posting_date'}
-			self.save_entry(fdict, is_cancel = 'Yes')
-
-		# Update is_cancelled = 'Yes' to all gl entries for current voucher
-		sql("update `tabGL Entry` set is_cancelled = 'Yes' where voucher_type = '%s' and voucher_no = '%s'" % (self.doc.doctype, self.doc.name))
\ No newline at end of file
+		from accounts.general_ledger import make_gl_entries
+		make_gl_entries(gl_entries)
\ No newline at end of file
diff --git a/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
new file mode 100644
index 0000000..c21d63f
--- /dev/null
+++ b/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
@@ -0,0 +1,53 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+
+from __future__ import unicode_literals
+import unittest
+import webnotes
+
+class TestPeriodClosingVoucher(unittest.TestCase):
+	def test_closing_entry(self):
+		from accounts.doctype.journal_voucher.test_journal_voucher import test_records as jv_records
+		jv = webnotes.bean(copy=jv_records[2])
+		jv.insert()
+		jv.submit()
+		
+		jv1 = webnotes.bean(copy=jv_records[0])
+		jv1.doclist[2].account = "_Test Account Cost for Goods Sold - _TC"
+		jv1.doclist[2].debit = 600.0
+		jv1.doclist[1].credit = 600.0
+		jv1.insert()
+		jv1.submit()
+		
+		pcv = webnotes.bean(copy=test_record)
+		pcv.insert()
+		pcv.submit()
+		
+		gl_entries = webnotes.conn.sql("""select account, debit, credit
+			from `tabGL Entry` where voucher_type='Period Closing Voucher' and voucher_no=%s
+			order by account asc, debit asc""", pcv.doc.name, as_dict=1)
+
+		self.assertTrue(gl_entries)
+		
+		expected_gl_entries = sorted([
+			["_Test Account Reserves and Surplus - _TC", 200.0, 0.0],
+			["_Test Account Cost for Goods Sold - _TC", 0.0, 600.0],
+			["Sales - _TC", 400.0, 0.0]
+		])
+		for i, gle in enumerate(gl_entries):
+			self.assertEquals(expected_gl_entries[i][0], gle.account)
+			self.assertEquals(expected_gl_entries[i][1], gle.debit)
+			self.assertEquals(expected_gl_entries[i][2], gle.credit)
+		
+		
+test_dependencies = ["Customer", "Cost Center"]
+	
+test_record = [{
+	"doctype": "Period Closing Voucher", 
+	"closing_account_head": "_Test Account Reserves and Surplus - _TC",
+	"company": "_Test Company", 
+	"fiscal_year": "_Test Fiscal Year 2013", 
+	"posting_date": "2013-03-31", 
+	"remarks": "test"
+}]