Files
tryton/account.py
T

1658 lines
63 KiB
Python

#This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms.
"Account"
from trytond.osv import fields, OSV
from trytond.osv.orm import 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
from _strptime import LocaleTime
from trytond.server import LOCALE_SEMAPHORE
class Type(OSV):
'Account Type'
_name = 'account.account.type'
_description = __doc__
name = fields.Char('Name', size=None, required=True, translate=True)
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')
currency_digits = fields.Function('get_currency_digits', type='integer',
string='Currency Digits')
amount = fields.Function('get_amount', digits="(16, currency_digits)",
string='Amount')
balance_sheet = fields.Boolean('Balance Sheet', states={
'invisible': "parent",
})
income_statement = fields.Boolean('Income Statement')
display_balance = fields.Selection([
('debit-credit', 'Debit - Credit'),
('credit-debit', 'Credit - Debit'),
], 'Display Balance', required=True)
def __init__(self):
super(Type, self).__init__()
self._order.insert(0, ('sequence', 'ASC'))
def default_balance_sheet(self, cursor, user, context=None):
return False
def default_income_statement(self, cursor, user, context=None):
return False
def default_display_balance(self, cursor, user, context=None):
return 'debit-credit'
def get_currency_digits(self, cursor, user, ids, name, arg, context=None):
company_obj = self.pool.get('company.company')
if context is None:
context = {}
res = {}
if not context.get('company'):
for type_id in ids:
res[type_id] = 2
return res
company = company_obj.browse(cursor, user, context['company'],
context=context)
for type_id in ids:
res[type_id] = company.currency.digits
return res
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('currency.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)
types = self.browse(cursor, user, ids, context=context)
for type in types:
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])
if type.display_balance == 'credit-debit':
res[type.id] = - res[type.id]
return res
def name_get(self, cursor, user, ids, context=None):
if not ids:
return []
if isinstance(ids, (int, long)):
ids = [ids]
res = []
def _name(type):
if type.parent:
return _name(type.parent) + '\\' + type.name
else:
return type.name
for type in self.browse(cursor, user, ids, context=context):
res.append((type.id, _name(type)))
return res
Type()
class AccountTemplate(OSV):
'Account Template'
_name = 'account.account.template'
_description = __doc__
name = fields.Char('Name', size=None, required=True, translate=True,
select=1)
complete_name = fields.Function('get_complete_name', type='char',
string='Name', order_field='code')
code = fields.Char('Code', size=None, select=1)
type = fields.Many2One('account.account.type', 'Type',
states={
'invisible': "kind == 'view'",
'required': "kind != 'view'",
})
parent = fields.Many2One('account.account.template', 'Parent', select=1)
childs = fields.One2Many('account.account.template', 'parent', 'Childs')
reconcile = fields.Boolean('Reconcile',
states={
'invisible': "kind == 'view'",
})
close_method = fields.Selection([
('none', 'None'),
('balance', 'Balance'),
('detail', 'Detail'),
('unreconciled', 'Unreconciled'),
], 'Deferral method',
states={
'invisible': "kind == 'view'",
'required': "kind != 'view'",
})
kind = fields.Selection([
('other', 'Other'),
('payable', 'Payable'),
('revenue', 'Revenue'),
('receivable', 'Receivable'),
('expense', 'Expense'),
('view', 'View'),
], 'Kind', required=True)
def __init__(self):
super(AccountTemplate, self).__init__()
self._constraints += [
('check_recursion', 'recursive_accounts'),
]
self._error_messages.update({
'recursive_accounts': 'You can not create recursive accounts!',
})
self._order.insert(0, ('code', 'ASC'))
self._order.insert(1, ('name', 'ASC'))
def default_kind(self, cursor, user, context=None):
return 'view'
def default_reconcile(self, cursor, user, context=None):
return False
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 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'] + ' - ' + unicode(r[self._rec_name]) \
or unicode(r[self._rec_name])) for r in self.read(cursor, user, ids,
[self._rec_name, 'code'], context=context, load='_classic_write')]
def _get_account_value(self, cursor, user, template, context=None):
'''
Set the values for account creation.
:param cursor: the database cursor
:param user: the user id
:param template: the BrowseRecord of the template
:param context: the context
:return: a dictionary with account fields as key and values as value
'''
res = {}
res['name'] = template.name
res['code'] = template.code
res['kind'] = template.kind
res['type'] = template.type.id
res['reconcile'] = template.reconcile
res['close_method'] = template.close_method
res['taxes'] = [('add', x.id) for x in template.taxes]
return res
def create_account(self, cursor, user, template, company_id, context=None,
template2account=None, parent_id=False):
'''
Create recursively accounts based on template.
:param cursor: the database cursor
:param user: the user id
:param template: the template id or the BrowseRecord of template
used for account creation
:param company_id: the id of the company for which accounts
are created
:param context: the context
:param template2account: a dictionary with template id as key
and account id as value, used to convert template id
into account. The dictionary is filled with new accounts
:param parent_id: the account id of the parent of the accounts
that must be created
:return: id of the account created
'''
account_obj = self.pool.get('account.account')
if template2account is None:
template2account = {}
if isinstance(template, (int, long)):
template = self.browse(cursor, user, template, context=context)
if template.id not in template2account:
vals = self._get_account_value(cursor, user, template, context=context)
vals['company'] = company_id
vals['parent'] = parent_id
new_id = account_obj.create(cursor, user, vals, context=context)
template2account[template.id] = new_id
else:
new_id = template2account[template.id]
new_childs = []
for child in template.childs:
new_childs.append(self.create_account(cursor, user, child,
company_id, context=context, template2account=template2account,
parent_id=new_id))
return new_id
AccountTemplate()
class Account(OSV):
'Account'
_name = 'account.account'
_description = __doc__
name = fields.Char('Name', size=None, required=True, translate=True,
select=1)
complete_name = fields.Function('get_complete_name', type='char',
string='Name', order_field='code')
code = fields.Char('Code', size=None, select=1)
active = fields.Boolean('Active', select=2)
company = fields.Many2One('company.company', 'Company', required=True)
currency = fields.Function('get_currency', type='many2one',
relation='currency.currency', string='Currency')
currency_digits = fields.Function('get_currency_digits', type='integer',
string='Currency Digits')
second_currency = fields.Many2One('currency.currency', 'Secondary currency',
help='Force all moves for this account \n' \
'to have this secondary currency.')
type = fields.Many2One('account.account.type', 'Type',
states={
'invisible': "kind == 'view'",
'required': "kind != 'view'",
})
parent = fields.Many2One('account.account', 'Parent', select=1,
left="left", right="right")
left = fields.Integer('Left', required=True)
right = fields.Integer('Right', required=True)
childs = fields.One2Many('account.account', 'parent', 'Childs')
balance = fields.Function('get_balance', digits="(16, currency_digits)",
string='Balance')
credit = fields.Function('get_credit_debit', digits="(16, currency_digits)",
string='Credit')
debit = fields.Function('get_credit_debit', digits="(16, currency_digits)",
string='Debit')
reconcile = fields.Boolean('Reconcile',
help='Check if the account can be used \n' \
'for reconciliation.',
states={
'invisible': "kind == 'view'",
})
close_method = fields.Selection([
('none', 'None'),
('balance', 'Balance'),
('detail', 'Detail'),
('unreconciled', 'Unreconciled'),
], 'Deferral method',
help='How to process the lines of this account ' \
'when closing the fiscal year\n' \
'- None: to start with an empty account\n'
'- Balance: to create one entry to keep the balance\n'
'- Detail: to keep all entries\n'
'- Unreconciled: to keep all unreconciled entries',
states={
'invisible': "kind == 'view'",
'required': "kind != 'view'",
})
note = fields.Text('Note')
kind = fields.Selection([
('other', 'Other'),
('payable', 'Payable'),
('revenue', 'Revenue'),
('receivable', 'Receivable'),
('expense', 'Expense'),
('view', 'View'),
], 'Kind', required=True)
def __init__(self):
super(Account, self).__init__()
self._constraints += [
('check_recursion', 'recursive_accounts'),
]
self._error_messages.update({
'recursive_accounts': 'You can not create recursive accounts!',
})
self._order.insert(0, ('code', 'ASC'))
self._order.insert(1, ('name', 'ASC'))
def default_left(self, cursor, user, context=None):
return 0
def default_right(self, cursor, user, context=None):
return 0
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_reconcile(self, cursor, user, context=None):
return False
def default_close_method(self, cursor, user, context=None):
return 'balance'
def default_kind(self, cursor, user, context=None):
return 'view'
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_currency(self, cursor, user, ids, name, arg, context=None):
currency_obj = self.pool.get('currency.currency')
res = {}
for account in self.browse(cursor, user, ids, context=context):
res[account.id] = account.company.currency.id
currency_names = {}
for currency_id, currency_name in currency_obj.name_get(cursor, user,
[x for x in res.values() if x], context=context):
currency_names[currency_id] = currency_name
for i in res.keys():
if res[i] and res[i] in currency_names:
res[i] = (res[i], currency_names[res[i]])
else:
res[i] = False
return res
def get_currency_digits(self, cursor, user, ids, name, arg, context=None):
res = {}
for account in self.browse(cursor, user, ids, context=context):
res[account.id] = account.company.currency.digits
return res
def get_balance(self, cursor, user, ids, name, arg, context=None):
res = {}
company_obj = self.pool.get('company.company')
currency_obj = self.pool.get('currency.currency')
move_line_obj = self.pool.get('account.move.line')
query_ids, args_ids = self.search(cursor, user, [
('parent', 'child_of', ids),
], context=context, query_string=True)
line_query = move_line_obj.query_get(cursor, user, context=context)
cursor.execute('SELECT a.id, ' \
'SUM((COALESCE(l.debit, 0) - COALESCE(l.credit, 0))) ' \
'FROM account_account a ' \
'LEFT JOIN account_move_line l ' \
'ON (a.id = l.account) ' \
'WHERE a.kind != \'view\' ' \
'AND a.id IN (' + query_ids + ') ' \
'AND ' + line_query + ' ' \
'AND a.active ' \
'GROUP BY a.id', args_ids)
account_sum = {}
for account_id, sum in cursor.fetchall():
account_sum[account_id] = sum
account2company = {}
id2company = {}
id2account = {}
all_ids = self.search(cursor, user, [('parent', 'child_of', ids)],
context=context)
accounts = self.browse(cursor, user, all_ids, context=context)
for account in accounts:
account2company[account.id] = account.company.id
id2company[account.company.id] = account.company
id2account[account.id] = account
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)
company_id = account2company[account_id]
to_currency = id2company[company_id].currency
for child_id in child_ids:
child_company_id = account2company[child_id]
from_currency = id2company[child_company_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])
return res
def get_credit_debit(self, cursor, user, ids, names, arg, context=None):
'''
Function to compute debit, credit for account ids.
:param cursor: the database cursor
:param user: the user id
:param ids: the ids of the account
:param names: the list of field name to compute
:param arg: optional argument
:param context: the context
:return: a dictionary with all field names as key and
a dictionary as value with id as key
'''
res = {}
move_line_obj = self.pool.get('account.move.line')
company_obj = self.pool.get('company.company')
currency_obj = self.pool.get('currency.currency')
for name in names:
if name not in ('credit', 'debit'):
raise Exception('Bad argument')
res[name] = {}
line_query = move_line_obj.query_get(cursor, user, context=context)
cursor.execute('SELECT a.id, ' + \
','.join(['SUM(COALESCE(l.' + name + ', 0))'
for name in names]) + ' ' \
'FROM account_account a ' \
'LEFT JOIN account_move_line l ' \
'ON (a.id = l.account) ' \
'WHERE a.kind != \'view\' ' \
'AND a.id IN (' + \
','.join(['%s' for x in ids]) + ') ' \
'AND ' + line_query + ' ' \
'AND a.active ' \
'GROUP BY a.id', ids)
for account_id, sums in cursor.fetchall():
for i in range(len(names)):
res[names[i]][account_id] = sum[i]
account2company = {}
id2company = {}
accounts = self.browse(cursor, user, ids, context=context)
for account in accounts:
account2company[account.id] = account.company.id
id2company[account.company.id] = account.company
for account_id in ids:
for name in names:
res[name].setdefault(account_id, Decimal('0.0'))
company_id = account2company[account_id]
currency = id2company[company_id].currency
for name in names:
res[name][account_id] = currency_obj.round(cursor, user,
currency, res[name][account_id])
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'] + ' - ' + unicode(r[self._rec_name]) \
or unicode(r[self._rec_name])) for r in self.read(cursor, user, ids,
[self._rec_name, 'code'], context=context, load='_classic_write')]
def copy(self, cursor, user, object_id, default=None, context=None):
account = self.browse(cursor, user, object_id, context=context)
default['parent'] = False
if account:
#Also duplicate all childs
new_child_ids = []
for child in account.childs:
new_child_ids.append(
self.copy(cursor, user, child.id, default,
context=context))
default['childs'] = [('set', new_child_ids)]
else:
default['childs'] = False
return super(Account, self).copy(cursor, user, object_id, default,
context=context)
def write(self, cursor, user, ids, vals, context=None):
if not vals.get('active', True):
move_line_obj = self.pool.get('account.move.line')
account_ids = self.search(cursor, user, [
('parent', 'child_of', ids),
], context=context)
if move_line_obj.search(cursor, user, [
('account', 'in', account_ids),
], context=context):
vals = vals.copy()
del vals['active']
return super(Account, self).write(cursor, user, ids, vals,
context=context)
Account()
class OpenChartAccountInit(WizardOSV):
_name = 'account.account.open_chart_account.init'
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
help='Keep empty for all open fiscal year')
posted = fields.Boolean('Posted Move', help='Only posted move')
def default_posted(self, cursor, user, context=None):
return False
OpenChartAccountInit()
class OpenChartAccount(Wizard):
'Open Chart Of Account'
_name = 'account.account.open_chart_account'
states = {
'init': {
'result': {
'type': 'form',
'object': 'account.account.open_chart_account.init',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('open', 'Open', 'tryton-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', '=', '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({
'fiscalyear': data['form']['fiscalyear'],
'posted': data['form']['posted'],
})
return res
OpenChartAccount()
class Party(OSV):
_name = 'relationship.party'
account_payable = fields.Property(type='many2one',
relation='account.account', string='Account Payable',
group_name='Accounting Properties', view_load=True,
domain="[('kind', '=', 'payable'), ('company', '=', company)]",
states={
'required': "company",
'invisible': "not company",
})
account_receivable = fields.Property(type='many2one',
relation='account.account', string='Account Receivable',
group_name='Accounting Properties', view_load=True,
domain="[('kind', '=', 'receivable'), ('company', '=', company)]",
states={
'required': "company",
'invisible': "not company",
})
Party()
class PrintGeneralLegderInit(WizardOSV):
_name = 'account.account.print_general_ledger.init'
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
required=True, on_change=['fiscalyear'])
start_period = fields.Many2One('account.period', 'Start Period',
domain="[('fiscalyear', '=', fiscalyear), " \
"('start_date', '<=', (end_period, 'start_date'))]")
end_period = fields.Many2One('account.period', 'End Period',
domain="[('fiscalyear', '=', fiscalyear), " \
"('start_date', '>=', (start_period, 'start_date'))]")
company = fields.Many2One('company.company', 'Company', required=True)
posted = fields.Boolean('Posted Move', help='Only posted move')
def default_fiscalyear(self, cursor, user, context=None):
fiscalyear_obj = self.pool.get('account.fiscalyear')
if context is None:
context = {}
fiscalyear_id = fiscalyear_obj.find(cursor, user,
context.get('company', False), exception=False, context=context)
if fiscalyear_id:
return fiscalyear_obj.name_get(cursor, user, fiscalyear_id,
context=context)[0]
return False
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
def on_change_fiscalyear(self, cursor, user, ids, vals, context=None):
return {
'start_period': False,
'end_period': False,
}
PrintGeneralLegderInit()
class PrintGeneralLegder(Wizard):
'Print General Legder'
_name = 'account.account.print_general_ledger'
states = {
'init': {
'result': {
'type': 'form',
'object': 'account.account.print_general_ledger.init',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('print', 'Print', 'tryton-print', True),
],
},
},
'print': {
'result': {
'type': 'print',
'report': 'account.account.general_ledger',
'state': 'end',
},
},
}
PrintGeneralLegder()
class GeneralLegder(Report):
_name = 'account.account.general_ledger'
def parse(self, cursor, user, report, objects, datas, context):
if context is None:
context = {}
context = context.copy()
account_obj = self.pool.get('account.account')
period_obj = self.pool.get('account.period')
company_obj = self.pool.get('company.company')
company = company_obj.browse(cursor, user,
datas['form']['company'], context=context)
account_ids = account_obj.search(cursor, user, [
('company', '=', datas['form']['company']),
], order=[('code', 'ASC'), ('id', 'ASC')], context=context)
start_period_ids = [0]
if datas['form']['start_period']:
start_period = period_obj.browse(cursor, user,
datas['form']['start_period'], context=context)
start_period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', datas['form']['fiscalyear']),
('end_date', '<=', start_period.start_date),
], context=context)
start_context = context.copy()
start_context['fiscalyear'] = datas['form']['fiscalyear']
start_context['periods'] = start_period_ids
start_context['posted'] = datas['form']['posted']
start_accounts = account_obj.browse(cursor, user, account_ids,
context=start_context)
id2start_account = {}
for account in start_accounts:
id2start_account[account.id] = account
end_period_ids = []
if datas['form']['end_period']:
end_period = period_obj.browse(cursor, user,
datas['form']['end_period'], context=context)
end_period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', datas['form']['fiscalyear']),
('end_date', '<=', end_period.start_date),
], context=context)
end_period_ids = exclude(end_period_ids, start_period_ids)
if datas['form']['end_period'] not in end_period_ids:
end_period_ids.append(datas['form']['end_period'])
else:
end_period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', datas['form']['fiscalyear']),
], context=context)
end_period_ids = exclude(end_period_ids, start_period_ids)
end_context = context.copy()
end_context['fiscalyear'] = datas['form']['fiscalyear']
end_context['periods'] = end_period_ids
end_context['posted'] = datas['form']['posted']
end_accounts = account_obj.browse(cursor, user, account_ids,
context=end_context)
id2end_account = {}
for account in end_accounts:
id2end_account[account.id] = account
periods = period_obj.browse(cursor, user, end_period_ids,
context=context)
periods.sort(lambda x, y: cmp(x.start_date, y.start_date))
context['start_period'] = periods[0]
periods.sort(lambda x, y: cmp(x.end_date, y.end_date))
context['end_period'] = periods[-1]
context['accounts'] = account_obj.browse(cursor, user, account_ids,
context=context)
context['id2start_account'] = id2start_account
context['id2end_account'] = id2end_account
context['digits'] = company.currency.digits
context['lines'] = lambda account_id: self.lines(cursor, user,
account_id, end_period_ids, datas['form']['posted'], context)
context['company'] = company
return super(GeneralLegder, self).parse(cursor, user, report, objects,
datas, context)
def lines(self, cursor, user, account_id, period_ids, posted, context):
move_line_obj = self.pool.get('account.move.line')
period_obj = self.pool.get('account.period')
res = []
clause = [
('account', '=', account_id),
('period', 'in', period_ids),
('state', '!=', 'draft'),
]
if posted:
clause.append(('move.state', '=', 'posted'))
move_line_ids = move_line_obj.search(cursor, user, clause,
context=context)
move_lines = move_line_obj.browse(cursor, user, move_line_ids,
context=context)
move_lines.sort(lambda x, y: cmp(x.date, y.date))
balance = Decimal('0.0')
for line in move_lines:
balance += line.debit - line.credit
res.append({
'date': line.date,
'debit': line.debit,
'credit': line.credit,
'balance': balance,
'name': line.name,
'state': line.move.state,
})
return res
GeneralLegder()
class PrintTrialBalanceInit(WizardOSV):
_name = 'account.account.print_trial_balance.init'
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
required=True, on_change=['fiscalyear'])
start_period = fields.Many2One('account.period', 'Start Period',
domain="[('fiscalyear', '=', fiscalyear), " \
"('start_date', '<=', (end_period, 'start_date'))]")
end_period = fields.Many2One('account.period', 'End Period',
domain="[('fiscalyear', '=', fiscalyear), " \
"('start_date', '>=', (start_period, 'start_date'))]")
company = fields.Many2One('company.company', 'Company', required=True)
posted = fields.Boolean('Posted Move', help='Only posted move')
def default_fiscalyear(self, cursor, user, context=None):
fiscalyear_obj = self.pool.get('account.fiscalyear')
if context is None:
context = {}
fiscalyear_id = fiscalyear_obj.find(cursor, user,
context.get('company', False), exception=False, context=context)
if fiscalyear_id:
return fiscalyear_obj.name_get(cursor, user, fiscalyear_id,
context=context)[0]
return False
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
def on_change_fiscalyear(self, cursor, user, ids, vals, context=None):
return {
'start_period': False,
'end_period': False,
}
PrintTrialBalanceInit()
class PrintTrialBalance(Wizard):
'Print Trial Balance'
_name = 'account.account.print_trial_balance'
states = {
'init': {
'result': {
'type': 'form',
'object': 'account.account.print_trial_balance.init',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('print', 'Print', 'tryton-print', True),
],
},
},
'print': {
'result': {
'type': 'print',
'report': 'account.account.trial_balance',
'state': 'end',
},
},
}
PrintTrialBalance()
class TrialBalance(Report):
_name = 'account.account.trial_balance'
def parse(self, cursor, user, report, objects, datas, context):
if context is None:
context = {}
context = context.copy()
account_obj = self.pool.get('account.account')
period_obj = self.pool.get('account.period')
company_obj = self.pool.get('company.company')
company = company_obj.browse(cursor, user,
datas['form']['company'], context=context)
account_ids = account_obj.search(cursor, user, [
('kind', '!=', 'view'),
], context=context)
start_period_ids = [0]
if datas['form']['start_period']:
start_period = period_obj.browse(cursor, user,
datas['form']['start_period'], context=context)
start_period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', datas['form']['fiscalyear']),
('end_date', '<=', start_period.start_date),
], context=context)
end_period_ids = []
if datas['form']['end_period']:
end_period = period_obj.browse(cursor, user,
datas['form']['end_period'], context=context)
end_period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', datas['form']['fiscalyear']),
('end_date', '<=', end_period.start_date),
], context=context)
end_period_ids = exclude(end_period_ids, start_period_ids)
if datas['form']['end_period'] not in end_period_ids:
end_period_ids.append(datas['form']['end_period'])
else:
end_period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', datas['form']['fiscalyear']),
], context=context)
end_period_ids = exclude(end_period_ids, start_period_ids)
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)
periods = period_obj.browse(cursor, user, end_period_ids,
context=context)
context['accounts'] = accounts
context['start_period'] = periods[0]
context['end_period'] = periods[-1]
context['company'] = company
context['digits'] = company.currency.digits
context['sum'] = lambda accounts, field: self.sum(cursor, user,
accounts, field, context)
return super(TrialBalance, self).parse(cursor, user, report, objects,
datas, context)
def sum(self, cursor, user, accounts, field, context):
amount = Decimal('0.0')
for account in accounts:
amount += account[field]
return amount
TrialBalance()
class OpenBalanceSheetInit(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
OpenBalanceSheetInit()
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', 'tryton-cancel'),
('open', 'Open', 'tryton-ok', True),
],
},
},
'open': {
'result': {
'type': 'action',
'action': '_action_open',
'state': 'end',
},
},
}
def _action_open(self, cursor, user, datas, 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, datas['form']['company'],
context=context)
lang = context.get('language', False) or 'en_US'
LOCALE_SEMAPHORE.acquire()
try:
encoding = locale.getdefaultlocale()[1]
if encoding == 'utf':
encoding = 'UTF-8'
if encoding == 'cp1252':
encoding = '1252'
if not encoding:
encoding = 'UTF-8'
try:
if os.name == 'nt':
os.environ['LANG'] = lang
lang = _LOCALE2WIN32.get(lang, lang)
elif os.name == 'mac' or os.uname()[0] == 'Darwin':
encoding = 'UTF-8'
locale.setlocale(locale.LC_ALL, str(lang + '.' + encoding))
except:
pass
date = time.strptime(str(datas['form']['date']), '%Y-%m-%d')
date = time.strftime(LocaleTime().LC_date.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': datas['form']['date'],
'posted': datas['form']['posted'],
'company': datas['form']['company'],
})
res['name'] = res['name'] + ' - '+ date + ' - ' + company.name
if os.name == 'nt':
os.environ['LANG'] = ''
locale.setlocale(locale.LC_ALL, '')
finally:
LOCALE_SEMAPHORE.release()
return res
OpenBalanceSheet()
class OpenIncomeStatementInit(WizardOSV):
_name = 'account.account.open_income_statement.init'
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
required=True, on_change=['fiscalyear'])
start_period = fields.Many2One('account.period', 'Start Period',
domain="[('fiscalyear', '=', fiscalyear), " \
"('start_date', '<=', (end_period, 'start_date'))]")
end_period = fields.Many2One('account.period', 'End Period',
domain="[('fiscalyear', '=', fiscalyear), " \
"('start_date', '>=', (start_period, 'start_date'))]")
company = fields.Many2One('company.company', 'Company', required=True)
posted = fields.Boolean('Posted Move', help='Only posted move')
def default_fiscalyear(self, cursor, user, context=None):
fiscalyear_obj = self.pool.get('account.fiscalyear')
if context is None:
context = {}
fiscalyear_id = fiscalyear_obj.find(cursor, user,
context.get('company', False), exception=False, context=context)
if fiscalyear_id:
return fiscalyear_obj.name_get(cursor, user, fiscalyear_id,
context=context)[0]
return False
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
def on_change_fiscalyear(self, cursor, user, ids, vals, context=None):
return {
'start_period': False,
'end_period': False,
}
OpenIncomeStatementInit()
class OpenIncomeStatement(Wizard):
'Open Income Statement'
_name = 'account.account.open_income_statement'
states = {
'init': {
'result': {
'type': 'form',
'object': 'account.account.open_income_statement.init',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('open', 'Open', 'tryton-ok', True),
],
},
},
'open': {
'result': {
'type': 'action',
'action': '_action_open',
'state': 'end',
},
},
}
def _action_open(self, cursor, user, datas, 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')
period_obj = self.pool.get('account.period')
start_period_ids = [0]
if datas['form']['start_period']:
start_period = period_obj.browse(cursor, user,
datas['form']['start_period'], context=context)
start_period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', datas['form']['fiscalyear']),
('end_date', '<=', start_period.start_date),
], context=context)
end_period_ids = []
if datas['form']['end_period']:
end_period = period_obj.browse(cursor, user,
datas['form']['end_period'], context=context)
end_period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', datas['form']['fiscalyear']),
('end_date', '<=', end_period.start_date),
], context=context)
end_period_ids = exclude(end_period_ids, start_period_ids)
if datas['form']['end_period'] not in end_period_ids:
end_period_ids.append(datas['form']['end_period'])
else:
end_period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', datas['form']['fiscalyear']),
], context=context)
end_period_ids = exclude(end_period_ids, start_period_ids)
model_data_ids = model_data_obj.search(cursor, user, [
('fs_id', '=', 'act_account_income_statement_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({
'periods': end_period_ids,
'posted': datas['form']['posted'],
'company': datas['form']['company'],
})
return res
OpenIncomeStatement()
class CreateChartAccountInit(WizardOSV):
_name = 'account.account.create_chart_account.init'
CreateChartAccountInit()
class CreateChartAccountAccount(WizardOSV):
_name = 'account.account.create_chart_account.account'
company = fields.Many2One('company.company', 'Company', required=True)
account_template = fields.Many2One('account.account.template',
'Account Template', required=True, domain=[('parent', '=', False)])
CreateChartAccountAccount()
class CreateChartAccountPropertites(WizardOSV):
_name = 'account.account.create_chart_account.properties'
company = fields.Many2One('company.company', 'Company')
account_receivable = fields.Many2One('account.account',
'Default Receivable Account',
domain="[('kind', '=', 'receivable'), ('company', '=', company)]")
account_payable = fields.Many2One('account.account',
'Default Payable Account',
domain="[('kind', '=', 'payable'), ('company', '=', company)]")
CreateChartAccountPropertites()
class CreateChartAccount(Wizard):
'Create chart account from template'
_name = 'account.account.create_chart_account'
states = {
'init': {
'result': {
'type': 'form',
'object': 'account.account.create_chart_account.init',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('account', 'Ok', 'tryton-ok', True),
],
},
},
'account': {
'result': {
'type': 'form',
'object': 'account.account.create_chart_account.account',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('create_account', 'Create', 'tryton-ok', True),
],
},
},
'create_account': {
'actions': ['_action_create_account'],
'result': {
'type': 'form',
'object': 'account.account.create_chart_account.properties',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('create_properties', 'Create', 'tryton-ok', True),
],
},
},
'create_properties': {
'result': {
'type': 'action',
'action': '_action_create_properties',
'state': 'end',
},
},
}
def _action_create_account(self, cursor, user, datas, context=None):
account_template_obj = self.pool.get('account.account.template')
tax_code_template_obj = self.pool.get('account.tax.code.template')
tax_template_obj = self.pool.get('account.tax.template')
template2account = {}
account_template_obj.create_account(cursor, user,
datas['form']['account_template'], datas['form']['company'],
context=context, template2account=template2account)
template2tax_code = {}
tax_code_template_ids = tax_code_template_obj.search(cursor, user, [
('account', '=', datas['form']['account_template']),
], context=context)
for tax_code_template in tax_code_template_obj.browse(cursor, user,
tax_code_template_ids, context=context):
tax_code_obj.create_tax_code(cursor, user, tax_code_template,
datas['form']['company'], context=context,
template2tax_code=template2tax_code)
template2tax = {}
tax_template_ids = tax_template_obj.search(cursor, user, [
('account', '=', datas['form']['account_template']),
], context=context)
for tax_template in tax_template_obj.browse(cursor, user,
tax_template_ids, context=context):
tax_template_obj.create_tax(cursor, user, tax_template,
datas['form']['company'],
template2tax_code=template2tax_code,
template2account=template2account,
context=context, template2tax=template2tax)
return {'company': datas['form']['company']}
def _action_create_properties(self, cursor, user, datas, context=None):
property_obj = self.pool.get('ir.property')
model_field_obj = self.pool.get('ir.model.field')
account_receivable_field_id = model_field_obj.search(cursor, user, [
('model.model', '=', 'relationship.party'),
('name', '=', 'account_receivable'),
], limit=1, context=context)[0]
property_ids = property_obj.search(cursor, user, [
('field', '=', account_receivable_field_id),
('res', '=', False),
('company', '=', datas['form']['company']),
], context=context)
property_obj.delete(cursor, user, property_ids, context=context)
property_obj.create(cursor, user, {
'name': 'account_receivable',
'field': account_receivable_field_id,
'value': 'account.account,' + \
str(datas['form']['account_receivable']),
'company': datas['form']['company'],
}, context=context)
account_payable_field_id = model_field_obj.search(cursor, user, [
('model.model', '=', 'relationship.party'),
('name', '=', 'account_payable'),
], limit=1, context=context)[0]
property_ids = property_obj.search(cursor, user, [
('field', '=', account_payable_field_id),
('res', '=', False),
('company', '=', datas['form']['company']),
], context=context)
property_obj.delete(cursor, user, property_ids, context=context)
property_obj.create(cursor, user, {
'name': 'account_payable',
'field': account_payable_field_id,
'value': 'account.account,' + \
str(datas['form']['account_payable']),
'company': datas['form']['company'],
}, context=context)
return {}
CreateChartAccount()
class OpenThirdPartyBalanceInit(WizardOSV):
_name = 'account.account.open_third_party_balance.init'
company = fields.Many2One('company.company', 'Company', required=True)
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
required=True)
posted = fields.Boolean('Posted Move', help='Only posted move')
def default_fiscalyear(self, cursor, user, context=None):
fiscalyear_obj = self.pool.get('account.fiscalyear')
if context is None:
context = {}
fiscalyear_id = fiscalyear_obj.find(cursor, user,
context.get('company', False), exception=False, context=context)
if fiscalyear_id:
return fiscalyear_obj.name_get(cursor, user, fiscalyear_id,
context=context)[0]
return False
def default_posted(self, cursor, user, context=None):
return False
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
OpenThirdPartyBalanceInit()
class OpenThirdPartyBalance(Wizard):
'Open Third Party Balance'
_name = 'account.account.open_third_party_balance'
states = {
'init': {
'result': {
'type': 'form',
'object': 'account.account.open_third_party_balance.init',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('print', 'Print', 'tryton-ok', True),
],
},
},
'print': {
'result': {
'type': 'print',
'report': 'account.account.third_party_balance',
'state': 'end',
},
},
}
OpenThirdPartyBalance()
class ThirdPartyBalance(Report):
_name = 'account.account.third_party_balance'
def parse(self, cursor, user, report, objects, datas, context):
party_obj = self.pool.get('relationship.party')
move_line_obj = self.pool.get('account.move.line')
company_obj = self.pool.get('company.company')
context = context.copy()
company = company_obj.browse(cursor, user,
datas['form']['company'], context=context)
context['company'] = company
context['digits'] = company.currency.digits
context['fiscalyear'] = datas['form']['fiscalyear']
line_query = move_line_obj.query_get(cursor, user, context=context)
if datas['form']['posted']:
posted_clause = "and m.state = 'posted' "
else:
posted_clause = ""
cursor.execute('SELECT l.party, SUM(l.debit), SUM(l.credit) ' \
'FROM account_move_line l ' \
'JOIN account_move m on (l.move = m.id) '
'JOIN account_account a on (l.account = a.id) '
'WHERE l.party is not null '\
'AND a.active ' \
'AND a.kind in (\'payable\',\'receivable\') ' \
'AND l.reconciliation IS NULL ' \
'AND a.company = %s ' \
'AND (l.maturity_date <= %s ' \
'OR l.maturity_date IS NULL) '\
'AND ' + line_query + ' ' \
+ posted_clause + \
'GROUP BY l.party',
(datas['form']['company'], datetime.date.today()))
res = cursor.fetchall()
party_name = dict(party_obj.name_get(
cursor, user, [x[0] for x in res],
context=context))
objects = [{'name': party_name[x[0]],
'debit': x[1],
'credit': x[2],
'solde': x[1]-x[2]} for x in res]
objects.sort(lambda x,y: cmp(x['name'],y['name']))
context['total_debit'] = sum((o['debit'] for o in objects))
context['total_credit'] = sum((o['credit'] for o in objects))
context['total_solde'] = sum((o['solde'] for o in objects))
return super(ThirdPartyBalance, self).parse(cursor, user, report,
objects, datas, context)
ThirdPartyBalance()
class OpenAgedBalanceInit(WizardOSV):
_name = 'account.account.open_aged_balance.init'
company = fields.Many2One('company.company', 'Company', required=True)
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
required=True)
balance_type = fields.Selection(
[('customer', 'Customer'), ('supplier', 'Supplier'), ('both', 'Both')],
"Type", required=True)
term1 = fields.Integer("First Term", required=True)
term2 = fields.Integer("Second Term", required=True)
term3 = fields.Integer("Third Term", required=True)
unit = fields.selection(
[('day', 'Day'), ('month', 'Month')], "Unit", required=True)
posted = fields.Boolean('Posted Move', help='Only posted move')
def default_fiscalyear(self, cursor, user, context=None):
fiscalyear_obj = self.pool.get('account.fiscalyear')
if context is None:
context = {}
fiscalyear_id = fiscalyear_obj.find(cursor, user,
context.get('company', False), exception=False, context=context)
if fiscalyear_id:
return fiscalyear_obj.name_get(cursor, user, fiscalyear_id,
context=context)[0]
return False
def default_balance_type(self, cursor, user, context=None):
return "customer"
def default_posted(self, cursor, user, context=None):
return False
def default_term1(self, cursor, user, context=None):
return 30
def default_term2(self, cursor, user, context=None):
return 60
def default_term3(self, cursor, user, context=None):
return 90
def default_unit(self, cursor, user, context=None):
return 'day'
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
OpenAgedBalanceInit()
class OpenAgedBalance(Wizard):
'Open Aged Party Balance'
_name = 'account.account.open_aged_balance'
states = {
'init': {
'result': {
'type': 'form',
'object': 'account.account.open_aged_balance.init',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('print', 'Print', 'tryton-ok', True),
],
},
},
'print': {
'actions': ['check',],
'result': {
'type': 'print',
'report': 'account.account.aged_balance',
'state': 'end',
},
},
}
def __init__(self):
super(OpenAgedBalance, self).__init__()
self._error_messages.update({
'warning': 'Warning',
'term_overlap_desc': 'You cannot define overlapping terms'})
def check(self, cursor, user, datas, context=None):
if not (datas['form']['term1'] < datas['form']['term2'] \
< datas['form']['term3']):
self.raise_user_error(cursor, error="warning",
error_description="term_overlap_desc")
return datas['form']
OpenAgedBalance()
class AgedBalance(Report):
_name = 'account.account.aged_balance'
def parse(self, cursor, user, report, objects, datas, context):
party_obj = self.pool.get('relationship.party')
move_line_obj = self.pool.get('account.move.line')
company_obj = self.pool.get('company.company')
company = company_obj.browse(cursor, user,
datas['form']['company'], context=context)
context['digits'] = company.currency.digits
context['fiscalyear'] = datas['form']['fiscalyear']
context['posted'] = datas['form']['posted']
line_query = move_line_obj.query_get(cursor, user, context=context)
terms = (datas['form']['term1'],
datas['form']['term2'],
datas['form']['term3'])
if datas['form']['unit'] == 'month':
coef = 30
else:
coef = 1
kind = {'both': ('payable','receivable'),
'supplier': ('payable',),
'customer': ('receivable',),
}[datas['form']['balance_type']]
res = {}
for position,term in enumerate(terms):
if position == 0:
term_query = '(l.maturity_date <= %s '\
'OR l.maturity_date IS NULL) '
term_args = (datetime.date.today() + \
datetime.timedelta(days=term*coef),)
else:
term_query = '(l.maturity_date <= %s '\
'AND l.maturity_date >= %s) '
term_args = (
datetime.date.today() + \
datetime.timedelta(days=term*coef),
datetime.date.today() + \
datetime.timedelta(days=terms[position-1]*coef),
)
cursor.execute(
'SELECT l.party, SUM(l.debit) - SUM(l.credit) ' \
'FROM account_move_line l ' \
'JOIN account_move m on (l.move = m.id) '
'JOIN account_account a on (l.account = a.id) '
'WHERE l.party is not null '\
'AND a.active ' \
'AND a.kind in ('+ ','.join('%s' for i in kind) + ") "\
'AND l.reconciliation IS NULL ' \
'AND a.company = %s ' \
'AND '+ term_query+\
'AND ' + line_query + ' ' \
'GROUP BY l.party',
kind + (datas['form']['company'],) + term_args)
for party, solde in cursor.fetchall():
if party in res:
res[party][position] = solde
else:
res[party] = [i[0] == position and solde or Decimal("0.0")\
for i in enumerate(terms)]
parties = party_obj.name_get(
cursor, user, [k for k in res.iterkeys()], context=context)
parties.sort(lambda x,y: cmp(x[1],y[1]))
context['main_title'] = datas['form']['balance_type']
context['unit'] = datas['form']['unit']
for i in range(3):
context['total' + str(i)] = sum((v[i] for v in res.itervalues()))
context['term' + str(i)] = terms[i]
context['company'] = company
context['parties']= ({'name': p[1],
'amount0': res[p[0]][0],
'amount1': res[p[0]][1],
'amount2': res[p[0]][2],
} for p in parties)
return super(AgedBalance, self).parse(cursor, user, report, objects,
datas, context)
AgedBalance()