Files
tryton/fiscalyear.py
T
2008-10-08 12:17:47 +02:00

341 lines
13 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.
'Fiscal Year'
from trytond.osv import fields, OSV
from trytond.wizard import Wizard, WizardOSV
import mx.DateTime
STATES = {
'readonly': "state == 'close'",
}
class FiscalYear(OSV):
'Fiscal Year'
_name = 'account.fiscalyear'
_description = __doc__
name = fields.Char('Name', size=None, required=True)
code = fields.Char('Code', size=None)
start_date = fields.Date('Starting Date', required=True, states=STATES)
end_date = fields.Date('Ending Date', required=True, states=STATES)
periods = fields.One2Many('account.period', 'fiscalyear', 'Periods',
states=STATES)
state = fields.Selection([
('open', 'Open'),
('close', 'Close'),
], 'State', readonly=True, required=True)
post_move_sequence = fields.Many2One('ir.sequence', 'Post Move Sequence',
required=True, domain="[('code', '=', 'account.move')]")
company = fields.Many2One('company.company', 'Company', required=True)
def __init__(self):
super(FiscalYear, self).__init__()
self._rpc_allowed += [
'create_period',
'create_period_3',
'close',
'reopen',
]
self._constraints += [
('check_dates', 'fiscalyear_overlaps'),
('check_post_move_sequence', 'different_post_move_sequence'),
]
self._order.insert(0, ('start_date', 'ASC'))
self._error_messages.update({
'change_post_move_sequence': 'You can not change ' \
'the post move sequence',
'no_fiscalyear_date': 'No fiscal year defined for this date!',
'fiscalyear_overlaps':
'You can not have 2 fiscal years that overlaps!',
'different_post_move_sequence':
'You must have different post move sequence per fiscal year!',
'account_balance_not_zero':
'The balance of the account "%s" must be zero!',
'close_error': 'You can not close a fiscal year until ' \
'there is older fiscal year opened!',
'reopen_error': 'You can not re-open a fiscal year until ' \
'there is youngest fiscal year closed!',
})
def default_state(self, cursor, user, context=None):
return 'open'
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 check_dates(self, cursor, user, ids):
for fiscalyear in self.browse(cursor, user, ids):
cursor.execute('SELECT id ' \
'FROM ' + self._table + ' ' \
'WHERE ((start_date <= %s AND end_date >= %s) ' \
'OR (start_date <= %s AND end_date >= %s) ' \
'OR (start_date >= %s AND end_date <= %s)) ' \
'AND company = %s ' \
'AND id != %s',
(fiscalyear.start_date, fiscalyear.start_date,
fiscalyear.end_date, fiscalyear.end_date,
fiscalyear.start_date, fiscalyear.end_date,
fiscalyear.company.id, fiscalyear.id))
if cursor.rowcount:
return False
return True
def check_post_move_sequence(self, cursor, user, ids):
for fiscalyear in self.browse(cursor, user, ids):
if self.search(cursor, user, [
('post_move_sequence', '=', fiscalyear.post_move_sequence.id),
('id', '!=', fiscalyear.id),
]):
return False
return True
def write(self, cursor, user, ids, vals, context=None):
move_obj = self.pool.get('account.move')
if vals.get('post_move_sequence'):
for fiscalyear in self.browse(cursor, user, ids, context=context):
if fiscalyear.post_move_sequence and \
fiscalyear.post_move_sequence.id != \
vals['post_move_sequence']:
self.raise_user_error(cursor, 'change_post_move_sequence',
context=context)
return super(FiscalYear, self).write(cursor, user, ids, vals,
context=context)
def delete(self, cursor, user, ids, context=None):
period_obj = self.pool.get('account.period')
period_ids = []
for fiscalyear in self.browse(cursor, user, ids, context=context):
period_ids.extend([x.id for x in fiscalyear.periods])
period_obj.delete(cursor, user, period_ids, context=context)
return super(FiscalYear, self).delete(cursor, user, ids,
context=context)
def create_period(self, cursor, user, ids, context=None, interval=1):
'''
Create periods for the fiscal years with month interval
'''
period_obj = self.pool.get('account.period')
for fiscalyear in self.browse(cursor, user, ids, context=context):
end_date = mx.DateTime.strptime(str(fiscalyear.end_date),
'%Y-%m-%d')
period_start_date = mx.DateTime.strptime(str(fiscalyear.start_date),
'%Y-%m-%d')
while period_start_date < end_date:
period_end_date = period_start_date + \
mx.DateTime.RelativeDateTime(months=interval)
period_end_date = mx.DateTime.DateTime(period_end_date.year,
period_end_date.month, 1) - \
mx.DateTime.RelativeDateTime(days=1)
if period_end_date > end_date:
period_end_date = end_date
period_obj.create(cursor, user, {
'name': period_start_date.strftime('%Y-%m') + ' - ' + \
period_end_date.strftime('%Y-%m'),
'start_date': period_start_date.strftime('%Y-%m-%d'),
'end_date': period_end_date.strftime('%Y-%m-%d'),
'fiscalyear': fiscalyear.id,
'post_move_sequence': fiscalyear.post_move_sequence.id,
'type': 'standard',
}, context=context)
period_start_date = period_end_date + \
mx.DateTime.RelativeDateTime(days=1)
return True
def create_period_3(self, cursor, user, ids, context=None):
'''
Create periods for the fiscal years with 3 months interval
'''
return self.create_period(cursor, user, ids, context=context,
interval=3)
def find(self, cursor, user, company_id, date=None, exception=True,
context=None):
'''
Return the fiscal year for the company_id
at the date or the current date.
If exception is set the function will raise an exception
if any fiscal year is found.
'''
date_obj = self.pool.get('ir.date')
if not date:
date = date_obj.today(cursor, user, context=context)
ids = self.search(cursor, user, [
('start_date', '<=', date),
('end_date', '>=', date),
('company', '=', company_id),
], order=[('start_date', 'DESC')], limit=1, context=context)
if not ids:
if exception:
self.raise_user_error(cursor, 'no_fiscalyear_date',
context=context)
else:
return False
return ids[0]
def _process_account(self, cursor, user, account, fiscalyear, context=None):
'''
Process account for a fiscal year closed
:param cursor: the database cursor
:param user: the user id
:param account: a BrowseRecord of the account
:param fiscalyear: a BrowseRecord of the fiscal year closed
:param context: the context
'''
currency_obj = self.pool.get('currency.currency')
account_obj = self.pool.get('account.account')
deferral_obj = self.pool.get('account.account.deferral')
if account.kind == 'view':
return
if not account.deferral:
if not currency_obj.is_zero(cursor, user,
fiscalyear.company.currency, account.balance):
_, account_name = account_obj.name_get(cursor, user, account.id,
context=context)[0]
self.raise_user_error(cursor, 'account_balance_not_zero',
error_args=(account_name,), context=context)
else:
deferral_obj.create(cursor, user, {
'account': account.id,
'fiscalyear': fiscalyear.id,
'debit': account.debit,
'credit': account.credit,
}, context=context)
def close(self, cursor, user, fiscalyear_id, context=None):
'''
Close a fiscal year
:param cursor: the database cursor
:param user: the user id
:param fiscalyear_id: the fiscal year id
:param context: the context
'''
period_obj = self.pool.get('account.period')
account_obj = self.pool.get('account.account')
if context is None:
context = {}
if isinstance(fiscalyear_id, list):
fiscalyear_id = fiscalyear_id[0]
fiscalyear = self.browse(cursor, user, fiscalyear_id, context=context)
if self.search(cursor, user, [
('end_date', '<=', fiscalyear.start_date),
('state', '=', 'open'),
('company', '=', fiscalyear.company.id),
], context=context):
self.raise_user_error(cursor, 'close_error', context=context)
#First close the fiscalyear to be sure
#it will not have new period created between.
self.write(cursor, user, fiscalyear_id, {
'state': 'close',
}, context=context)
period_ids = period_obj.search(cursor, user, [
('fiscalyear', '=', fiscalyear_id),
], context=context)
period_obj.close(cursor, user, period_ids, context=context)
ctx = context.copy()
ctx['fiscalyear'] = fiscalyear_id
if 'date' in context:
del context['date']
account_ids = account_obj.search(cursor, user, [
('company', '=', fiscalyear.company.id),
], context=ctx)
for account in account_obj.browse(cursor, user, account_ids,
context=ctx):
self._process_account(cursor, user, account, fiscalyear,
context=context)
def reopen(self, cursor, user, fiscalyear_id, context=None):
'''
Re-open a fiscal year
:param cursor: the database cursor
:param user: the user id
:param fiscalyear_id: the fiscal year id
:param context: the context
'''
deferral_obj = self.pool.get('account.account.deferral')
if isinstance(fiscalyear_id, list):
fiscalyear_id = fiscalyear_id[0]
fiscalyear = self.browse(cursor, user, fiscalyear_id, context=context)
if self.search(cursor, user, [
('start_date', '>=', fiscalyear.end_date),
('state', '=', 'close'),
('company', '=', fiscalyear.company.id),
], context=context):
self.raise_user_error(cursor, 'reopen_error', context=context)
deferral_ids = deferral_obj.search(cursor, user, [
('fiscalyear', '=', fiscalyear_id),
], context=context)
deferral_obj.delete(cursor, user, deferral_ids, context=context)
self.write(cursor, user, fiscalyear_id, {
'state': 'open',
}, context=context)
FiscalYear()
class CloseFiscalYearInit(WizardOSV):
_name = 'account.fiscalyear.close_fiscalyear.init'
close_fiscalyear = fields.Many2One('account.fiscalyear',
'Fiscal Year to close',
required=True,
domain="[('state', '!=', 'close')]")
CloseFiscalYearInit()
class CloseFiscalYear(Wizard):
'Close Fiscal Year'
_name = 'account.fiscalyear.close_fiscalyear'
states = {
'init': {
'result': {
'type': 'form',
'object': 'account.fiscalyear.close_fiscalyear.init',
'state': [
('end', 'Cancel', 'tryton-cancel'),
('close', 'Close', 'tryton-ok', True),
],
},
},
'close': {
'actions': ['_close'],
'result': {
'type': 'state',
'state': 'end',
},
},
}
def _close(self, cursor, user, data, context=None):
fiscalyear_obj = self.pool.get('account.fiscalyear')
fiscalyear_obj.close(cursor, user, data['form']['close_fiscalyear'],
context=context)
return {}
CloseFiscalYear()