Files
tryton/fiscalyear.py
T

456 lines
17 KiB
Python
Raw Normal View History

#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
2010-08-19 22:02:00 +02:00
from dateutil.relativedelta import relativedelta
2009-02-28 16:57:10 +01:00
from trytond.model import ModelView, ModelSQL, fields
2013-09-16 11:33:24 +02:00
from trytond.wizard import Wizard, StateView, StateTransition, StateAction, \
Button
from trytond.tools import datetime_strftime
2013-09-16 11:33:24 +02:00
from trytond.pyson import Eval, If, PYSONEncoder
2010-08-19 22:02:00 +02:00
from trytond.transaction import Transaction
2011-07-03 13:18:40 +02:00
from trytond.pool import Pool
2013-09-16 11:33:24 +02:00
__all__ = ['FiscalYear',
'BalanceNonDeferralStart', 'BalanceNonDeferral',
'CloseFiscalYearStart', 'CloseFiscalYear']
2012-09-24 22:17:29 +02:00
2008-03-13 23:22:59 +01:00
STATES = {
2011-08-21 11:20:29 +02:00
'readonly': Eval('state') == 'close',
2008-03-13 23:22:59 +01:00
}
DEPENDS = ['state']
2008-03-13 23:22:59 +01:00
2009-02-28 16:57:10 +01:00
class FiscalYear(ModelSQL, ModelView):
2008-03-13 23:22:59 +01:00
'Fiscal Year'
2012-09-24 22:17:29 +02:00
__name__ = 'account.fiscalyear'
name = fields.Char('Name', size=None, required=True, depends=DEPENDS)
2008-03-13 23:22:59 +01:00
code = fields.Char('Code', size=None)
start_date = fields.Date('Starting Date', required=True, states=STATES,
depends=DEPENDS)
end_date = fields.Date('Ending Date', required=True, states=STATES,
depends=DEPENDS)
2008-03-13 23:22:59 +01:00
periods = fields.One2Many('account.period', 'fiscalyear', 'Periods',
states=STATES, depends=DEPENDS)
2008-03-13 23:22:59 +01:00
state = fields.Selection([
('open', 'Open'),
('close', 'Close'),
2008-03-17 18:32:06 +01:00
], 'State', readonly=True, required=True)
post_move_sequence = fields.Many2One('ir.sequence', 'Post Move Sequence',
2010-01-20 10:56:33 +01:00
required=True, domain=[('code', '=', 'account.move'),
2009-06-23 18:52:32 +02:00
['OR',
2010-01-20 10:56:33 +01:00
('company', '=', Eval('company')),
2012-03-27 10:43:03 +02:00
('company', '=', None)
2010-01-20 10:56:33 +01:00
]],
context={
'code': 'account.move',
'company': Eval('company'),
},
depends=['company'])
2009-04-11 12:42:55 +02:00
company = fields.Many2One('company.company', 'Company', required=True,
2011-08-21 11:20:29 +02:00
domain=[
('id', If(Eval('context', {}).contains('company'), '=', '!='),
2013-10-09 18:25:04 +02:00
Eval('context', {}).get('company', -1)),
2012-03-14 09:23:04 +01:00
], select=True)
2008-03-13 23:22:59 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
def __setup__(cls):
super(FiscalYear, cls).__setup__()
cls._order.insert(0, ('start_date', 'ASC'))
cls._error_messages.update({
'change_post_move_sequence': ('You can not change the post '
'move sequence in fiscal year "%s".'),
'no_fiscalyear_date': 'No fiscal year defined for "%s".',
'fiscalyear_overlaps': ('Fiscal year "%(first)s" and '
'"%(second)s" overlap.'),
'different_post_move_sequence': ('Fiscal year "%(first)s" and '
'"%(second)s" have the same post move sequence.'),
'account_balance_not_zero': ('The balance of the account "%s" '
'must be zero.'),
'close_error': ('You can not close fiscal year "%s" until you '
'close all previous fiscal years.'),
'reopen_error': ('You can not reopen fiscal year "%s" until '
'you reopen all later fiscal years.'),
})
2012-09-24 22:17:29 +02:00
cls._buttons.update({
2012-03-25 00:17:23 +01:00
'create_period': {
'invisible': ((Eval('state') != 'open')
| Eval('periods', [0])),
},
'create_period_3': {
'invisible': ((Eval('state') != 'open')
| Eval('periods', [0])),
},
'close': {
'invisible': Eval('state') != 'open',
},
'reopen': {
'invisible': Eval('state') != 'close',
},
})
2008-03-13 23:22:59 +01:00
2012-09-24 22:17:29 +02:00
@staticmethod
def default_state():
2008-03-13 23:22:59 +01:00
return 'open'
2012-09-24 22:17:29 +02:00
@staticmethod
def default_company():
2012-03-27 10:43:03 +02:00
return Transaction().context.get('company')
@classmethod
def validate(cls, years):
super(FiscalYear, cls).validate(years)
for year in years:
year.check_dates()
year.check_post_move_sequence()
2012-09-24 22:17:29 +02:00
def check_dates(self):
2010-08-19 22:02:00 +02:00
cursor = Transaction().cursor
2013-09-06 22:33:47 +02:00
table = self.__table__()
cursor.execute(*table.select(table.id,
where=(((table.start_date <= self.start_date)
& (table.end_date >= self.start_date))
| ((table.start_date <= self.end_date)
& (table.end_date >= self.end_date))
| ((table.start_date >= self.start_date)
& (table.end_date <= self.end_date)))
& (table.company == self.company.id)
& (table.id != self.id)))
second_id = cursor.fetchone()
if second_id:
second = self.__class__(second_id[0])
self.raise_user_error('fiscalyear_overlaps', {
'first': self.rec_name,
'second': second.rec_name,
})
2012-09-24 22:17:29 +02:00
def check_post_move_sequence(self):
years = self.search([
('post_move_sequence', '=', self.post_move_sequence.id),
('id', '!=', self.id),
])
if years:
self.raise_user_error('different_post_move_sequence', {
'first': self.rec_name,
'second': years[0].rec_name,
})
2012-09-24 22:17:29 +02:00
@classmethod
2013-12-19 14:44:33 +01:00
def write(cls, *args):
actions = iter(args)
args = []
for fiscalyears, values in zip(actions, actions):
if values.get('post_move_sequence'):
for fiscalyear in fiscalyears:
if (fiscalyear.post_move_sequence
and fiscalyear.post_move_sequence.id !=
values['post_move_sequence']):
cls.raise_user_error('change_post_move_sequence', (
fiscalyear.rec_name,))
values = values.copy()
if 'periods' in values:
2013-12-20 12:33:20 +01:00
operator = ['delete', 'remove', 'create', 'write', 'add']
2013-12-19 14:44:33 +01:00
values['periods'].sort(lambda x, y:
cmp(operator.index(x[0]), operator.index(y[0])))
args.extend((fiscalyears, values))
super(FiscalYear, cls).write(*args)
2008-10-07 12:25:54 +02:00
2012-09-24 22:17:29 +02:00
@classmethod
def delete(cls, fiscalyears):
Period = Pool().get('account.period')
Period.delete([p for f in fiscalyears for p in f.periods])
super(FiscalYear, cls).delete(fiscalyears)
2012-09-24 22:17:29 +02:00
@classmethod
2012-03-25 00:17:23 +01:00
@ModelView.button
2012-09-24 22:17:29 +02:00
def create_period(cls, fiscalyears, interval=1):
2008-03-13 23:22:59 +01:00
'''
Create periods for the fiscal years with month interval
'''
2012-09-24 22:17:29 +02:00
Period = Pool().get('account.period')
2013-01-06 22:18:13 +01:00
to_create = []
2012-09-24 22:17:29 +02:00
for fiscalyear in fiscalyears:
period_start_date = fiscalyear.start_date
while period_start_date < fiscalyear.end_date:
2008-03-13 23:22:59 +01:00
period_end_date = period_start_date + \
2013-02-16 18:54:35 +01:00
relativedelta(months=interval - 1) + \
relativedelta(day=31)
if period_end_date > fiscalyear.end_date:
period_end_date = fiscalyear.end_date
name = datetime_strftime(period_start_date, '%Y-%m')
if name != datetime_strftime(period_end_date, '%Y-%m'):
name += ' - ' + datetime_strftime(period_end_date, '%Y-%m')
2013-01-06 22:18:13 +01:00
to_create.append({
2008-11-22 14:53:02 +01:00
'name': name,
'start_date': period_start_date,
'end_date': period_end_date,
2008-03-13 23:22:59 +01:00
'fiscalyear': fiscalyear.id,
2008-09-16 19:43:07 +02:00
'type': 'standard',
2010-08-19 22:02:00 +02:00
})
period_start_date = period_end_date + relativedelta(days=1)
2013-01-06 22:18:13 +01:00
if to_create:
Period.create(to_create)
2008-03-13 23:22:59 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
2012-03-25 00:17:23 +01:00
@ModelView.button
2012-09-24 22:17:29 +02:00
def create_period_3(cls, fiscalyears):
2008-03-13 23:22:59 +01:00
'''
Create periods for the fiscal years with 3 months interval
'''
2012-09-24 22:17:29 +02:00
cls.create_period(fiscalyears, interval=3)
2008-03-13 23:22:59 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
def find(cls, company_id, date=None, exception=True):
2008-03-13 23:22:59 +01:00
'''
Return the fiscal year for the company_id
at the date or the current date.
2008-03-13 23:22:59 +01:00
If exception is set the function will raise an exception
if any fiscal year is found.
'''
pool = Pool()
Lang = pool.get('ir.lang')
Date = pool.get('ir.date')
2008-10-08 12:17:47 +02:00
2008-03-13 23:22:59 +01:00
if not date:
2012-09-24 22:17:29 +02:00
date = Date.today()
fiscalyears = cls.search([
2008-03-13 23:22:59 +01:00
('start_date', '<=', date),
('end_date', '>=', date),
('company', '=', company_id),
2010-08-19 22:02:00 +02:00
], order=[('start_date', 'DESC')], limit=1)
2012-09-24 22:17:29 +02:00
if not fiscalyears:
2008-03-13 23:22:59 +01:00
if exception:
language = Transaction().language
languages = Lang.search([('code', '=', language)])
if not languages:
languages = Lang.search([('code', '=', 'en_US')])
language, = languages
formatted = Lang.strftime(date, language.code, language.date)
cls.raise_user_error('no_fiscalyear_date', (formatted,))
2008-03-13 23:22:59 +01:00
else:
2012-03-27 10:43:03 +02:00
return None
2012-09-24 22:17:29 +02:00
return fiscalyears[0].id
2008-03-13 23:22:59 +01:00
2012-09-24 22:17:29 +02:00
def _process_account(self, account):
2008-09-19 15:00:26 +02:00
'''
Process account for a fiscal year closed
'''
2012-09-24 22:17:29 +02:00
Currency = Pool().get('currency.currency')
Deferral = Pool().get('account.account.deferral')
2008-09-19 15:00:26 +02:00
2012-09-25 11:26:00 +02:00
if account.kind == 'view':
2008-09-19 15:00:26 +02:00
return
2012-09-25 11:26:00 +02:00
if not account.deferral:
2012-09-24 22:17:29 +02:00
if not Currency.is_zero(self.company.currency, account.balance):
2010-08-19 22:02:00 +02:00
self.raise_user_error('account_balance_not_zero',
error_args=(account.rec_name,))
2008-09-19 15:00:26 +02:00
else:
2013-01-06 22:18:13 +01:00
Deferral.create([{
'account': account.id,
'fiscalyear': self.id,
'debit': account.debit,
'credit': account.credit,
}])
2008-09-19 15:00:26 +02:00
2012-09-24 22:17:29 +02:00
@classmethod
2012-03-25 00:17:23 +01:00
@ModelView.button
2012-09-24 22:17:29 +02:00
def close(cls, fiscalyears):
2008-09-19 15:00:26 +02:00
'''
Close a fiscal year
'''
2012-09-24 22:17:29 +02:00
pool = Pool()
Period = pool.get('account.period')
Account = pool.get('account.account')
2008-09-19 15:00:26 +02:00
2012-09-24 22:17:29 +02:00
for fiscalyear in fiscalyears:
if cls.search([
2013-02-16 18:54:35 +01:00
('end_date', '<=', fiscalyear.start_date),
('state', '=', 'open'),
('company', '=', fiscalyear.company.id),
]):
cls.raise_user_error('close_error', (fiscalyear.rec_name,))
2008-09-19 15:00:26 +02:00
2012-03-25 00:17:23 +01:00
#First close the fiscalyear to be sure
#it will not have new period created between.
2012-09-24 22:17:29 +02:00
cls.write([fiscalyear], {
2012-03-25 00:17:23 +01:00
'state': 'close',
})
2012-09-24 22:17:29 +02:00
periods = Period.search([
('fiscalyear', '=', fiscalyear.id),
])
Period.close(periods)
2012-03-25 00:17:23 +01:00
with Transaction().set_context(fiscalyear=fiscalyear.id,
date=None, cumulate=True):
2012-09-24 22:17:29 +02:00
accounts = Account.search([
('company', '=', fiscalyear.company.id),
])
2012-03-25 00:17:23 +01:00
for account in accounts:
2012-09-24 22:17:29 +02:00
fiscalyear._process_account(account)
2012-03-25 00:17:23 +01:00
2012-09-24 22:17:29 +02:00
@classmethod
2012-03-25 00:17:23 +01:00
@ModelView.button
2012-09-24 22:17:29 +02:00
def reopen(cls, fiscalyears):
2008-09-19 15:00:26 +02:00
'''
Re-open a fiscal year
'''
2012-09-24 22:17:29 +02:00
Deferral = Pool().get('account.account.deferral')
2008-09-19 15:00:26 +02:00
2012-09-24 22:17:29 +02:00
for fiscalyear in fiscalyears:
if cls.search([
2013-02-16 18:54:35 +01:00
('start_date', '>=', fiscalyear.end_date),
('state', '=', 'close'),
('company', '=', fiscalyear.company.id),
]):
2012-09-24 22:17:29 +02:00
cls.raise_user_error('reopen_error')
2008-09-19 15:00:26 +02:00
2012-09-24 22:17:29 +02:00
deferrals = Deferral.search([
2012-03-25 00:17:23 +01:00
('fiscalyear', '=', fiscalyear.id),
])
2012-09-24 22:17:29 +02:00
Deferral.delete(deferrals)
2008-09-19 15:00:26 +02:00
2012-09-24 22:17:29 +02:00
cls.write([fiscalyear], {
2012-03-25 00:17:23 +01:00
'state': 'open',
})
2008-09-19 15:00:26 +02:00
2014-01-31 14:06:56 +01:00
@classmethod
def search_rec_name(cls, name, clause):
return ['OR',
('code',) + tuple(clause[1:]),
(cls._rec_name,) + tuple(clause[1:]),
]
2013-09-16 11:33:24 +02:00
class BalanceNonDeferralStart(ModelView):
'Balance Non-Deferral'
__name__ = 'account.fiscalyear.balance_non_deferral.start'
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
required=True, domain=[('state', '=', 'open')])
journal = fields.Many2One('account.journal', 'Journal', required=True,
domain=[
('type', '=', 'situation'),
])
period = fields.Many2One('account.period', 'Period', required=True,
domain=[
('fiscalyear', '=', Eval('fiscalyear')),
('type', '=', 'adjustment'),
],
depends=['fiscalyear'])
credit_account = fields.Many2One('account.account', 'Credit Account',
required=True,
domain=[
('kind', '!=', 'view'),
2013-10-09 18:25:04 +02:00
('company', '=', Eval('context', {}).get('company', -1)),
2013-09-16 11:33:24 +02:00
('deferral', '=', True),
])
debit_account = fields.Many2One('account.account', 'Debit Account',
required=True,
domain=[
('kind', '!=', 'view'),
2013-10-09 18:25:04 +02:00
('company', '=', Eval('context', {}).get('company', -1)),
2013-09-16 11:33:24 +02:00
('deferral', '=', True),
])
class BalanceNonDeferral(Wizard):
'Balance Non-Deferral'
__name__ = 'account.fiscalyear.balance_non_deferral'
start = StateView('account.fiscalyear.balance_non_deferral.start',
'account.fiscalyear_balance_non_deferral_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'balance', 'tryton-ok', default=True),
])
balance = StateAction('account.act_move_line_form')
def get_move_line(self, account):
pool = Pool()
Line = pool.get('account.move.line')
if account.company.currency.is_zero(account.balance):
return
line = Line()
line.account = account
if account.balance >= 0:
line.credit = abs(account.balance)
line.debit = 0
else:
line.credit = 0
line.debit = abs(account.balance)
return line
def get_counterpart_line(self, amount):
pool = Pool()
Line = pool.get('account.move.line')
if self.start.fiscalyear.company.currency.is_zero(amount):
return
line = Line()
if amount >= 0:
line.credit = abs(amount)
line.debit = 0
line.account = self.start.credit_account
else:
line.credit = 0
line.debit = abs(amount)
line.account = self.start.debit_account
return line
def create_move(self):
pool = Pool()
Account = pool.get('account.account')
Move = pool.get('account.move')
with Transaction().set_context(fiscalyear=self.start.fiscalyear.id,
date=None, cumulate=False):
accounts = Account.search([
('company', '=', self.start.fiscalyear.company.id),
('deferral', '=', False),
])
lines = []
for account in accounts:
line = self.get_move_line(account)
if line:
lines.append(line)
if not lines:
return
amount = sum(l.debit - l.credit for l in lines)
counter_part_line = self.get_counterpart_line(amount)
if counter_part_line:
lines.append(counter_part_line)
move = Move()
move.period = self.start.period
move.journal = self.start.journal
move.date = self.start.period.start_date
move.origin = self.start.fiscalyear
move.lines = lines
move.save()
return move
def do_balance(self, action):
self.create_move()
action['pyson_domain'] = PYSONEncoder().encode([
('origin', '=', str(self.start.fiscalyear)),
])
return action, {}
2012-01-02 17:54:33 +01:00
class CloseFiscalYearStart(ModelView):
'Close Fiscal Year'
2012-09-24 22:17:29 +02:00
__name__ = 'account.fiscalyear.close.start'
close_fiscalyear = fields.Many2One('account.fiscalyear',
2010-01-20 10:56:33 +01:00
'Fiscal Year to close', required=True,
domain=[('state', '!=', 'close')])
class CloseFiscalYear(Wizard):
'Close Fiscal Year'
2012-09-24 22:17:29 +02:00
__name__ = 'account.fiscalyear.close'
2012-01-02 17:54:33 +01:00
start = StateView('account.fiscalyear.close.start',
'account.fiscalyear_close_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Close', 'close', 'tryton-ok', default=True),
])
close = StateTransition()
2012-09-24 22:17:29 +02:00
def transition_close(self):
Fiscalyear = Pool().get('account.fiscalyear')
Fiscalyear.close([self.start.close_fiscalyear])
2012-01-02 17:54:33 +01:00
return 'end'