diff --git a/account.py b/account.py
index 14d6729dfb..a12f6b5a10 100644
--- a/account.py
+++ b/account.py
@@ -5,26 +5,86 @@ from trytond.osv.orm import ID_MAX, exclude
from trytond.wizard import Wizard, WizardOSV
from trytond.report import Report
from decimal import Decimal
+import datetime
+import time
+import locale
+import os
+from trytond.report.report import _LOCALE2WIN32
class Type(OSV):
'Account Type'
_name = 'account.account.type'
- _order = 'code'
+ _order = 'sequence, id'
_description = __doc__
name = fields.Char('Name', size=None, required=True, translate=True)
- code = fields.Char('Code', size=None, required=True)
+ code = fields.Char('Code', size=None)
+ parent = fields.Many2One('account.account.type', 'Parent')
+ childs = fields.One2Many('account.account.type', 'parent', 'Childs')
+ sequence = fields.Integer('Sequence', required=True,
+ help='Use to order the account type')
+ #TODO add constraint (only for end level)
+ account_type = fields.Boolean('Account Type',
+ help='Can be use as type for account')
partner_account = fields.Boolean('Partner account')
-
- def __init__(self):
- super(Type, self).__init__()
- self._sql_constraints += [
- ('code_uniq', 'UNIQUE(code)', 'Code must be unique!'),
- ]
+ #TODO fix digits depend of the currency
+ amount = fields.Function('get_amount', digits=(16, 2), string='Amount')
def default_partner_account(self, cursor, user, context=None):
return False
+ def default_sequence(self, cursor, user, context=None):
+ cursor.execute('SELECT MAX(sequence) ' \
+ 'FROM "' + self._table + '"')
+ res = cursor.fetchone()
+ if res:
+ return res[0]
+ return 0
+
+ def default_account_type(self, cursor, user, context=None):
+ return False
+
+ def get_amount(self, cursor, user, ids, name, arg, context=None):
+ company_obj = self.pool.get('company.company')
+ account_obj = self.pool.get('account.account')
+ currency_obj = self.pool.get('account.currency')
+
+ if context is None:
+ context = {}
+ res = {}
+ for type_id in ids:
+ res[type_id] = Decimal('0.0')
+
+ if not context.get('company'):
+ return res
+ company = company_obj.browse(cursor, user, context['company'],
+ context=context)
+
+ child_ids = self.search(cursor, user, [
+ ('parent', 'child_of', ids),
+ ], context=context)
+ type_sum = {}
+ for type_id in child_ids:
+ type_sum[type_id] = Decimal('0.0')
+
+ account_ids = account_obj.search(cursor, user, [
+ ('type', 'in', child_ids),
+ ('company', '=', company.id),
+ ], context=context)
+ for account in account_obj.browse(cursor, user, account_ids,
+ context=context):
+ type_sum[account.type.id] += currency_obj.round(cursor, user,
+ company.currency, account.debit - account.credit)
+ for type_id in ids:
+ child_ids = self.search(cursor, user, [
+ ('parent', 'child_of', [type_id]),
+ ], context=context)
+ for child_id in child_ids:
+ res[type_id] += type_sum[child_id]
+ res[type_id] = currency_obj.round(cursor, user,
+ company.currency, res[type_id])
+ return res
+
Type()
@@ -45,7 +105,8 @@ class Account(OSV):
second_currency = fields.Many2One('account.currency', 'Secondary currency',
help='Force all moves for this account \n' \
'to have this secondary currency.')
- type = fields.Selection('get_types', 'Type', required=True)
+ type = fields.Many2One('account.account.type', 'Type', required=True,
+ domain=[('account_type', '=', True)])
parents = fields.Many2Many('account.account', 'account_account_rel',
'child', 'parent', 'Parents')
childs = fields.Many2Many('account.account', 'account_account_rel',
@@ -87,7 +148,11 @@ class Account(OSV):
return context.get('company', False)
def default_type(self, cursor, user, context=None):
- return 'view'
+ type_obj = self.pool.get('account.account.type')
+ types = type_obj.search(cursor, user, [
+ ('code', '=', 'view'),
+ ], limit=1, context=context)
+ return type_obj.name_get(cursor, user, types, context=context)[0]
def default_reconcile(self, cursor, user, context=None):
return False
@@ -131,12 +196,6 @@ class Account(OSV):
res[i] = False
return res
- def get_types(self, cursor, user, context=None):
- type_obj = self.pool.get('account.account.type')
- type_ids = type_obj.search(cursor, user, [], context=context)
- types = type_obj.browse(cursor, user, type_ids, context=context)
- return [(x.code, x.name) for x in types]
-
def get_balance(self, cursor, user, ids, name, arg, context=None):
res = {}
company_obj = self.pool.get('company.company')
@@ -152,7 +211,9 @@ class Account(OSV):
'FROM account_account a ' \
'LEFT JOIN account_move_line l ' \
'ON (a.id = l.account) ' \
- 'WHERE a.type != \'view\' ' \
+ 'JOIN account_account_type t ' \
+ 'ON (a.type = t.id) ' \
+ 'WHERE t.code != \'view\' ' \
'AND a.id IN (' + \
','.join(['%s' for x in all_ids]) + ') ' \
'AND ' + line_query + ' ' \
@@ -201,7 +262,9 @@ class Account(OSV):
'FROM account_account a ' \
'LEFT JOIN account_move_line l ' \
'ON (a.id = l.account) ' \
- 'WHERE a.type != \'view\' ' \
+ 'JOIN account_account_type t ' \
+ 'ON (a.type = t.id) ' \
+ 'WHERE t.code != \'view\' ' \
'AND a.id IN (' + \
','.join(['%s' for x in ids]) + ') ' \
'AND ' + line_query + ' ' \
@@ -342,7 +405,7 @@ class Partner(OSV):
account_payable = fields.Property('account.account', type='many2one',
relation='account.account', string='Account Payable',
group_name='Accounting Properties', view_load=True,
- domain="[('type', '=', 'payable'), ('company', '=', company)]",
+ domain="[('type.code', '=', 'payable'), ('company', '=', company)]",
states={
'required': "company",
'invisible': "not company",
@@ -350,7 +413,7 @@ class Partner(OSV):
account_receivable = fields.Property('account.account', type='many2one',
relation='account.account', string='Account Receivable',
group_name='Accounting Properties', view_load=True,
- domain="[('type', '=', 'receivable'), ('company', '=', company)]",
+ domain="[('type.code', '=', 'receivable'), ('company', '=', company)]",
states={
'required': "company",
'invisible': "not company",
@@ -636,7 +699,7 @@ class TrialBalance(Report):
datas['form']['company'], context=context)
account_ids = account_obj.search(cursor, user, [
- ('type', '!=', 'view'),
+ ('type.code', '!=', 'view'),
], context=context)
start_period_ids = [0]
@@ -668,6 +731,7 @@ class TrialBalance(Report):
ctx = context.copy()
ctx['fiscalyear'] = datas['form']['fiscalyear']
ctx['periods'] = end_period_ids
+ ctx['posted'] = datas['form']['posted']
accounts = account_obj.browse(cursor, user, account_ids,
context=ctx)
@@ -692,3 +756,96 @@ class TrialBalance(Report):
return amount
TrialBalance()
+
+
+class OpenBalanceSheet(WizardOSV):
+ _name = 'account.account.open_balance_sheet.init'
+ date = fields.Date('Date', required=True)
+ company = fields.Many2One('company.company', 'Company', required=True)
+ posted = fields.Boolean('Posted Move', help='Only posted move')
+
+ def default_date(self, cursor, user, context=None):
+ return datetime.date.today()
+
+ def default_company(self, cursor, user, context=None):
+ if context is None:
+ context = {}
+ company_obj = self.pool.get('company.company')
+ if context.get('company'):
+ return company_obj.name_get(cursor, user, context['company'],
+ context=context)[0]
+ return False
+
+ def default_posted(self, cursor, user, context=None):
+ return False
+
+OpenBalanceSheet()
+
+
+class OpenBalanceSheet(Wizard):
+ 'Open Balance Sheet'
+ _name = 'account.account.open_balance_sheet'
+ states = {
+ 'init': {
+ 'result': {
+ 'type': 'form',
+ 'object': 'account.account.open_balance_sheet.init',
+ 'state': [
+ ('end', 'Cancel', 'gtk-cancel'),
+ ('open', 'Open', 'gtk-ok', True),
+ ],
+ },
+ },
+ 'open': {
+ 'result': {
+ 'type': 'action',
+ 'action': '_action_open',
+ 'state': 'end',
+ },
+ },
+ }
+
+ def _get_root_codes(self, cursor, user, data, context=None):
+ return ('asset', 'liability', 'equity')
+
+ def _action_open(self, cursor, user, data, context=None):
+ if context is None:
+ context = {}
+ model_data_obj = self.pool.get('ir.model.data')
+ act_window_obj = self.pool.get('ir.action.act_window')
+ company_obj = self.pool.get('company.company')
+
+ company = company_obj.browse(cursor, user, data['form']['company'],
+ context=context)
+ lang = context.get('language', False) or 'en_US'
+ try:
+ if os.name == 'nt':
+ locale.setlocale(locale.LC_ALL,
+ _LOCALE2WIN32.get(lang, lang) + '.' + encoding)
+ else:
+ locale.setlocale(locale.LC_ALL, lang + '.' + encoding)
+ except:
+ pass
+ date = time.strptime(str(data['form']['date']), '%Y-%m-%d')
+ date = time.strftime(locale.nl_langinfo(locale.D_FMT).\
+ replace('%y', '%Y'), date)
+
+ model_data_ids = model_data_obj.search(cursor, user, [
+ ('fs_id', '=', 'act_account_balance_sheet_tree'),
+ ('module', '=', '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({
+ 'date': data['form']['date'],
+ 'posted': data['form']['posted'],
+ 'company': data['form']['company'],
+ })
+ res['domain'] = res['domain'][:-1] + ', ' + \
+ str(('code', 'in', self._get_root_codes(cursor, user, data,
+ context=context))) + res['domain'][-1]
+ res['name'] = res['name'] + ' - '+ date + ' - ' + company.name
+ return res
+
+OpenBalanceSheet()
diff --git a/account.xml b/account.xml
index ea3cc28a6e..9968fd3c4e 100644
--- a/account.xml
+++ b/account.xml
@@ -36,6 +36,12 @@
+
+
+
+
+
+
@@ -45,11 +51,13 @@
account.account.type
tree
+ childs
+
]]>
@@ -72,6 +80,19 @@
+
+ Account Types
+ account.account.type
+ tree
+ [('parent', '=', False)]
+
+
+
+
+
+
+
@@ -87,47 +108,95 @@
-
- Receivable
- receivable
-
-
-
- Payable
- payable
-
-
-
- View
- view
-
-
- Income
- income
-
-
- Expense
- expense
-
-
- Tax
- tax
-
-
- Cash
- cash
-
Asset
asset
+
+
+
+ Current
+
+
+
+
+ Cash
+ cash
+
+
+
+
+
+ Receivable
+ receivable
+
+
+
+
+
+
+ Long-term
+
+
+
+
+ Liability
+ liability
+
+
+
+ Current
+
+
+
+
+ Payable
+ payable
+
+
+
+
+
+
+ Tax
+ tax
+
+
+
+
+
+ Long-term
+
+
Equity
equity
+
+
+
+
+ Revenue
+ revenue
+
+
+
+
+ Expense
+ expense
+
+
+
+
+ View
+ view
+
+
Closed
closed
+
+
@@ -147,7 +216,7 @@
-
+
@@ -268,12 +337,12 @@
Root
none
- view
+
Main Receivable
- receivable
+
unreconciled
@@ -281,27 +350,27 @@
Main Payable
- payable
+
unreconciled
-
- Main Income
- income
+
+ Main Revenue
+
Main Expense
- expense
+
Main Cash
- cash
+
@@ -406,5 +475,56 @@
account.account.trial_balance
account/trial_balance.odt
+
+
+ account.account.open_balance_sheet.init
+ form
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+ account.account.type
+ tree
+ childs
+
+
+
+
+
+ ]]>
+
+
+
+ Balance Sheet
+ account.account.type
+ tree
+ [('parent', '=', False)]
+
+
+
+
+
+
+
+
+ Open Balance Sheet
+ account.account.open_balance_sheet
+
+
+
diff --git a/move.py b/move.py
index a0de0a49e3..a55e6512fe 100644
--- a/move.py
+++ b/move.py
@@ -332,7 +332,7 @@ class Line(OSV):
on_change=['account', 'debit', 'credit', 'tax_lines',
'journal', 'move'])
account = fields.Many2One('account.account', 'Account', required=True,
- domain=[('type', '!=', 'view'), ('type', '!=', 'closed')],
+ domain=[('type.code', '!=', 'view'), ('type.code', '!=', 'closed')],
select=1,
on_change=['account', 'debit', 'credit', 'tax_lines',
'journal', 'move'])
@@ -786,12 +786,44 @@ class Line(OSV):
def query_get(self, cursor, user, obj='l', context=None):
'''
- Return SQL clause for account move line.
+ Return SQL clause for account move line depending of the context.
obj is the SQL alias of the account_move_line in the query.
'''
fiscalyear_obj = self.pool.get('account.fiscalyear')
if context is None:
context = {}
+
+ if context.get('date'):
+ fiscalyear_ids = fiscalyear_obj.search(cursor, user, [
+ ('start_date', '<=', context['date']),
+ ('end_date', '>=', context['date']),
+ ], limit=1, context=context)
+ if context.get('posted'):
+ return obj + '.active ' \
+ 'AND ' + obj + '.state != \'draft\' ' \
+ 'AND ' + obj + '.move IN (' \
+ 'SELECT m.id FROM account_move AS m, ' \
+ 'account_period AS p ' \
+ 'WHERE m.period = p.id ' \
+ 'AND p.fiscalyear = ' + \
+ str(fiscalyear_ids[0]) + ' ' \
+ 'AND m.date <= \'' + \
+ str(context['date']) + '\' ' \
+ 'AND m.state = \'posted\' ' \
+ ')'
+ else:
+ return obj + '.active ' \
+ 'AND ' + obj + '.state != \'draft\' ' \
+ 'AND ' + obj + '.move IN (' \
+ 'SELECT m.id FROM account_move AS m, ' \
+ 'account_period AS p ' \
+ 'WHERE m.period = p.id ' \
+ 'AND p.fiscalyear = ' + \
+ str(fiscalyear_ids[0]) + ' ' \
+ 'AND m.date <= \'' + \
+ str(context['date']) + '\'' \
+ ')'
+
if not context.get('fiscalyear', False):
fiscalyear_ids = fiscalyear_obj.search(cursor, user, [
('state', '=', 'open'),
@@ -799,6 +831,7 @@ class Line(OSV):
fiscalyear_clause = (','.join([str(x) for x in fiscalyear_ids])) or '0'
else:
fiscalyear_clause = '%s' % int(context.get('fiscalyear'))
+
if context.get('periods', False):
ids = ','.join([str(int(x)) for x in context['periods']])
if context.get('posted'):
@@ -848,7 +881,7 @@ class Line(OSV):
def check_account(self, cursor, user, ids):
for line in self.browse(cursor, user, ids):
- if line.account.type in ('view', 'closed'):
+ if line.account.type.code in ('view', 'closed'):
return False
if not line.account.active:
return False
@@ -1255,7 +1288,7 @@ class ReconcileLinesWriteOff(WizardOSV):
journal = fields.Many2One('account.journal', 'Journal', required=True)
period = fields.Many2One('account.period', 'Period', required=True)
account = fields.Many2One('account.account', 'Account', required=True,
- domain=[('type', '!=', 'view'), ('type', '!=', 'closed')])
+ domain=[('type.code', '!=', 'view'), ('type.code', '!=', 'closed')])
def default_period(self, cursor, user, context=None):
period_obj = self.pool.get('account.period')
@@ -1374,7 +1407,7 @@ UnreconcileLines()
class OpenReconcileLinesInit(WizardOSV):
_name = 'account.move.open_reconcile_lines.init'
account = fields.Many2One('account.account', 'Account', required=True,
- domain=[('type', '!=', 'view'), ('reconcile', '=', True)])
+ domain=[('type.code', '!=', 'view'), ('reconcile', '=', True)])
OpenReconcileLinesInit()
@@ -1473,10 +1506,12 @@ class Partner(OSV):
cursor.execute('SELECT l.partner, ' \
'SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))) ' \
'FROM account_move_line AS l, ' \
- 'account_account AS a ' \
+ 'account_account AS a, ' \
+ 'account_account_type AS t ' \
'WHERE a.id = l.account ' \
'AND a.active ' \
- 'AND a.type = %s ' \
+ 'AND a.type = t.id ' \
+ 'AND t.code = %s ' \
'AND l.partner IN ' \
'(' + ','.join(['%s' for x in ids]) + ') ' \
'AND l.reconciliation IS NULL ' \
@@ -1520,10 +1555,12 @@ class Partner(OSV):
cursor.execute('SELECT l.partner ' \
'FROM account_move_line AS l, ' \
- 'account_account AS a ' \
+ 'account_account AS a, ' \
+ 'account_account_type AS t ' \
'WHERE a.id = l.account ' \
'AND a.active ' \
- 'AND a.type = %s ' \
+ 'AND a.type = t.id ' \
+ 'AND t.code = %s ' \
'AND l.partner IS NOT NULL ' \
'AND l.reconciliation IS NOT NULL ' \
'AND ' + line_query + ' ' \