Initial commit for analytic_account module

This commit is contained in:
Cédric Krier committed 2008-05-23 15:04:38 +02:00
commit fe26763982
8 files changed
+775

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
# Copyright (c) 2004-2006 Tiny SPRL. (http://tiny.be)
# Copyright (c) 2007-2008 Cedric Krier.
#
# 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 contact 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 account import *
from line import *
+22
View File
@@ -0,0 +1,22 @@
{
'name': 'Analytic Account',
'version': '0.1',
'author': 'B2CK',
'email': 'info@b2ck.com',
'website': 'http://www.tryton.org/',
'category': 'Generic Modules',
'description': 'Financial and Accounting Module',
'depends': [
'ir',
'company',
'currency',
'account',
'relationship',
'res',
],
'xml': [
'analytic_account.xml',
'account.xml',
'line.xml',
],
}
+270
View File
@@ -0,0 +1,270 @@
"Account"
from trytond.osv import fields, OSV
from trytond.wizard import Wizard, WizardOSV
from decimal import Decimal
class Account(OSV):
'Analytic Account'
_name = 'analytic_account.account'
_description = __doc__
name = fields.Char('Name', required=True, translate=True, select=1)
complete_name = fields.Function('get_complete_name', type='char',
string='Name')
code = fields.Char('Code', select=1)
active = fields.Boolean('Active', select=2)
company = fields.Many2One('company.company', 'Company')
currency = fields.Many2One('currency.currency', 'Currency', required=True)
type = fields.Selection([
('view', 'View'),
('normal', 'Normal'),
], 'Type', required=True)
parent = fields.Many2One('analytic_account.account', 'Parent', select=2)
childs = fields.One2Many('analytic_account.account', 'parent', 'Childs')
#TODO fix digits depend of the currency
balance = fields.Function('get_balance', digits=(16, 2), string='Balance')
credit = fields.Function('get_credit_debit', digits=(16, 2), string='Credit')
debit = fields.Function('get_credit_debit', digits=(16, 2), string='Debit')
state = fields.Selection([
('draft', 'Draft'),
('opened', 'Opened'),
('closed', 'Closed'),
], 'State', required=True)
note = fields.Text('Note')
display_balance = fields.Selection([
('debit-credit', 'Debit - Credit'),
('credit-debit', 'Credit - Debit'),
], 'Display Balance', required=True)
def __init__(self):
super(Account, self).__init__()
self._constraints += [
('check_recursion',
'Error! You can not create recursive account.', ['parent']),
]
self._order.insert(0, ('code', 'ASC'))
def default_active(self, cursor, user, context=None):
return True
def default_company(self, cursor, user, context=None):
company_obj = self.pool.get('company.company')
if context is None:
context = {}
if context.get('company'):
return company_obj.name_get(cursor, user, context['company'],
context=context)[0]
return False
def default_currency(self, cursor, user, context=None):
company_obj = self.pool.get('company.company')
currency_obj = self.pool.get('currency.currency')
if context is None:
context = {}
if context.get('company'):
company = company_obj.browse(cursor, user, context['company'],
context=context)
return currency_obj.name_get(cursor, user, company.currency.id,
context=context)[0]
return False
def default_type(self, cursor, user, context=None):
return 'normal'
def default_state(self, cursor, user, context=None):
return 'draft'
def default_display_balance(self, cursor, user, context=None):
return 'credit-debit'
def get_complete_name(self, cursor, user, ids, name, arg, context=None):
res = self.name_get(cursor, user, ids, context=context)
return dict(res)
def get_balance(self, cursor, user, ids, name, arg, context=None):
res = {}
line_obj = self.pool.get('analytic_account.line')
currency_obj = self.pool.get('currency.currency')
child_ids = self.search(cursor, user, [('parent', 'child_of', ids)],
context=context)
all_ids = {}.fromkeys(ids + child_ids).keys()
id2account = {}
accounts = self.browse(cursor, user, all_ids, context=context)
for account in accounts:
id2account[account.id] = account
line_query = line_obj.query_get(cursor, user, context=context)
cursor.execute('SELECT a.id, ' \
'SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))), ' \
'l.currency ' \
'FROM analytic_account_account a ' \
'LEFT JOIN analytic_account_line l ' \
'ON (a.id = l.account) ' \
'WHERE a.type != \'view\' ' \
'AND a.id IN (' + \
','.join(['%s' for x in all_ids]) + ') ' \
'AND ' + line_query + ' ' \
'AND a.active ' \
'GROUP BY a.id, l.currency', all_ids)
account_sum = {}
id2currency = {}
for account_id, sum, currency_id in cursor.fetchall():
account_sum.setdefault(account_id, Decimal('0.0'))
if currency_id != id2account[account_id].currency.id:
currency = None
if currency_id in id2currency:
currency = id2currency[currency_id]
else:
currency = currency_obj.browse(cursor, user, currency_id,
context=context)
id2currency[currency.id] = currency
account_sum[account_id] += currency_obj.compute(cursor, user,
currency, sum, id2account[account_id].currency,
round=True, context=context)
else:
account_sum[account_id] += currency_obj.round(cursor, user,
id2account[account_id].currency, sum)
for account_id in ids:
res.setdefault(account_id, Decimal('0.0'))
child_ids = self.search(cursor, user, [
('parent', 'child_of', [account_id]),
], context=context)
to_currency = id2account[account_id].currency
for child_id in child_ids:
from_currency = id2account[child_id].currency
res[account_id] += currency_obj.compute(cursor, user,
from_currency, account_sum.get(child_id, Decimal('0.0')),
to_currency, round=True, context=context)
res[account_id] = currency_obj.round(cursor, user, to_currency,
res[account_id])
if id2account[account_id].display_balance == 'credit-debit':
res[account_id] = - res[account_id]
return res
def get_credit_debit(self, cursor, user, ids, name, arg, context=None):
res = {}
line_obj = self.pool.get('analytic_account.line')
currency_obj = self.pool.get('currency.currency')
if name not in ('credit', 'debit'):
raise Exception('Bad argument')
id2account = {}
accounts = self.browse(cursor, user, ids, context=context)
for account in accounts:
res[account.id] = Decimal('0.0')
id2account[account.id] = account
line_query = line_obj.query_get(cursor, user, context=context)
cursor.execute('SELECT a.id, ' \
'SUM(COALESCE(l.' + name + ', 0)), ' \
'l.currency ' \
'FROM analytic_account_account a ' \
'LEFT JOIN analytic_account_line l ' \
'ON (a.id = l.account) ' \
'WHERE a.type != \'view\' ' \
'AND a.id IN (' + \
','.join(['%s' for x in ids]) + ') ' \
'AND ' + line_query + ' ' \
'AND a.active ' \
'GROUP BY a.id, l.currency', ids)
id2currency = {}
for account_id, sum, currency_id in cursor.fetchall():
if currency_id != id2account[account_id].currency.id:
currency = None
if currency_id in id2currency:
currency = id2currency[currency_id]
else:
currency = currency_obj.browse(cursor, user, currency_id,
context=context)
id2currency[currency.id] = currency
res[account_id] += currency_obj.compute(cursor, user,
currency, sum, id2account[account_id].currency,
round=True, context=context)
else:
res[account_id] += currency_obj.round(cursor, user,
id2account[account_id].currency, sum)
return res
def name_search(self, cursor, user, name='', args=None, operator='ilike',
context=None, limit=None):
if name:
ids = self.search(cursor, user,
[('code', 'like', name + '%')] + args,
limit=limit, context=context)
if not ids:
ids = self.search(cursor, user,
[(self._rec_name, operator, name)] + args,
limit=limit, context=context)
else:
ids = self.search(cursor, user, args, limit=limit, context=context)
res = self.name_get(cursor, user, ids, context=context)
return res
def name_get(self, cursor, user, ids, context=None):
if not ids:
return []
if isinstance(ids, (int, long)):
ids = [ids]
return [(r['id'], r['code'] and r['code'] + ' - ' + str(r[self._rec_name]) \
or str(r[self._rec_name])) for r in self.read(cursor, user, ids,
[self._rec_name, 'code'], context=context, load='_classic_write')]
Account()
class OpenChartAccountInit(WizardOSV):
_name = 'analytic_account.account.open_chart_account.init'
start_date = fields.Date('Start Date')
end_date = fields.Date('End Date')
OpenChartAccountInit()
class OpenChartAccount(Wizard):
'Open Chart Of Account'
_name = 'analytic_account.account.open_chart_account'
states = {
'init': {
'result': {
'type': 'form',
'object': 'analytic_account.account.open_chart_account.init',
'state': [
('end', 'Cancel', 'gtk-cancel'),
('open', 'Open', 'gtk-ok', True),
],
},
},
'open': {
'result': {
'type': 'action',
'action': '_action_open_chart',
'state': 'end',
},
},
}
def _action_open_chart(self, cursor, user, data, context=None):
model_data_obj = self.pool.get('ir.model.data')
act_window_obj = self.pool.get('ir.action.act_window')
model_data_ids = model_data_obj.search(cursor, user, [
('fs_id', '=', 'act_account_tree2'),
('module', '=', 'analytic_account'),
], limit=1, context=context)
model_data = model_data_obj.browse(cursor, user, model_data_ids[0],
context=context)
res = act_window_obj.read(cursor, user, model_data.db_id, context=context)
res['context'] = str({
'start_date': data['form']['start_date'],
'end_date': data['form']['end_date']
})
return res
OpenChartAccount()
+157
View File
@@ -0,0 +1,157 @@
<?xml version="1.0"?>
<tryton>
<data>
<menuitem name="Analytic Account" parent="account.menu_account_configuration"
id="menu_analytic_account_configuration"/>
<record model="ir.ui.view" id="account_view_form">
<field name="model">analytic_account.account</field>
<field name="type">form</field>
<field name="arch" type="xml">
<![CDATA[
<form string="Analytic Account" col="6">
<label name="name"/>
<field name="name"/>
<label name="code"/>
<field name="code"/>
<label name="active"/>
<field name="active" xexpand="0" width="100"/>
<notebook colspan="6">
<page string="General Information">
<label name="parent"/>
<field name="parent"/>
<label name="type"/>
<field name="type"/>
<label name="company"/>
<field name="company"/>
<label name="currency"/>
<field name="currency"/>
<label name="display_balance"/>
<field name="display_balance"/>
</page>
<page string="Notes">
<field name="note"/>
</page>
</notebook>
<group colspan="6" col="2">
<label name="state"/>
<field name="state"/>
</group>
</form>
]]>
</field>
</record>
<record model="ir.ui.view" id="account_view_tree">
<field name="model">analytic_account.account</field>
<field name="type">tree</field>
<field name="field_childs">childs</field>
<field name="arch" type="xml">
<![CDATA[
<tree string="Analytic Accounts">
<field name="complete_name"/>
<field name="company" select="2"/>
<field name="type" select="2"/>
<field name="active" select="2" tree_invisible="1"/>
<field name="name" select="1" tree_invisible="1"/>
<field name="code" select="1" tree_invisible="1"/>
</tree>
]]>
</field>
</record>
<record model="ir.action.act_window" id="act_account_form">
<field name="name">Analytic Accounts</field>
<field name="res_model">analytic_account.account</field>
<field name="view_type">form</field>
</record>
<record model="ir.action.act_window.view" id="act_account_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="account_view_tree"/>
<field name="act_window" ref="act_account_form"/>
</record>
<record model="ir.action.act_window.view" id="act_account_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="account_view_form"/>
<field name="act_window" ref="act_account_form"/>
</record>
<menuitem parent="menu_analytic_account_configuration"
action="act_account_form" id="menu_account_form"/>
<record model="ir.action.act_window" id="act_account_tree">
<field name="name">Analytic Accounts</field>
<field name="res_model">analytic_account.account</field>
<field name="view_type">tree</field>
<field name="domain">[('parent', '=', False)]</field>
</record>
<record model="ir.action.act_window.view" id="act_account_tree_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="account_view_tree"/>
<field name="act_window" ref="act_account_tree"/>
</record>
<menuitem parent="menu_account_form"
action="act_account_tree" id="menu_account_tree"/>
<record model="ir.ui.view" id="account_view_tree2">
<field name="model">analytic_account.account</field>
<field name="type">tree</field>
<field name="field_childs">childs</field>
<field name="priority" eval="20"/>
<field name="arch" type="xml">
<![CDATA[
<tree string="Analytic Accounts">
<field name="name"/>
<field name="code"/>
<field name="debit"/>
<field name="credit"/>
<field name="balance"/>
<field name="currency"/>
</tree>
]]>
</field>
</record>
<record model="ir.action.act_window" id="act_account_tree2">
<field name="name">Analytic Accounts</field>
<field name="res_model">analytic_account.account</field>
<field name="view_type">tree</field>
<field name="domain">[('parent', '=', False)]</field>
</record>
<record model="ir.action.act_window.view" id="act_account_tree2_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="account_view_tree2"/>
<field name="act_window" ref="act_account_tree2"/>
</record>
<record model="ir.action.wizard" id="act_open_chart_account">
<field name="name">Open Chart of Analytic Account</field>
<field name="wiz_name">analytic_account.account.open_chart_account</field>
</record>
<menuitem parent="account.menu_charts" action="act_open_chart_account"
icon="STOCK_INDENT" id="menu_open_chart_account"/>
<record model="ir.model.access" id="access_account">
<field name="model" search="[('model', '=', 'analytic_account.account')]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<record model="ir.model.access" id="access_account_account_admin">
<field name="model" search="[('model', '=', 'analytic_account.account')]"/>
<field name="group" ref="group_analytic_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_unlink" eval="True"/>
</record>
<record model="ir.ui.view" id="open_chart_account_init_view_form">
<field name="model">analytic_account.account.open_chart_account.init</field>
<field name="type">form</field>
<field name="arch" type="xml">
<![CDATA[
<form string="Open Chart of Analytic Account" col="2">
<label name="start_date"/>
<field name="start_date"/>
<label name="end_date"/>
<field name="end_date"/>
</form>
]]>
</field>
</record>
</data>
</tryton>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<tryton>
<data>
<record model="res.group" id="group_analytic">
<field name="name">Analytic</field>
</record>
<record model="res.group" id="group_analytic_admin">
<field name="name">Analytic Admin</field>
</record>
<record model="res.user" id="res.user_admin">
<field name="groups" eval="[('add', ref('group_analytic')), ('add', ref('group_analytic_admin'))]"/>
</record>
</data>
</tryton>
+128
View File
@@ -0,0 +1,128 @@
"Line"
from trytond.osv import fields, OSV
from trytond.wizard import Wizard
import datetime
class Line(OSV):
'Analytic Line'
_name = 'analytic_account.line'
_description = __doc__
name = fields.Char('Name', required=True)
debit = fields.Numeric('Debit', digits=(16, 2))
credit = fields.Numeric('Credit', digits=(16, 2))
currency = fields.Many2One('currency.currency', 'Currency', required=True)
account = fields.Many2One('analytic_account.account', 'Account',
required=True, select=1, domain=[('type', '!=', 'view')])
move_line = fields.Many2One('account.move.line', 'Account Move Line',
ondelete='CASCADE', required=True)
journal = fields.Many2One('account.journal', 'Journal', required=True,
select=1)
date = fields.Date('Date', required=True)
reference = fields.Char('Reference')
party = fields.Many2One('relationship.party', 'Party')
active = fields.Boolean('Active', select=2)
def __init__(self):
super(Line, self).__init__()
self._sql_constraints += [
('credit_debit',
'CHECK((credit * debit = 0.0) AND (credit + debit >= 0.0))',
'Wrong credit/debit values!'),
]
self._constraints += [
('check_account', 'You can not create move line \n' \
'on view/inactive account!', ['account']),
]
self._order.insert(0, ('date', 'ASC'))
def default_currency(self, cursor, user, context=None):
company_obj = self.pool.get('company.company')
currency_obj = self.pool.get('currency.currency')
if context is None:
context = {}
if context.get('company'):
company = company_obj.browse(cursor, user, context['company'],
context=context)
return currency_obj.name_get(cursor, user, company.currency.id,
context=context)[0]
return False
def default_date(self, cursor, user, context=None):
return datetime.date.today()
def default_active(self, cursor, user, context=None):
return True
def query_get(self, cursor, user, obj='l', context=None):
'''
Return SQL clause for analytic line depending of the context.
obj is the SQL alias of the analytic_account_line in the query.
'''
if context is None:
context = {}
res = obj + '.active '
if context.get('start_date'):
res += 'AND ' + obj + '.date >= ' + str(context['start_date'])
if context.get('end_date'):
res += 'AND ' + obj + '.date <= ' + str(context['end_date'])
return res
def check_account(self, cursor, user, ids):
for line in self.browse(cursor, user, ids):
if line.account.type == 'view':
return False
if not line.account.active:
return False
return True
Line()
class MoveLine(OSV):
_name = 'account.move.line'
analytic_lines = fields.One2Many('analytic_account.line', 'move_line',
'Analytic Lines')
MoveLine()
class OpenAccount(Wizard):
'Open Account'
_name = 'analytic_account.line.open_account'
states = {
'init': {
'result': {
'type': 'action',
'action': '_action_open_account',
'state': 'end',
},
},
}
def _action_open_account(self, cursor, user, data, context=None):
model_data_obj = self.pool.get('ir.model.data')
act_window_obj = self.pool.get('ir.action.act_window')
if context is None:
context = {}
model_data_ids = model_data_obj.search(cursor, user, [
('fs_id', '=', 'act_line_form'),
('module', '=', 'analytic_account'),
], limit=1, context=context)
model_data = model_data_obj.browse(cursor, user, model_data_ids[0],
context=context)
res = act_window_obj.read(cursor, user, model_data.db_id, context=context)
res['domain'] = []
if context.get('start_date'):
res['domain'].append(('date', '>=', context['start_date']))
if context.get('end_date'):
res['domain'].append(('date', '<=', context['end_date']))
return res
OpenAccount()
+103
View File
@@ -0,0 +1,103 @@
<?xml version="1.0"?>
<tryton>
<data>
<record model="ir.ui.view" id="line_view_form">
<field name="model">analytic_account.line</field>
<field name="type">form</field>
<field name="arch" type="xml">
<![CDATA[
<form string="Analytic Line">
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active" xexpand="0" width="100"/>
<notebook colspan="4">
<page string="General Information">
<label name="account"/>
<field name="account"/>
<label name="date"/>
<field name="date"/>
<label name="reference"/>
<field name="reference"/>
<label name="party"/>
<field name="party"/>
<label name="journal"/>
<field name="journal"/>
<label name="move_line"/>
<field name="move_line"/>
<label name="debit"/>
<field name="debit"/>
<label name="credit"/>
<field name="credit"/>
<label name="currency"/>
<field name="currency"/>
</page>
</notebook>
</form>
]]>
</field>
</record>
<record model="ir.ui.view" id="line_view_tree">
<field name="model">analytic_account.line</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<![CDATA[
<tree string="Analytic Lines" editable="top">
<field name="move_line" select="1"/>
<field name="journal" select="2"/>
<field name="name" select="1"/>
<field name="reference" select="2"/>
<field name="date" select="1"/>
<field name="party" select="2"/>
<field name="account" select="2"/>
<field name="debit" select="2"/>
<field name="credit" select="2"/>
<field name="currency" select="2"/>
</tree>
]]>
</field>
</record>
<record model="ir.action.act_window" id="act_line_form">
<field name="name">Analytic Lines</field>
<field name="res_model">analytic_account.line</field>
<field name="view_type">form</field>
</record>
<record model="ir.action.act_window.view" id="act_line_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="line_view_tree"/>
<field name="act_window" ref="act_line_form"/>
</record>
<record model="ir.action.act_window.view" id="act_line_form_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="line_view_form"/>
<field name="act_window" ref="act_line_form"/>
</record>
<record model="ir.action.wizard" id="act_open_account">
<field name="name">Open Account</field>
<field name="wiz_name">analytic_account.line.open_account</field>
</record>
<record model="ir.action.keyword" id="act_open_account_keyword">
<field name="keyword">tree_open</field>
<field name="model">analytic_account.account,0</field>
<field name="action" ref="act_open_account"/>
</record>
<record model="ir.ui.view" id="move_line_view_form">
<field name="model">account.move.line</field>
<field name="inherit" ref="account.move_line_view_form"/>
<field name="arch" type="xml">
<![CDATA[
<data>
<xpath expr="/form/notebook/page[@string=&quot;General Information&quot;]"
position="after">
<page string="Analytic" col="1">
<separator name="analytic_lines"/>
<field name="analytic_lines"/>
</page>
</xpath>
</data>
]]>
</field>
</record>
</data>
</tryton>
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python
from distutils.core import setup
from distutils.command import sdist
class mysdist(sdist.sdist):
def add_defaults(self):
sdist.sdist.add_defaults(self)
if self.distribution.has_pure_modules():
build_py = self.get_finalized_command('build_py')
data = []
for package in build_py.packages:
src_dir = build_py.get_package_dir(package)
data.extend(build_py.find_data_files(package, src_dir))
self.filelist.extend(data)
info = eval(file('__tryton__.py').read())
requires = ['trytond'] + ['trytond-' + x for x in info.get('depends', [])]
setup(name='trytond-' + info['name'].lower(),
version=info.get('version', '0'),
description=info.get('description', ''),
author=info.get('author', ''),
author_email=info.get('email', ''),
url=info.get('website', ''),
package_dir={'trytond.modules.' + info['name'].lower(): '.'},
packages=[
'trytond.modules.' + info['name'].lower(),
],
package_data={
'trytond.modules.' + info['name'].lower(): info.get('xml', []),
},
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
'Intended Audience :: Developers',
'Intended Audience :: Financial and Insurance Industry',
'Intended Audience :: Legal Industry',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Office/Business',
'Topic :: Office/Business :: Financial :: Accounting',
],
license=info.get('license', 'GPL-2'),
#requires=requires,
cmdclass={
'sdist': mysdist,
},
)