[MERGE] account_test module

bzr revid: qdp-launchpad@openerp.com-20121206162139-gymgu2u8dic3kaxg
This commit is contained in:
Quentin (OpenERP) 2012-12-06 17:21:39 +01:00
commit f8f51e4eef
10 changed files with 556 additions and 0 deletions

View File

@ -0,0 +1,2 @@
import account_test
import report

View File

@ -0,0 +1,47 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (c) 2011 CCI Connect asbl (http://www.cciconnect.be) All Rights Reserved.
# Philmer <philmer@cciconnect.be>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name' : 'Accounting Consistency Tests',
'version' : '1.0',
'author' : 'OpenERP',
'category' : 'Accounting & Finance',
'website': 'http://www.openerp.com',
'description': """
Asserts on accounting.
======================
With this module you can manually check consistencies and inconsistencies of accounting module from menu Reporting/Accounting/Accounting Tests.
You can write a query in order to create Consistency Test and you will get the result of the test
in PDF format which can be accessed by Menu Reporting -> Accounting Tests, then select the test
and print the report from Print button in header area.
""",
'depends' : ['account'],
'data' : [
'security/ir.model.access.csv',
'account_test_view.xml',
'account_test_report.xml',
'account_test_data.xml',
],
'active': False,
'installable': True
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,65 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2005-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
#
# $Id: product_expiry.py 4304 2006-10-25 09:54:51Z ged $
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from osv import fields,osv
import pooler
import netsvc
import time
from xml import dom
CODE_EXEC_DEFAULT = '''\
res = []
cr.execute("select id, code from account_journal")
for record in cr.dictfetchall():
res.append(record['code'])
result = res
'''
class accounting_assert_test(osv.osv):
_name = "accounting.assert.test"
_order = "sequence"
_columns = {
'name': fields.char('Test Name', size=256, required=True, select=True, translate=True),
'desc': fields.text('Test Description', select=True, translate=True),
'code_exec': fields.text('Python code', required=True),
'active': fields.boolean('Active'),
'sequence': fields.integer('Sequence'),
}
_defaults = {
'code_exec': CODE_EXEC_DEFAULT,
'active': True,
'sequence': 10,
}
accounting_assert_test()

View File

@ -0,0 +1,172 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="accounting.assert.test" id="account_test_01">
<field name="sequence">1</field>
<field name="name">Test 1: General balance</field>
<field name="desc">Check the balance: Debit sum = Credit sum</field>
<field name="code_exec"><![CDATA[sql="""SELECT
sum(debit)-sum(credit) as balance
FROM account_move_line
"""
cr.execute(sql)
result=[]
res= cr.dictfetchall()
if res[0]['balance']!=0.0 and res[0]['balance'] is not None:
result.append(_('* The difference of the balance is: '))
result.append(res)
]]></field>
</record>
<record model="accounting.assert.test" id="account_test_02">
<field name="sequence">2</field>
<field name="name">Test 2: Opening a fiscal year</field>
<field name="desc">Check if the balance of the new opened fiscal year matches with last year's balance</field>
<field name="code_exec"><![CDATA[result = []
cr.execute("select coalesce(sum(debit),0) as debit_new_fyear,coalesce(sum(credit),0) as credit_new_fyear from account_move_line where period_id in (select id from account_period where state='draft' and special order by id desc limit 1);")
rec = cr.dictfetchall()
cr.execute("select coalesce(sum(debit),0) as debit_last_fyear,coalesce(sum(credit),0) as credit_last_fyear from account_move_line where period_id in (select period_id from account_fiscalyear where state='done' order by id desc limit 1);")
rec2= cr.dictfetchall()
if (rec2[0]['credit_last_fyear']-rec[0]['credit_new_fyear']!=0) or (rec2[0]['debit_last_fyear']-rec[0]['debit_new_fyear']!=0) :
result.append(_('* New fiscalyear debit and credit are:'))
result.append(rec[0])
result.append(_('* Last year debit and credit are:'))
result.append(rec2[0])
]]></field>
</record>
<record model="accounting.assert.test" id="account_test_03">
<field name="sequence">3</field>
<field name="name">Test 3: Movement lines</field>
<field name="desc">Check if movement lines are balanced and have the same date and period</field>
<field name="code_exec"><![CDATA[order_columns=['am_date','ml_date','am.period_id','ml.period_id','am.id']
sql="""SELECT
am.id as move_id,
sum(debit)-sum(credit) as balance,
am.period_id,
ml.period_id,
am.date as am_date,
ml.date as ml_date
FROM account_move am, account_move_line ml
WHERE
ml.move_id = am.id
GROUP BY am.name, am.id, am.state, am.period_id, ml.period_id,am.period_id, ml.period_id,am.date, ml.date
HAVING abs(sum(ml.debit-ml.credit)) <> 0 or am.period_id!=ml.period_id or (am.date!=ml.date)
"""
cr.execute(sql)
res = cr.dictfetchall()
if res:
res.insert(0,_('* The test failed for these movement lines:'))
result = res
]]></field>
</record>
<record model="accounting.assert.test" id="account_test_04">
<field name="sequence">4</field>
<field name="name">Test 4: Totally reconciled mouvements</field>
<field name="desc">Check if the totally reconciled movements are balanced</field>
<field name="code_exec"><![CDATA[res = []
cr.execute("SELECT distinct reconcile_id from account_move_line where reconcile_id is not null")
rec_ids = cr.dictfetchall()
for record in rec_ids :
cr.execute("SELECT distinct r.name,r.id from account_journal j,account_period p, account_move_reconcile r,account_move m, account_move_line ml where m.journal_id=j.id and m.period_id=p.id and ml.reconcile_id=%s and ml.move_id=m.id and ml.reconcile_id=r.id group by r.id,r.name having sum(ml.debit)-sum(ml.credit)<>0", (record['reconcile_id'],))
reconcile_ids=cr.dictfetchall()
if reconcile_ids:
res.append(', '.join(["Reconcile name: %(name)s, id=%(id)s " % r for r in reconcile_ids]))
result = res
if result:
result.insert(0,_('* The test failed for these reconciled items(id/name):'))
]]></field>
</record>
<record model="accounting.assert.test" id="account_test_05">
<field name="sequence">5</field>
<field name="name">Test 5.1 : Payable and Receivable accountant lines of reconciled invoices</field>
<field name="desc">Check that reconciled invoice for Sales/Purchases has reconciled entries for Payable and Receivable Accounts</field>
<field name="code_exec"><![CDATA[res = []
cr.execute("SELECT distinct inv.number,inv.id from account_invoice inv, account_move m, account_move_line ml, account_account a where m.id=ml.move_id and ml.account_id=a.id and a.type in ('receivable','payable') and inv.move_id=m.id and ml.reconcile_id is not null;")
records= cr.dictfetchall()
rec = [r['id'] for r in records]
res = reconciled_inv()
invoices = set(rec).difference(set(res))
result = [rec for rec in records if rec['id'] in invoices]
if result:
result.insert(0,_('* Invoices that need to be checked: '))
]]></field>
</record>
<record model="accounting.assert.test" id="account_test_05_2">
<field name="sequence">6</field>
<field name="name">Test 5.2 : Reconcilied invoices and Payable/Receivable accounts</field>
<field name="desc">Check that reconciled account moves, that define Payable and Receivable accounts, are belonging to reconciled invoices</field>
<field name="code_exec"><![CDATA[res = reconciled_inv()
result=[]
if res:
cr.execute("SELECT distinct inv.number,inv.id from account_invoice inv, account_move_line ml, account_account a, account_move m where m.id=ml.move_id and inv.move_id=m.id and inv.id=inv.move_id and ml.reconcile_id is null and a.type in ('receivable','payable') and ml.account_id=a.id and inv.id in %s",(tuple(res),))
records = cr.dictfetchall()
result = [rec for rec in records]
if result:
result.insert(0,_('* Invoices that need to be checked: '))
]]></field>
</record>
<record model="accounting.assert.test" id="account_test_06">
<field name="sequence">7</field>
<field name="name">Test 6 : Invoices status</field>
<field name="desc">Check that paid/reconciled invoices are not in 'Open' state</field>
<field name="code_exec"><![CDATA[
res = []
column_order = ['number','id','name','state']
if reconciled_inv():
cr.execute("select inv.name,inv.state,inv.id,inv.number from account_invoice inv where inv.state!='paid' and id in %s", (tuple(reconciled_inv()),))
res = cr.dictfetchall()
result = res
if result:
result.insert(0,_('* Invoices that need to be checked: '))
]]></field>
</record>
<record model="accounting.assert.test" id="account_test_06_1">
<field name="sequence">8</field>
<field name="name">Test 7: « View  » account type</field>
<field name="desc">Check that there's no move for any account with « View » account type</field>
<field name="code_exec"><![CDATA[column_order=['name','ref','id','date']
sql = "select id, name, ref, date from account_move_line where account_id in (select id from account_account where type = 'view')"
cr.execute(sql)
result = cr.dictfetchall()
if result:
result.insert(0,_('* Movement lines that need to be checked: '))
]]></field>
</record>
<record model="accounting.assert.test" id="account_test_07">
<field name="sequence">9</field>
<field name="name">Test 8 : Closing balance on bank statements</field>
<field name="desc">Check on bank statement that the Closing Balance = Starting Balance + sum of statement lines</field>
<field name="code_exec"><![CDATA[column_order = ['name','difference']
cr.execute("SELECT s.balance_start+sum(m.amount)-s.balance_end_real as difference, s.name from account_bank_statement s inner join account_bank_statement_line m on m.statement_id=s.id group by s.id, s.balance_start, s.balance_end_real,s.name having abs(s.balance_start+sum(m.amount)-s.balance_end_real) > 0.000000001;")
result = cr.dictfetchall()
if result:
result.insert(0,_('* Unbalanced bank statement that need to be checked: '))
]]></field>
</record>
<record model="accounting.assert.test" id="account_test_08">
<field name="sequence">10</field>
<field name="name">Test 9 : Accounts and partners on account moves</field>
<field name="desc">Check that general accounts and partners on account moves are active</field>
<field name="code_exec"><![CDATA[column_order=['partner_name','partner_active','account_name','move_line_id','period']
res = []
cr.execute("SELECT l.id as move_line_id,a.name as account_name,a.code as account_code,r.name as partner_name,r.active as partner_active,p.name as period from account_period p,res_partner r, account_account a,account_move_line l where l.account_id=a.id and l.partner_id=r.id and (not r.active or not a.active) and l.period_id=p.id")
res = cr.dictfetchall()
result = res
if result:
result.insert(0,_('* Here is the list of inactive partners and movement lines that are not correct: '))
]]></field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<report
id="account_assert_test_report"
model="accounting.assert.test"
name="account.test.assert.print"
rml="account_test/report/account_test.rml"
header="False"
string="Accounting Tests"/>
</data>
</openerp>

View File

@ -0,0 +1,85 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="account_assert_tree">
<field name="name">Tests</field>
<field name="model">accounting.assert.test</field>
<field name="arch" type="xml">
<tree string="Tests">
<field name="sequence"/>
<field name="name"/>
<field name="desc"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="account_assert_form">
<field name="name">Tests</field>
<field name="model">accounting.assert.test</field>
<field name="arch" type="xml">
<form string="Tests" version="7.0">
<sheet>
<group>
<group>
<field name="name"/>
<field name="sequence"/>
</group>
<group>
<field name="active"/>
</group>
</group>
<notebook>
<page string="Description">
<field name="desc" nolabel="1"/>
</page>
<page string="Expression">
<group string="Python Code">
<field colspan="4" name="code_exec" nolabel="1"/>
</group>
<group string="Code Help">
<pre>
Code should always set a variable named `result` with the result of your test, that can be a list or
a dictionary. If `result` is an empty list, it means that the test was succesful. Otherwise it will
try to translate and print what is inside `result`.
If the result of your test is a dictionary, you can set a variable named `column_order` to choose in
what order you want to print `result`'s content.
Should you need them, you can also use the following variables into your code:
* cr: cursor to the database
* uid: ID of the current user
In any ways, the code must be legal python statements with correct indentation (if needed).
Example:
sql = '''SELECT id, name, ref, date
FROM account_move_line
WHERE account_id IN (SELECT id FROM account_account WHERE type = 'view')
'''
cr.execute(sql)
result = cr.dictfetchall()
</pre>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="action_accounting_assert">
<field name="name">Accounting Tests</field>
<field name="res_model">accounting.assert.test</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create Accounting Test.
</p>
</field>
</record>
<menuitem name="Accounting Tests" parent="account.menu_finance_reporting" id="menu_action_license" action="action_accounting_assert"/>
</data>
</openerp>

View File

@ -0,0 +1,3 @@
import account_test_report
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,78 @@
<?xml version="1.0"?>
<document filename="test.pdf">
<template pageSize="(595.0,842.0)" title="Test" author="Martin Simon" allowSplitting="20">
<pageTemplate id="first">
<frame id="first" x1="57.0" y1="57.0" width="481" height="728"/>
</pageTemplate>
</template>
<stylesheet>
<blockTableStyle id="Standard_Outline">
<blockAlignment value="LEFT"/>
<blockValign value="TOP"/>
</blockTableStyle>
<blockTableStyle id="Tableau1">
<blockAlignment value="LEFT"/>
<blockValign value="TOP"/>
<lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/>
<lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/>
</blockTableStyle>
<blockTableStyle id="Table1">
<blockAlignment value="LEFT"/>
<blockValign value="TOP"/>
<lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/>
<lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/>
<lineStyle kind="LINEABOVE" colorName="#000000" start="0,1" stop="1,1"/>
<lineStyle kind="LINEBEFORE" colorName="#000000" start="0,0" stop="0,-1"/>
<lineStyle kind="LINEBEFORE" colorName="#000000" start="0,1" stop="0,-1"/>
<lineStyle kind="LINEBEFORE" colorName="#000000" start="1,0" stop="1,-1"/>
<lineStyle kind="LINEBEFORE" colorName="#000000" start="1,1" stop="1,-1"/>
<lineStyle kind="LINEAFTER" colorName="#000000" start="1,0" stop="1,-1"/>
<lineStyle kind="LINEAFTER" colorName="#000000" start="1,1" stop="1,-1"/>
</blockTableStyle>
<initialize>
<paraStyle name="all" alignment="justify"/>
</initialize>
<paraStyle name="Standard" fontName="Times-Roman"/>
<paraStyle name="Heading" fontName="Times-Roman" fontSize="16.0" leading="20" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="P10" fontName="Times-Roman" fontSize="16.0" leading="20" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="Text body" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="List" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="Caption" fontName="Times-Roman" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/>
<paraStyle name="Index" fontName="Times-Roman"/>
<paraStyle name="Table Contents" fontName="Times-Roman"/>
<images/>
</stylesheet>
<story>
<para style="Heading">Accouting tests on [[ datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") ]]</para>
<para style="Standard">
<font color="white"> </font>
</para>
<section>
<para style="Standard">[[repeatIn(objects,'o')]]</para>
<blockTable colWidths="482.0" style="Table1">
<tr>
<td>
<para style="Table Contents">[[ o.name ]]</para>
</td>
</tr>
<tr>
<td>
<para style="Table Contents"><i>[[ o.desc or '' ]]</i></para>
</td>
</tr>
</blockTable>
<blockTable colWidths="482.0" style="Tableau1">
<tr>
<td>
<para style="Table Contents">[[ repeatIn(execute_code(o.code_exec), 'test_result') ]]</para>
<para style="Table Contents">[[ test_result ]] </para>
</td>
</tr>
</blockTable>
<para style="Standard">
<font color="white"> </font>
</para>
</section>
</story>
</document>

View File

@ -0,0 +1,87 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# 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/>.
#
##############################################################################
import datetime
import time
from report import report_sxw
from tools.translate import _
#
# Use period and Journal for selection or resources
#
class report_assert_account(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context):
super(report_assert_account, self).__init__(cr, uid, name, context=context)
self.localcontext.update( {
'time': time,
'datetime': datetime,
'execute_code': self.execute_code,
})
def execute_code(self, code_exec):
def reconciled_inv():
"""
returns the list of invoices that are set as reconciled = True
"""
return self.pool.get('account.invoice').search(self.cr, self.uid, [('reconciled','=',True)])
def order_columns(item, cols=None):
"""
This function is used to display a dictionary as a string, with its columns in the order chosen.
:param item: dict
:param cols: list of field names
:returns: a list of tuples (fieldname: value) in a similar way that would dict.items() do except that the
returned values are following the order given by cols
:rtype: [(key, value)]
"""
if cols is None:
cols = item.keys()
return [(col, item.get(col)) for col in cols if col in item.keys()]
localdict = {
'cr': self.cr,
'uid': self.uid,
'reconciled_inv': reconciled_inv, #specific function used in different tests
'result': None, #used to store the result of the test
'column_order': None, #used to choose the display order of columns (in case you are returning a list of dict)
}
exec code_exec in localdict
result = localdict['result']
column_order = localdict.get('column_order', None)
if not isinstance(result, (tuple, list, set)):
result = [result]
if not result:
result = [_('The test was passed successfully')]
else:
def _format(item):
if isinstance(item, dict):
return ', '.join(["%s: %s" % (tup[0], tup[1]) for tup in order_columns(item, column_order)])
else:
return item
result = [_(_format(rec)) for rec in result]
return result
report_sxw.report_sxw('report.account.test.assert.print', 'accounting.assert.test', 'addons/account_test/report/account_test.rml', parser=report_assert_account, header=False)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,3 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_accounting_assert_test","accounting.assert.test","model_accounting_assert_test",base.group_system,1,0,0,1
"access_accounting_assert_test_manager","accounting.assert.test","model_accounting_assert_test",account.group_account_manager,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_accounting_assert_test accounting.assert.test model_accounting_assert_test base.group_system 1 0 0 1
3 access_accounting_assert_test_manager accounting.assert.test model_accounting_assert_test account.group_account_manager 1 0 0 0