first commit
This commit is contained in:
716
modules/account/fiscalyear.py
Normal file
716
modules/account/fiscalyear.py
Normal file
@@ -0,0 +1,716 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from sql import Null
|
||||
from sql.conditionals import Case
|
||||
from sql.operators import Equal, NotEqual
|
||||
|
||||
from trytond.cache import Cache
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import (
|
||||
Exclude, ModelSQL, ModelView, Unique, Workflow, fields)
|
||||
from trytond.model.exceptions import AccessError
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, Id
|
||||
from trytond.rpc import RPC
|
||||
from trytond.sql.functions import DateRange
|
||||
from trytond.sql.operators import RangeOverlap
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.wizard import (
|
||||
Button, StateAction, StateTransition, StateView, Wizard)
|
||||
|
||||
from .exceptions import (
|
||||
FiscalYearCloseError, FiscalYearNotFoundError, FiscalYearReOpenError)
|
||||
|
||||
STATES = {
|
||||
'readonly': Eval('state') != 'open',
|
||||
}
|
||||
|
||||
|
||||
class FiscalYear(Workflow, ModelSQL, ModelView):
|
||||
__name__ = 'account.fiscalyear'
|
||||
name = fields.Char('Name', size=None, required=True)
|
||||
start_date = fields.Date(
|
||||
"Start Date", required=True, states=STATES,
|
||||
domain=[('start_date', '<=', Eval('end_date', None))])
|
||||
end_date = fields.Date(
|
||||
"End Date", required=True, states=STATES,
|
||||
domain=[('end_date', '>=', Eval('start_date', None))])
|
||||
periods = fields.One2Many('account.period', 'fiscalyear', 'Periods',
|
||||
states=STATES,
|
||||
domain=[
|
||||
('company', '=', Eval('company', -1)),
|
||||
],
|
||||
order=[('start_date', 'ASC'), ('id', 'ASC')])
|
||||
state = fields.Selection([
|
||||
('open', 'Open'),
|
||||
('closed', 'Closed'),
|
||||
('locked', 'Locked'),
|
||||
], 'State', readonly=True, required=True, sort=False)
|
||||
move_sequence = fields.Many2One(
|
||||
'ir.sequence.strict', "Move Sequence", required=True,
|
||||
domain=[
|
||||
('sequence_type', '=',
|
||||
Id('account', 'sequence_type_account_move')),
|
||||
('company', '=', Eval('company', -1)),
|
||||
],
|
||||
help="Used to generate the move number when posting "
|
||||
"if the period has no sequence.")
|
||||
company = fields.Many2One(
|
||||
'company.company', "Company", required=True)
|
||||
icon = fields.Function(fields.Char("Icon"), 'get_icon')
|
||||
_find_cache = Cache(__name__ + '.find', context=False)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
t = cls.__table__()
|
||||
cls._sql_constraints += [
|
||||
('dates_overlap',
|
||||
Exclude(t,
|
||||
(t.company, Equal),
|
||||
(DateRange(t.start_date, t.end_date, '[]'), RangeOverlap),
|
||||
),
|
||||
'account.msg_fiscalyear_overlap'),
|
||||
('open_earlier',
|
||||
Exclude(t,
|
||||
(t.company, Equal),
|
||||
(DateRange(
|
||||
Case(
|
||||
(t.state == 'open', t.start_date), else_=Null),
|
||||
t.end_date), RangeOverlap),
|
||||
(Case((t.state == 'open', t.id), else_=-1), NotEqual)),
|
||||
'account.msg_open_fiscalyear_earlier'),
|
||||
('move_sequence_unique', Unique(t, t.move_sequence),
|
||||
'account.msg_fiscalyear_move_sequence_unique'),
|
||||
]
|
||||
cls._order.insert(0, ('start_date', 'DESC'))
|
||||
cls._transitions |= set((
|
||||
('open', 'closed'),
|
||||
('closed', 'locked'),
|
||||
('closed', 'open'),
|
||||
))
|
||||
cls._buttons.update({
|
||||
'create_periods': {
|
||||
'invisible': ((Eval('state') != 'open')
|
||||
| Eval('periods', [0])),
|
||||
'depends': ['state'],
|
||||
},
|
||||
'close': {
|
||||
'invisible': Eval('state') != 'open',
|
||||
'depends': ['state'],
|
||||
},
|
||||
'reopen': {
|
||||
'invisible': Eval('state') != 'closed',
|
||||
'depends': ['state'],
|
||||
},
|
||||
'lock_': {
|
||||
'invisible': Eval('state') != 'closed',
|
||||
'depends': ['state'],
|
||||
},
|
||||
})
|
||||
cls.__rpc__.update({
|
||||
'create_period': RPC(readonly=False, instantiate=0),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module):
|
||||
pool = Pool()
|
||||
Period = pool.get('account.period')
|
||||
Sequence = pool.get('ir.sequence')
|
||||
SequenceStrict = pool.get('ir.sequence.strict')
|
||||
|
||||
table_h = cls.__table_handler__(module)
|
||||
cursor = Transaction().connection.cursor()
|
||||
t = cls.__table__()
|
||||
|
||||
migrate_move_sequence = not table_h.column_exist('move_sequence')
|
||||
|
||||
super().__register__(module)
|
||||
# Migration from 6.8: rename state close to closed
|
||||
cursor.execute(
|
||||
*t.update([t.state], ['closed'], where=t.state == 'close'))
|
||||
|
||||
# Migrationn from 7.4: use strict sequence
|
||||
if (table_h.column_exist('post_move_sequence')
|
||||
and migrate_move_sequence):
|
||||
table_h.not_null_action('post_move_sequence', 'remove')
|
||||
|
||||
period_h = Period.__table_handler__(module)
|
||||
period = Period.__table__()
|
||||
old2new = {}
|
||||
period_migrated = (
|
||||
period_h.column_exist('post_move_sequence')
|
||||
and period_h.column_exist('move_sequence'))
|
||||
if period_migrated:
|
||||
cursor.execute(*period.select(
|
||||
period.post_move_sequence, period.move_sequence))
|
||||
old2new.update(cursor)
|
||||
|
||||
cursor.execute(*t.select(t.post_move_sequence, distinct=True))
|
||||
for sequence_id, in cursor:
|
||||
if sequence_id not in old2new:
|
||||
sequence = Sequence(sequence_id)
|
||||
new_sequence = SequenceStrict(
|
||||
name=sequence.name,
|
||||
sequence_type=sequence.sequence_type,
|
||||
prefix=sequence.prefix,
|
||||
suffix=sequence.suffix,
|
||||
type=sequence.type,
|
||||
number_next=sequence.number_next,
|
||||
number_increment=sequence.number_increment,
|
||||
padding=sequence.padding,
|
||||
timestamp_rounding=sequence.timestamp_rounding,
|
||||
timestamp_offset=sequence.timestamp_offset,
|
||||
last_timestamp=sequence.last_timestamp,
|
||||
company=sequence.company)
|
||||
new_sequence.save()
|
||||
old2new[sequence_id] = new_sequence.id
|
||||
|
||||
for old_id, new_id in old2new.items():
|
||||
cursor.execute(*t.update(
|
||||
[t.move_sequence], [new_id],
|
||||
where=t.post_move_sequence == old_id))
|
||||
|
||||
if period_migrated:
|
||||
table_h.drop_column('post_move_sequence')
|
||||
period_h.drop_column('post_move_sequence')
|
||||
|
||||
@staticmethod
|
||||
def default_state():
|
||||
return 'open'
|
||||
|
||||
@staticmethod
|
||||
def default_company():
|
||||
return Transaction().context.get('company')
|
||||
|
||||
def get_icon(self, name):
|
||||
return {
|
||||
'open': 'tryton-account-open',
|
||||
'closed': 'tryton-account-close',
|
||||
'locked': 'tryton-account-block',
|
||||
}.get(self.state)
|
||||
|
||||
@classmethod
|
||||
def validate_fields(cls, fiscalyears, field_names):
|
||||
super().validate_fields(fiscalyears, field_names)
|
||||
cls.check_period_dates(fiscalyears, field_names)
|
||||
|
||||
@classmethod
|
||||
def check_period_dates(cls, fiscalyears, field_names=None):
|
||||
pool = Pool()
|
||||
Period = pool.get('account.period')
|
||||
if field_names and not (field_names & {'start_date', 'end_date'}):
|
||||
return
|
||||
periods = [p for f in fiscalyears for p in f.periods]
|
||||
Period.check_fiscalyear_dates(periods, field_names={'fiscalyear'})
|
||||
|
||||
@classmethod
|
||||
def check_modification(
|
||||
cls, mode, fiscalyears, values=None, external=False):
|
||||
pool = Pool()
|
||||
Move = pool.get('account.move')
|
||||
super().check_modification(
|
||||
mode, fiscalyears, values=values, external=external)
|
||||
if mode == 'write' and 'move_sequence' in values:
|
||||
for fiscalyear in fiscalyears:
|
||||
if sequence := fiscalyear.move_sequence:
|
||||
if sequence.id != values['move_sequence']:
|
||||
if Move.search([
|
||||
('period.fiscalyear', '=', fiscalyear.id),
|
||||
('state', '=', 'posted'),
|
||||
], limit=1):
|
||||
raise AccessError(
|
||||
gettext('account.'
|
||||
'msg_change_fiscalyear_move_sequence',
|
||||
fiscalyear=fiscalyear.rec_name))
|
||||
|
||||
@classmethod
|
||||
def on_modification(cls, mode, records, field_names=None):
|
||||
super().on_modification(mode, records, field_names=field_names)
|
||||
cls._find_cache.clear()
|
||||
|
||||
@classmethod
|
||||
def create_period(cls, fiscalyears, interval=1, end_day=31):
|
||||
'''
|
||||
Create periods for the fiscal years with month interval
|
||||
'''
|
||||
Period = Pool().get('account.period')
|
||||
to_create = []
|
||||
for fiscalyear in fiscalyears:
|
||||
period_start_date = fiscalyear.start_date
|
||||
while period_start_date < fiscalyear.end_date:
|
||||
month_offset = 1 if period_start_date.day < end_day else 0
|
||||
period_end_date = (period_start_date
|
||||
+ relativedelta(months=interval - month_offset)
|
||||
+ relativedelta(day=end_day))
|
||||
if period_end_date > fiscalyear.end_date:
|
||||
period_end_date = fiscalyear.end_date
|
||||
name = period_start_date.strftime('%Y-%m')
|
||||
if name != period_end_date.strftime('%Y-%m'):
|
||||
name += ' - ' + period_end_date.strftime('%Y-%m')
|
||||
to_create.append({
|
||||
'name': name,
|
||||
'start_date': period_start_date,
|
||||
'end_date': period_end_date,
|
||||
'fiscalyear': fiscalyear.id,
|
||||
'type': 'standard',
|
||||
})
|
||||
period_start_date = period_end_date + relativedelta(days=1)
|
||||
if to_create:
|
||||
Period.create(to_create)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button_action('account.act_create_periods')
|
||||
def create_periods(cls, fiscalyears):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def find(cls, company, date=None, test_state=True):
|
||||
'''
|
||||
Return the fiscal year for the company at the date or the current date
|
||||
or raise FiscalYearNotFoundError.
|
||||
If test_state is true, it searches on non-closed fiscal years
|
||||
'''
|
||||
pool = Pool()
|
||||
Lang = pool.get('ir.lang')
|
||||
Date = pool.get('ir.date')
|
||||
Company = pool.get('company.company')
|
||||
|
||||
company_id = int(company) if company is not None else None
|
||||
if not date:
|
||||
with Transaction().set_context(company=company_id):
|
||||
date = Date.today()
|
||||
key = (company_id, date)
|
||||
fiscalyear = cls._find_cache.get(key, -1)
|
||||
if fiscalyear is not None and fiscalyear < 0:
|
||||
clause = [
|
||||
('start_date', '<=', date),
|
||||
('end_date', '>=', date),
|
||||
('company', '=', company_id),
|
||||
]
|
||||
fiscalyears = cls.search(
|
||||
clause, order=[('start_date', 'DESC')], limit=1)
|
||||
if fiscalyears:
|
||||
fiscalyear, = fiscalyears
|
||||
else:
|
||||
fiscalyear = None
|
||||
cls._find_cache.set(key, int(fiscalyear) if fiscalyear else None)
|
||||
elif fiscalyear is not None:
|
||||
fiscalyear = cls(fiscalyear)
|
||||
found = fiscalyear and (not test_state or fiscalyear.state == 'open')
|
||||
if not found:
|
||||
lang = Lang.get()
|
||||
if company is not None and not isinstance(company, Company):
|
||||
company = Company(company)
|
||||
if not fiscalyear:
|
||||
raise FiscalYearNotFoundError(
|
||||
gettext('account.msg_no_fiscalyear_date',
|
||||
date=lang.strftime(date),
|
||||
company=company.rec_name if company else ''))
|
||||
else:
|
||||
raise FiscalYearNotFoundError(
|
||||
gettext('account.msg_no_open_fiscalyear_date',
|
||||
date=lang.strftime(date),
|
||||
fiscalyear=fiscalyear.rec_name,
|
||||
company=company.rec_name if company else ''))
|
||||
else:
|
||||
return fiscalyear
|
||||
|
||||
def get_deferral(self, account):
|
||||
'Computes deferrals for accounts'
|
||||
pool = Pool()
|
||||
Currency = pool.get('currency.currency')
|
||||
Deferral = pool.get('account.account.deferral')
|
||||
|
||||
if not account.type:
|
||||
return
|
||||
if not account.deferral:
|
||||
if not Currency.is_zero(self.company.currency, account.balance):
|
||||
raise FiscalYearCloseError(
|
||||
gettext('account'
|
||||
'.msg_close_fiscalyear_account_balance_not_zero',
|
||||
account=account.rec_name))
|
||||
else:
|
||||
deferral = Deferral()
|
||||
deferral.account = account
|
||||
deferral.fiscalyear = self
|
||||
deferral.debit = account.debit
|
||||
deferral.credit = account.credit
|
||||
deferral.line_count = account.line_count
|
||||
deferral.amount_second_currency = account.amount_second_currency
|
||||
return deferral
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('closed')
|
||||
def close(cls, fiscalyears):
|
||||
'''
|
||||
Close a fiscal year
|
||||
'''
|
||||
pool = Pool()
|
||||
Period = pool.get('account.period')
|
||||
Account = pool.get('account.account')
|
||||
Deferral = pool.get('account.account.deferral')
|
||||
|
||||
# Prevent create new fiscal year or period
|
||||
cls.lock()
|
||||
Period.lock()
|
||||
|
||||
deferrals = []
|
||||
for fiscalyear in fiscalyears:
|
||||
if cls.search([
|
||||
('end_date', '<=', fiscalyear.start_date),
|
||||
('state', '=', 'open'),
|
||||
('company', '=', fiscalyear.company.id),
|
||||
]):
|
||||
raise FiscalYearCloseError(
|
||||
gettext('account.msg_close_fiscalyear_earlier',
|
||||
fiscalyear=fiscalyear.rec_name))
|
||||
|
||||
periods = Period.search([
|
||||
('fiscalyear', '=', fiscalyear.id),
|
||||
])
|
||||
Period.close(periods)
|
||||
|
||||
with Transaction().set_context(fiscalyear=fiscalyear.id,
|
||||
date=None, cumulate=True, journal=None):
|
||||
accounts = Account.search([
|
||||
('company', '=', fiscalyear.company.id),
|
||||
])
|
||||
for account in accounts:
|
||||
deferral = fiscalyear.get_deferral(account)
|
||||
if deferral:
|
||||
deferrals.append(deferral)
|
||||
Deferral.save(deferrals)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('open')
|
||||
def reopen(cls, fiscalyears):
|
||||
'''
|
||||
Reopen a fiscal year
|
||||
'''
|
||||
Deferral = Pool().get('account.account.deferral')
|
||||
|
||||
for fiscalyear in fiscalyears:
|
||||
if cls.search([
|
||||
('start_date', '>=', fiscalyear.end_date),
|
||||
('state', '!=', 'open'),
|
||||
('company', '=', fiscalyear.company.id),
|
||||
]):
|
||||
raise FiscalYearReOpenError(
|
||||
gettext('account.msg_reopen_fiscalyear_later',
|
||||
fiscalyear=fiscalyear.rec_name))
|
||||
|
||||
deferrals = Deferral.search([
|
||||
('fiscalyear', '=', fiscalyear.id),
|
||||
])
|
||||
Deferral.delete(deferrals)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('locked')
|
||||
def lock_(cls, fiscalyears):
|
||||
pool = Pool()
|
||||
Period = pool.get('account.period')
|
||||
periods = Period.search([
|
||||
('fiscalyear', 'in', [f.id for f in fiscalyears]),
|
||||
])
|
||||
Period.lock_(periods)
|
||||
|
||||
|
||||
class BalanceNonDeferralStart(ModelView):
|
||||
__name__ = 'account.fiscalyear.balance_non_deferral.start'
|
||||
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
|
||||
required=True, domain=[('state', '=', 'open')])
|
||||
company = fields.Function(fields.Many2One('company.company', 'Company'),
|
||||
'on_change_with_company')
|
||||
journal = fields.Many2One('account.journal', 'Journal', required=True,
|
||||
domain=[
|
||||
('type', '=', 'situation'),
|
||||
],
|
||||
context={
|
||||
'company': Eval('company', -1),
|
||||
},
|
||||
depends={'company'})
|
||||
period = fields.Many2One('account.period', 'Period', required=True,
|
||||
domain=[
|
||||
('fiscalyear', '=', Eval('fiscalyear', -1)),
|
||||
('type', '=', 'adjustment'),
|
||||
])
|
||||
credit_account = fields.Many2One('account.account', 'Credit Account',
|
||||
required=True,
|
||||
domain=[
|
||||
('type', '!=', None),
|
||||
('closed', '!=', True),
|
||||
('company', '=', Eval('company', -1)),
|
||||
('deferral', '=', True),
|
||||
])
|
||||
debit_account = fields.Many2One('account.account', 'Debit Account',
|
||||
required=True,
|
||||
domain=[
|
||||
('type', '!=', None),
|
||||
('closed', '!=', True),
|
||||
('company', '=', Eval('company', -1)),
|
||||
('deferral', '=', True),
|
||||
])
|
||||
|
||||
@fields.depends('fiscalyear')
|
||||
def on_change_with_company(self, name=None):
|
||||
return self.fiscalyear.company if self.fiscalyear else None
|
||||
|
||||
|
||||
class BalanceNonDeferral(Wizard):
|
||||
__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_form')
|
||||
|
||||
def get_move_line(self, account):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.move.line')
|
||||
# Don't use account.balance because we need the non-commulated balance
|
||||
balance = account.debit - account.credit
|
||||
if account.company.currency.is_zero(balance):
|
||||
return
|
||||
line = Line()
|
||||
line.account = account
|
||||
if balance >= 0:
|
||||
line.credit = abs(balance)
|
||||
line.debit = 0
|
||||
else:
|
||||
line.credit = 0
|
||||
line.debit = abs(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),
|
||||
('type', '!=', None),
|
||||
('closed', '!=', True),
|
||||
])
|
||||
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):
|
||||
move = self.create_move()
|
||||
if move:
|
||||
action['views'].reverse()
|
||||
return action, {'res_id': move.id if move else None}
|
||||
|
||||
|
||||
class CreatePeriodsStart(ModelView):
|
||||
__name__ = 'account.fiscalyear.create_periods.start'
|
||||
frequency = fields.Selection([
|
||||
('monthly', "Monthly"),
|
||||
('quarterly', "Quarterly"),
|
||||
('other', "Other"),
|
||||
], "Frequency", sort=False, required=True)
|
||||
interval = fields.Integer("Interval", required=True,
|
||||
states={
|
||||
'invisible': Eval('frequency') != 'other',
|
||||
},
|
||||
help="The length of each period, in months.")
|
||||
end_day = fields.Integer("End Day", required=True,
|
||||
help="The day of the month on which periods end.\n"
|
||||
"Months with fewer days will end on the last day.")
|
||||
|
||||
@classmethod
|
||||
def default_frequency(cls):
|
||||
return 'monthly'
|
||||
|
||||
@classmethod
|
||||
def default_end_day(cls):
|
||||
return 31
|
||||
|
||||
@classmethod
|
||||
def frequency_intervals(cls):
|
||||
return {
|
||||
'monthly': 1,
|
||||
'quarterly': 3,
|
||||
'other': None,
|
||||
}
|
||||
|
||||
@fields.depends('frequency', 'interval')
|
||||
def on_change_frequency(self):
|
||||
if self.frequency:
|
||||
self.interval = self.frequency_intervals()[self.frequency]
|
||||
|
||||
|
||||
class CreatePeriods(Wizard):
|
||||
__name__ = 'account.fiscalyear.create_periods'
|
||||
start = StateView('account.fiscalyear.create_periods.start',
|
||||
'account.fiscalyear_create_periods_start_view_form', [
|
||||
Button("Cancel", 'end', 'tryton-cancel'),
|
||||
Button("Create", 'create_periods', 'tryton-ok', default=True),
|
||||
])
|
||||
create_periods = StateTransition()
|
||||
|
||||
def transition_create_periods(self):
|
||||
self.model.create_period(
|
||||
[self.record], self.start.interval, self.start.end_day)
|
||||
return 'end'
|
||||
|
||||
|
||||
def month_delta(d1, d2):
|
||||
month_offset = 1 if d1.day < d2.day else 0
|
||||
return (d1.year - d2.year) * 12 + d1.month - d2.month - month_offset
|
||||
|
||||
|
||||
class RenewFiscalYearStart(ModelView):
|
||||
__name__ = 'account.fiscalyear.renew.start'
|
||||
name = fields.Char("Name", required=True)
|
||||
company = fields.Many2One('company.company', "Company", required=True)
|
||||
previous_fiscalyear = fields.Many2One(
|
||||
'account.fiscalyear', "Previous Fiscalyear", required=True,
|
||||
domain=[
|
||||
('company', '=', Eval('company', -1)),
|
||||
],
|
||||
help="Used as reference for fiscalyear configuration.")
|
||||
start_date = fields.Date("Start Date", required=True)
|
||||
end_date = fields.Date("End Date", required=True)
|
||||
reset_sequences = fields.Boolean("Reset Sequences",
|
||||
help="If checked, new sequences will be created.")
|
||||
|
||||
@classmethod
|
||||
def default_company(cls):
|
||||
return Transaction().context.get('company')
|
||||
|
||||
@classmethod
|
||||
def default_previous_fiscalyear(cls):
|
||||
pool = Pool()
|
||||
FiscalYear = pool.get('account.fiscalyear')
|
||||
fiscalyears = FiscalYear.search([
|
||||
('company', '=', cls.default_company() or -1),
|
||||
],
|
||||
order=[('end_date', 'DESC')], limit=1)
|
||||
if fiscalyears:
|
||||
fiscalyear, = fiscalyears
|
||||
return fiscalyear.id
|
||||
|
||||
@classmethod
|
||||
def default_reset_sequences(cls):
|
||||
return True
|
||||
|
||||
@fields.depends('previous_fiscalyear')
|
||||
def on_change_previous_fiscalyear(self):
|
||||
if self.previous_fiscalyear:
|
||||
fiscalyear = self.previous_fiscalyear
|
||||
months = month_delta(
|
||||
fiscalyear.end_date, fiscalyear.start_date) + 1
|
||||
self.start_date = fiscalyear.start_date + relativedelta(
|
||||
months=months, day=fiscalyear.start_date.day)
|
||||
self.end_date = fiscalyear.end_date + relativedelta(
|
||||
months=months, day=fiscalyear.end_date.day)
|
||||
self.name = fiscalyear.name.replace(
|
||||
str(fiscalyear.end_date.year),
|
||||
str(self.end_date.year)).replace(
|
||||
str(fiscalyear.start_date.year),
|
||||
str(self.start_date.year))
|
||||
|
||||
|
||||
class RenewFiscalYear(Wizard):
|
||||
__name__ = 'account.fiscalyear.renew'
|
||||
start = StateView('account.fiscalyear.renew.start',
|
||||
'account.fiscalyear_renew_start_view_form', [
|
||||
Button("Cancel", 'end', 'tryton-cancel'),
|
||||
Button("Create", 'create_', 'tryton-ok', default=True),
|
||||
])
|
||||
create_ = StateAction('account.act_fiscalyear_form')
|
||||
|
||||
def fiscalyear_defaults(self):
|
||||
pool = Pool()
|
||||
Sequence = pool.get('ir.sequence.strict')
|
||||
defaults = {
|
||||
'name': self.start.name,
|
||||
'start_date': self.start.start_date,
|
||||
'end_date': self.start.end_date,
|
||||
'periods': [],
|
||||
}
|
||||
previous_sequence = self.start.previous_fiscalyear.move_sequence
|
||||
sequence, = Sequence.copy([previous_sequence],
|
||||
default={
|
||||
'name': lambda data: data['name'].replace(
|
||||
self.start.previous_fiscalyear.name,
|
||||
self.start.name)
|
||||
})
|
||||
if self.start.reset_sequences:
|
||||
sequence.number_next = 1
|
||||
else:
|
||||
sequence.number_next = previous_sequence.number_next
|
||||
sequence.save()
|
||||
defaults['move_sequence'] = sequence.id
|
||||
return defaults
|
||||
|
||||
def create_fiscalyear(self):
|
||||
pool = Pool()
|
||||
FiscalYear = pool.get('account.fiscalyear')
|
||||
fiscalyear, = FiscalYear.copy(
|
||||
[self.start.previous_fiscalyear],
|
||||
default=self.fiscalyear_defaults())
|
||||
periods = [
|
||||
p for p in self.start.previous_fiscalyear.periods
|
||||
if p.type == 'standard']
|
||||
if periods:
|
||||
months = month_delta(fiscalyear.end_date, fiscalyear.start_date)
|
||||
months += 1
|
||||
interval = months / len(periods)
|
||||
end_day = max(
|
||||
p.end_date.day
|
||||
for p in self.start.previous_fiscalyear.periods
|
||||
if p.type == 'standard')
|
||||
if interval.is_integer():
|
||||
FiscalYear.create_period([fiscalyear], interval, end_day)
|
||||
return fiscalyear
|
||||
|
||||
def do_create_(self, action):
|
||||
fiscalyear = self.create_fiscalyear()
|
||||
fiscalyear.save()
|
||||
action['views'].reverse()
|
||||
return action, {'res_id': fiscalyear.id}
|
||||
Reference in New Issue
Block a user