diff --git a/CHANGELOG b/CHANGELOG
index 2484f15187..b89fb846e4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,5 @@
+* Add definitive lock of period and fiscal year
+
Version 4.2.0 - 2016-11-28
* Bug fixes (see mercurial logs for details)
* Add test wizard for taxes
diff --git a/__init__.py b/__init__.py
index 86b3b6b481..73d5853dd4 100644
--- a/__init__.py
+++ b/__init__.py
@@ -18,7 +18,6 @@ def register():
FiscalYear,
FiscalYearLine,
BalanceNonDeferralStart,
- CloseFiscalYearStart,
TypeTemplate,
Type,
AccountTemplate,
@@ -83,14 +82,9 @@ def register():
Pool.register(
OpenType,
BalanceNonDeferral,
- CloseFiscalYear,
OpenChartAccount,
CreateChart,
UpdateChart,
- ClosePeriod,
- ReOpenPeriod,
- CloseJournalPeriod,
- ReOpenJournalPeriod,
OpenJournal,
OpenAccount,
ReconcileLines,
diff --git a/doc/index.rst b/doc/index.rst
index 9b89769f37..602e2ff85a 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -8,15 +8,16 @@ Fiscal Year
***********
A fiscal year aggregates a set of periods that are included between
-two dates. A Fiscal year can be *Open* or *Closed*. Closing a fiscal
-year will close all the corresponding periods.
+two dates. A Fiscal year can be *Open*, *Closed* or *Locked*. Closing a fiscal
+year will close all the corresponding periods. A *Locked* fiscal can not be
+re-open.
- Name: The name of the fiscal year.
- Code: The code, useful for fast data entry and searching.
- Starting and Ending Date: The dates in which the periods should be
included.
- Company: The company for which the fiscal year is defined.
-- State: Can be *Open* or *Closed*.
+- State: Can be *Open*, *Closed* or *Locked*.
- Periods: The list of periods.
- Post Move Sequence: The sequence to use for numbering moves in this
fiscal year.
@@ -30,7 +31,7 @@ Period
******
A period is mainly defined by a Starting and an Ending date, a Fiscal
-Year, a Type and a State (*Open* or *Closed* ).
+Year, a Type and a State (*Open*, *Closed* or *Locked*).
The type can be *Standard* or *Adjustement*: Periods of type
*Standard* on the same fiscal year can not overlap. Period of type
diff --git a/fiscalyear.py b/fiscalyear.py
index 6eaa033804..4c36989797 100644
--- a/fiscalyear.py
+++ b/fiscalyear.py
@@ -1,25 +1,23 @@
# 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 trytond.model import ModelView, ModelSQL, fields
-from trytond.wizard import Wizard, StateView, StateTransition, StateAction, \
- Button
+from trytond.model import ModelView, ModelSQL, Workflow, fields
+from trytond.wizard import Wizard, StateView, StateAction, Button
from trytond.tools import datetime_strftime
from trytond.pyson import Eval, If, PYSONEncoder
from trytond.transaction import Transaction
from trytond.pool import Pool
__all__ = ['FiscalYear', 'FiscalYearLine',
- 'BalanceNonDeferralStart', 'BalanceNonDeferral',
- 'CloseFiscalYearStart', 'CloseFiscalYear']
+ 'BalanceNonDeferralStart', 'BalanceNonDeferral']
STATES = {
- 'readonly': Eval('state') == 'close',
+ 'readonly': Eval('state') != 'open',
}
DEPENDS = ['state']
-class FiscalYear(ModelSQL, ModelView):
+class FiscalYear(Workflow, ModelSQL, ModelView):
'Fiscal Year'
__name__ = 'account.fiscalyear'
name = fields.Char('Name', size=None, required=True, depends=DEPENDS)
@@ -32,9 +30,10 @@ class FiscalYear(ModelSQL, ModelView):
periods = fields.One2Many('account.period', 'fiscalyear', 'Periods',
states=STATES, depends=DEPENDS)
state = fields.Selection([
- ('open', 'Open'),
- ('close', 'Close'),
- ], 'State', readonly=True, required=True)
+ ('open', 'Open'),
+ ('close', 'Close'),
+ ('locked', 'Locked'),
+ ], 'State', readonly=True, required=True)
post_move_sequence = fields.Many2One('ir.sequence', 'Post Move Sequence',
required=True, domain=[('code', '=', 'account.move'),
['OR',
@@ -53,6 +52,7 @@ class FiscalYear(ModelSQL, ModelView):
], select=True)
close_lines = fields.Many2Many('account.fiscalyear-account.move.line',
'fiscalyear', 'line', 'Close Lines')
+ icon = fields.Function(fields.Char("Icon"), 'get_icon')
@classmethod
def __setup__(cls):
@@ -73,6 +73,11 @@ class FiscalYear(ModelSQL, ModelView):
'reopen_error': ('You can not reopen fiscal year "%s" until '
'you reopen all later fiscal years.'),
})
+ cls._transitions |= set((
+ ('open', 'close'),
+ ('close', 'locked'),
+ ('close', 'open'),
+ ))
cls._buttons.update({
'create_period': {
'invisible': ((Eval('state') != 'open')
@@ -88,6 +93,9 @@ class FiscalYear(ModelSQL, ModelView):
'reopen': {
'invisible': Eval('state') != 'close',
},
+ 'lock': {
+ 'invisible': Eval('state') != 'close',
+ },
})
@staticmethod
@@ -98,6 +106,13 @@ class FiscalYear(ModelSQL, ModelView):
def default_company():
return Transaction().context.get('company')
+ def get_icon(self, name):
+ return {
+ 'open': 'tryton-open',
+ 'close': 'tryton-close',
+ 'locked': 'tryton-readonly',
+ }.get(self.state)
+
@classmethod
def validate(cls, years):
super(FiscalYear, cls).validate(years)
@@ -258,6 +273,7 @@ class FiscalYear(ModelSQL, ModelView):
@classmethod
@ModelView.button
+ @Workflow.transition('close')
def close(cls, fiscalyears):
'''
Close a fiscal year
@@ -266,6 +282,12 @@ class FiscalYear(ModelSQL, ModelView):
Period = pool.get('account.period')
Account = pool.get('account.account')
Deferral = pool.get('account.account.deferral')
+ transaction = Transaction()
+ database = transaction.database
+ connection = transaction.connection
+
+ # Lock period to be sure no new period will be created in between.
+ database.lock(connection, Period._table)
deferrals = []
for fiscalyear in fiscalyears:
@@ -276,11 +298,6 @@ class FiscalYear(ModelSQL, ModelView):
]):
cls.raise_user_error('close_error', (fiscalyear.rec_name,))
- # First close the fiscalyear to be sure
- # it will not have new period created between.
- cls.write([fiscalyear], {
- 'state': 'close',
- })
periods = Period.search([
('fiscalyear', '=', fiscalyear.id),
])
@@ -297,6 +314,7 @@ class FiscalYear(ModelSQL, ModelView):
@classmethod
@ModelView.button
+ @Workflow.transition('open')
def reopen(cls, fiscalyears):
'''
Re-open a fiscal year
@@ -306,7 +324,7 @@ class FiscalYear(ModelSQL, ModelView):
for fiscalyear in fiscalyears:
if cls.search([
('start_date', '>=', fiscalyear.end_date),
- ('state', '=', 'close'),
+ ('state', '!=', 'open'),
('company', '=', fiscalyear.company.id),
]):
cls.raise_user_error('reopen_error')
@@ -316,9 +334,16 @@ class FiscalYear(ModelSQL, ModelView):
])
Deferral.delete(deferrals)
- cls.write([fiscalyear], {
- 'state': 'open',
- })
+ @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 FiscalYearLine(ModelSQL):
@@ -453,27 +478,3 @@ class BalanceNonDeferral(Wizard):
('origin', '=', str(self.start.fiscalyear)),
])
return action, {}
-
-
-class CloseFiscalYearStart(ModelView):
- 'Close Fiscal Year'
- __name__ = 'account.fiscalyear.close.start'
- close_fiscalyear = fields.Many2One('account.fiscalyear',
- 'Fiscal Year to close', required=True,
- domain=[('state', '!=', 'close')])
-
-
-class CloseFiscalYear(Wizard):
- 'Close Fiscal Year'
- __name__ = 'account.fiscalyear.close'
- 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()
-
- def transition_close(self):
- Fiscalyear = Pool().get('account.fiscalyear')
- Fiscalyear.close([self.start.close_fiscalyear])
- return 'end'
diff --git a/fiscalyear.xml b/fiscalyear.xml
index bba87fdf7a..aa7e305f37 100644
--- a/fiscalyear.xml
+++ b/fiscalyear.xml
@@ -15,6 +15,12 @@ this repository contains the full copyright notices and license terms. -->
tree
fiscalyear_tree
+
+ account.fiscalyear
+ tree
+ fiscalyear_list_close
+
+
Fiscal Years
account.fiscalyear
@@ -59,6 +65,22 @@ this repository contains the full copyright notices and license terms. -->
+
+ Close Fiscal Years
+ account.fiscalyear
+
+
+
+
+
+
+
+
+
+
-
-
- account.fiscalyear.close.start
- form
- fiscalyear_close_start_form
-
-
-
- Close Fiscal Year
- account.fiscalyear.close
-
-
diff --git a/journal.py b/journal.py
index 3f215639a8..82cc639012 100644
--- a/journal.py
+++ b/journal.py
@@ -5,8 +5,7 @@ from decimal import Decimal
from sql import Null
from sql.aggregate import Sum
-from trytond.model import ModelView, ModelSQL, fields, Unique
-from trytond.wizard import Wizard, StateTransition
+from trytond.model import ModelView, ModelSQL, Workflow, fields, Unique
from trytond import backend
from trytond.pyson import Eval, Bool
from trytond.transaction import Transaction
@@ -18,18 +17,13 @@ from trytond.modules.company.model import (
__all__ = ['JournalType', 'Journal', 'JournalSequence', 'JournalAccount',
'JournalCashContext',
- 'JournalPeriod', 'CloseJournalPeriod', 'ReOpenJournalPeriod']
+ 'JournalPeriod']
STATES = {
'readonly': Eval('state') == 'close',
}
DEPENDS = ['state']
-_ICONS = {
- 'open': 'tryton-open',
- 'close': 'tryton-readonly',
-}
-
class JournalType(ModelSQL, ModelView):
'Journal Type'
@@ -312,7 +306,7 @@ class JournalCashContext(ModelView):
default_end_date = default_start_date
-class JournalPeriod(ModelSQL, ModelView):
+class JournalPeriod(Workflow, ModelSQL, ModelView):
'Journal - Period'
__name__ = 'account.journal.period'
journal = fields.Many2One('account.journal', 'Journal', required=True,
@@ -345,6 +339,18 @@ class JournalPeriod(ModelSQL, ModelView):
'journal - period "%(journal_period)s" because period '
'"%(period)s" is closed.'),
})
+ cls._transitions |= set((
+ ('open', 'close'),
+ ('close', 'open'),
+ ))
+ cls._buttons.update({
+ 'close': {
+ 'invisible': Eval('state') != 'open',
+ },
+ 'reopen': {
+ 'invisible': Eval('state') != 'close',
+ },
+ })
@classmethod
def __register__(cls, module_name):
@@ -379,7 +385,10 @@ class JournalPeriod(ModelSQL, ModelView):
]
def get_icon(self, name):
- return _ICONS.get(self.state, '')
+ return {
+ 'open': 'tryton-open',
+ 'close': 'tryton-close',
+ }.get(self.state)
@classmethod
def _check(cls, periods):
@@ -399,7 +408,7 @@ class JournalPeriod(ModelSQL, ModelView):
for vals in vlist:
if vals.get('period'):
period = Period(vals['period'])
- if period.state == 'close':
+ if period.state != 'open':
cls.raise_user_error('create_journal_period', (
period.rec_name,))
return super(JournalPeriod, cls).create(vlist)
@@ -413,7 +422,7 @@ class JournalPeriod(ModelSQL, ModelView):
cls._check(journal_periods)
if values.get('state') == 'open':
for journal_period in journal_periods:
- if journal_period.period.state == 'close':
+ if journal_period.period.state != 'open':
cls.raise_user_error('open_journal_period', {
'journal_period': journal_period.rec_name,
'period': journal_period.period.rec_name,
@@ -426,43 +435,17 @@ class JournalPeriod(ModelSQL, ModelView):
super(JournalPeriod, cls).delete(periods)
@classmethod
+ @ModelView.button
+ @Workflow.transition('close')
def close(cls, periods):
'''
Close journal - period
'''
- cls.write(periods, {
- 'state': 'close',
- })
+ pass
@classmethod
- def open_(cls, periods):
+ @ModelView.button
+ @Workflow.transition('open')
+ def reopen(cls, periods):
"Open journal - period"
- cls.write(periods, {
- 'state': 'open',
- })
-
-
-class CloseJournalPeriod(Wizard):
- 'Close Journal - Period'
- __name__ = 'account.journal.period.close'
- start_state = 'close'
- close = StateTransition()
-
- def transition_close(self):
- JournalPeriod = Pool().get('account.journal.period')
- JournalPeriod.close(
- JournalPeriod.browse(Transaction().context['active_ids']))
- return 'end'
-
-
-class ReOpenJournalPeriod(Wizard):
- 'Re-Open Journal - Period'
- __name__ = 'account.journal.period.reopen'
- start_state = 'reopen'
- reopen = StateTransition()
-
- def transition_reopen(self):
- JournalPeriod = Pool().get('account.journal.period')
- JournalPeriod.open_(
- JournalPeriod.browse(Transaction().context['active_ids']))
- return 'end'
+ pass
diff --git a/journal.xml b/journal.xml
index 47b25dcdff..bb5d2a7650 100644
--- a/journal.xml
+++ b/journal.xml
@@ -172,6 +172,12 @@ this repository contains the full copyright notices and license terms. -->
tree
journal_period_tree
+
+ account.journal.period
+ tree
+ journal_period_list_close
+
+
Journals - Periods
account.journal.period
@@ -186,11 +192,12 @@ this repository contains the full copyright notices and license terms. -->
+
Journals - Periods
account.journal.period
@@ -201,47 +208,20 @@ this repository contains the full copyright notices and license terms. -->
-
+
Close Journals - Periods
- account.journal.period.close
- account.journal.period
-
-
- form_action
- account.journal.period,-1
-
-
-
-
- Re-Open Journals - Periods
- account.journal.period.reopen
- account.journal.period
-
-
- form_action
- account.journal.period,-1
-
-
-
-
- Journals - Periods
account.journal.period
-
+
-
+
-
-
+
+
-
-
-
-
-
-
+
+
diff --git a/period.py b/period.py
index 918a473bf0..821c4f79a3 100644
--- a/period.py
+++ b/period.py
@@ -1,22 +1,21 @@
# 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 trytond.model import ModelView, ModelSQL, fields
-from trytond.wizard import Wizard, StateTransition
+from trytond.model import ModelView, ModelSQL, Workflow, fields
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.const import OPERATORS
from trytond import backend
-__all__ = ['Period', 'ClosePeriod', 'ReOpenPeriod']
+__all__ = ['Period']
_STATES = {
- 'readonly': Eval('state') == 'close',
+ 'readonly': Eval('state') != 'open',
}
_DEPENDS = ['state']
-class Period(ModelSQL, ModelView):
+class Period(Workflow, ModelSQL, ModelView):
'Period'
__name__ = 'account.period'
name = fields.Char('Name', required=True)
@@ -29,9 +28,10 @@ class Period(ModelSQL, ModelView):
fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
required=True, states=_STATES, depends=_DEPENDS, select=True)
state = fields.Selection([
- ('open', 'Open'),
- ('close', 'Close'),
- ], 'State', readonly=True, required=True)
+ ('open', 'Open'),
+ ('close', 'Close'),
+ ('locked', 'Locked'),
+ ], 'State', readonly=True, required=True)
post_move_sequence = fields.Many2One('ir.sequence', 'Post Move Sequence',
domain=[
('code', '=', 'account.move'),
@@ -48,6 +48,7 @@ class Period(ModelSQL, ModelView):
states=_STATES, depends=_DEPENDS, select=True)
company = fields.Function(fields.Many2One('company.company', 'Company',),
'on_change_with_company', searcher='search_company')
+ icon = fields.Function(fields.Char("Icon"), 'get_icon')
@classmethod
def __register__(cls, module_name):
@@ -85,6 +86,22 @@ class Period(ModelSQL, ModelView):
'fiscalyear_dates': ('Dates of period "%s" are outside '
'are outside it\'s fiscal year dates.'),
})
+ cls._transitions |= set((
+ ('open', 'close'),
+ ('close', 'locked'),
+ ('close', 'open'),
+ ))
+ cls._buttons.update({
+ 'close': {
+ 'invisible': Eval('state') != 'open',
+ },
+ 'reopen': {
+ 'invisible': Eval('state') != 'close',
+ },
+ 'lock': {
+ 'invisible': Eval('state') != 'close',
+ },
+ })
@staticmethod
def default_state():
@@ -103,6 +120,13 @@ class Period(ModelSQL, ModelView):
def search_company(cls, name, clause):
return [('fiscalyear.%s' % name,) + tuple(clause[1:])]
+ def get_icon(self, name):
+ return {
+ 'open': 'tryton-open',
+ 'close': 'tryton-close',
+ 'locked': 'tryton-readonly',
+ }.get(self.state)
+
@classmethod
def validate(cls, periods):
super(Period, cls).validate(periods)
@@ -177,7 +201,7 @@ class Period(ModelSQL, ModelView):
('type', '=', 'standard'),
]
if test_state:
- clause.append(('state', '!=', 'close'))
+ clause.append(('state', '=', 'open'))
periods = cls.search(clause, order=[('start_date', 'DESC')], limit=1)
if not periods:
if exception:
@@ -237,7 +261,7 @@ class Period(ModelSQL, ModelView):
for vals in vlist:
if vals.get('fiscalyear'):
fiscalyear = FiscalYear(vals['fiscalyear'])
- if fiscalyear.state == 'close':
+ if fiscalyear.state != 'open':
cls.raise_user_error('create_period_closed_fiscalyear',
(fiscalyear.rec_name,))
if not vals.get('post_move_sequence'):
@@ -262,7 +286,7 @@ class Period(ModelSQL, ModelView):
break
if values.get('state') == 'open':
for period in periods:
- if period.fiscalyear.state == 'close':
+ if period.fiscalyear.state != 'open':
cls.raise_user_error('open_period_closed_fiscalyear', {
'period': period.rec_name,
'fiscalyear': period.fiscalyear.rec_name,
@@ -287,10 +311,18 @@ class Period(ModelSQL, ModelView):
super(Period, cls).delete(periods)
@classmethod
+ @ModelView.button
+ @Workflow.transition('close')
def close(cls, periods):
pool = Pool()
JournalPeriod = pool.get('account.journal.period')
Move = pool.get('account.move')
+ transaction = Transaction()
+ database = transaction.database
+ connection = transaction.connection
+
+ # Lock period to be sure no new period will be created in between.
+ database.lock(connection, JournalPeriod._table)
unposted_moves = Move.search([
('period', 'in', [p.id for p in periods]),
@@ -302,47 +334,24 @@ class Period(ModelSQL, ModelView):
'period': unposted_move.period.rec_name,
'move': unposted_move.rec_name,
})
- # First close the period to be sure
- # it will not have new journal.period created between.
- cls.write(periods, {
- 'state': 'close',
- })
journal_periods = JournalPeriod.search([
('period', 'in', [p.id for p in periods]),
])
JournalPeriod.close(journal_periods)
@classmethod
- def open_(cls, periods):
- "Open Journal"
- cls.write(periods, {
- 'state': 'open',
- })
+ @ModelView.button
+ @Workflow.transition('open')
+ def reopen(cls, periods):
+ "Re-open period"
+ pass
+
+ @classmethod
+ @ModelView.button
+ @Workflow.transition('locked')
+ def lock(cls, periods):
+ pass
@property
def post_move_sequence_used(self):
return self.post_move_sequence or self.fiscalyear.post_move_sequence
-
-
-class ClosePeriod(Wizard):
- 'Close Period'
- __name__ = 'account.period.close'
- start_state = 'close'
- close = StateTransition()
-
- def transition_close(self):
- Period = Pool().get('account.period')
- Period.close(Period.browse(Transaction().context['active_ids']))
- return 'end'
-
-
-class ReOpenPeriod(Wizard):
- 'Re-Open Period'
- __name__ = 'account.period.reopen'
- start_state = 'reopen'
- reopen = StateTransition()
-
- def transition_reopen(self):
- Period = Pool().get('account.period')
- Period.open_(Period.browse(Transaction().context['active_ids']))
- return 'end'
diff --git a/period.xml b/period.xml
index 622837ce88..52bcc98d03 100644
--- a/period.xml
+++ b/period.xml
@@ -13,6 +13,12 @@ this repository contains the full copyright notices and license terms. -->
tree
period_tree
+
+ account.period
+ tree
+ period_list_close
+
+
Periods
account.period
@@ -50,45 +56,21 @@ this repository contains the full copyright notices and license terms. -->
-
+
Close Periods
- account.period.close
- account.period
-
-
- form_action
- account.period,-1
-
-
-
- Re-Open Periods
- account.period.reopen
- account.period
-
-
- form_action
- account.period,-1
-
-
-
- Periods
account.period
-
+
-
+
-
-
+
+
-
-
-
-
-
-
+
+
diff --git a/view/fiscalyear_close_start_form.xml b/view/fiscalyear_close_start_form.xml
deleted file mode 100644
index 34b2f96553..0000000000
--- a/view/fiscalyear_close_start_form.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/view/fiscalyear_form.xml b/view/fiscalyear_form.xml
index 99a19a9421..841e03c077 100644
--- a/view/fiscalyear_form.xml
+++ b/view/fiscalyear_form.xml
@@ -27,12 +27,4 @@ this repository contains the full copyright notices and license terms. -->
-
-
-
-
-
diff --git a/view/fiscalyear_list_close.xml b/view/fiscalyear_list_close.xml
new file mode 100644
index 0000000000..c990d9419f
--- /dev/null
+++ b/view/fiscalyear_list_close.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/view/journal_period_list_close.xml b/view/journal_period_list_close.xml
new file mode 100644
index 0000000000..9ad31cdea5
--- /dev/null
+++ b/view/journal_period_list_close.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/view/journal_period_tree.xml b/view/journal_period_tree.xml
index 2b40847a70..f15c2203d7 100644
--- a/view/journal_period_tree.xml
+++ b/view/journal_period_tree.xml
@@ -2,7 +2,6 @@
-
diff --git a/view/period_list_close.xml b/view/period_list_close.xml
new file mode 100644
index 0000000000..251f6b606c
--- /dev/null
+++ b/view/period_list_close.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+