2014-12-02 12:54:29 +01:00
|
|
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
|
|
|
# this repository contains the full copyright notices and license terms.
|
2015-09-01 23:57:00 +02:00
|
|
|
from decimal import Decimal
|
|
|
|
|
|
|
|
|
|
from sql.aggregate import Sum
|
2014-11-24 13:58:35 +01:00
|
|
|
|
2018-12-29 14:20:28 +01:00
|
|
|
from trytond.i18n import gettext
|
2018-03-26 14:08:06 +02:00
|
|
|
from trytond.model import (
|
2022-12-26 15:08:29 +01:00
|
|
|
DeactivableMixin, MatchMixin, ModelSQL, ModelView, Unique, Workflow,
|
|
|
|
|
fields, sequence_ordered)
|
2018-12-29 14:20:28 +01:00
|
|
|
from trytond.model.exceptions import AccessError
|
2017-04-01 00:25:49 +02:00
|
|
|
from trytond.modules.company.model import (
|
|
|
|
|
CompanyMultiValueMixin, CompanyValueMixin)
|
2021-08-21 09:09:55 +02:00
|
|
|
from trytond.modules.currency.fields import Monetary
|
2021-12-11 17:58:41 +01:00
|
|
|
from trytond.pool import Pool
|
|
|
|
|
from trytond.pyson import Bool, Eval, Id
|
2022-09-16 23:37:38 +02:00
|
|
|
from trytond.tools import (
|
|
|
|
|
grouped_slice, is_full_text, lstrip_wildcard, reduce_ids)
|
2021-12-11 17:58:41 +01:00
|
|
|
from trytond.transaction import Transaction
|
2008-03-26 13:33:32 +01:00
|
|
|
|
2008-03-17 18:32:06 +01:00
|
|
|
STATES = {
|
2023-06-20 10:13:56 +02:00
|
|
|
'readonly': Eval('state') == 'closed',
|
2008-03-17 18:32:06 +01:00
|
|
|
}
|
2009-01-10 22:33:39 +01:00
|
|
|
|
2008-03-13 23:22:59 +01:00
|
|
|
|
2018-03-26 14:08:06 +02:00
|
|
|
class Journal(
|
2022-12-26 15:08:29 +01:00
|
|
|
DeactivableMixin, MatchMixin,
|
|
|
|
|
sequence_ordered('matching_sequence', "Matching Sequence"),
|
|
|
|
|
ModelSQL, ModelView, CompanyMultiValueMixin):
|
2008-03-13 23:22:59 +01:00
|
|
|
'Journal'
|
2012-09-24 22:17:29 +02:00
|
|
|
__name__ = 'account.journal'
|
2008-03-13 23:22:59 +01:00
|
|
|
name = fields.Char('Name', size=None, required=True, translate=True)
|
|
|
|
|
code = fields.Char('Code', size=None)
|
2018-05-30 14:12:08 +02:00
|
|
|
type = fields.Selection([
|
|
|
|
|
('general', "General"),
|
|
|
|
|
('revenue', "Revenue"),
|
|
|
|
|
('expense', "Expense"),
|
|
|
|
|
('cash', "Cash"),
|
|
|
|
|
('situation', "Situation"),
|
|
|
|
|
('write-off', "Write-Off"),
|
|
|
|
|
], 'Type', required=True)
|
2017-04-01 00:25:49 +02:00
|
|
|
sequence = fields.MultiValue(fields.Many2One(
|
|
|
|
|
'ir.sequence', "Sequence",
|
|
|
|
|
domain=[
|
2021-03-11 23:00:09 +01:00
|
|
|
('sequence_type', '=',
|
|
|
|
|
Id('account', 'sequence_type_account_journal')),
|
2017-04-01 00:25:49 +02:00
|
|
|
('company', 'in', [
|
|
|
|
|
Eval('context', {}).get('company', -1), None]),
|
|
|
|
|
],
|
2011-08-21 11:20:29 +02:00
|
|
|
states={
|
2013-10-09 18:25:04 +02:00
|
|
|
'required': Bool(Eval('context', {}).get('company', -1)),
|
2011-08-21 11:20:29 +02:00
|
|
|
}))
|
2017-04-01 00:25:49 +02:00
|
|
|
sequences = fields.One2Many(
|
|
|
|
|
'account.journal.sequence', 'journal', "Sequences")
|
2021-08-21 09:09:55 +02:00
|
|
|
debit = fields.Function(Monetary(
|
|
|
|
|
"Debit", currency='currency', digits='currency'),
|
|
|
|
|
'get_debit_credit_balance')
|
|
|
|
|
credit = fields.Function(Monetary(
|
|
|
|
|
"Credit", currency='currency', digits='currency'),
|
|
|
|
|
'get_debit_credit_balance')
|
|
|
|
|
balance = fields.Function(Monetary(
|
|
|
|
|
"Balance", currency='currency', digits='currency'),
|
|
|
|
|
'get_debit_credit_balance')
|
|
|
|
|
|
|
|
|
|
currency = fields.Function(fields.Many2One(
|
|
|
|
|
'currency.currency', "Currency"), 'get_currency')
|
2008-03-13 23:22:59 +01:00
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@classmethod
|
|
|
|
|
def __setup__(cls):
|
|
|
|
|
super(Journal, cls).__setup__()
|
|
|
|
|
cls._order.insert(0, ('name', 'ASC'))
|
2008-04-22 17:47:52 +02:00
|
|
|
|
2017-07-25 09:54:24 +02:00
|
|
|
@classmethod
|
|
|
|
|
def default_sequence(cls, **pattern):
|
2022-02-03 22:34:01 +01:00
|
|
|
return cls.multivalue_model('sequence').default_sequence()
|
2008-03-13 23:22:59 +01:00
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@classmethod
|
|
|
|
|
def search_rec_name(cls, name, clause):
|
2022-09-16 23:37:38 +02:00
|
|
|
_, operator, operand, *extra = clause
|
|
|
|
|
if operator.startswith('!') or operator.startswith('not '):
|
2015-04-05 23:39:57 +02:00
|
|
|
bool_op = 'AND'
|
|
|
|
|
else:
|
|
|
|
|
bool_op = 'OR'
|
2022-09-16 23:37:38 +02:00
|
|
|
code_value = operand
|
|
|
|
|
if operator.endswith('like') and is_full_text(operand):
|
|
|
|
|
code_value = lstrip_wildcard(operand)
|
2015-04-05 23:39:57 +02:00
|
|
|
return [bool_op,
|
2022-09-16 23:37:38 +02:00
|
|
|
('code', operator, code_value, *extra),
|
|
|
|
|
(cls._rec_name, operator, operand, *extra),
|
2014-01-31 14:06:56 +01:00
|
|
|
]
|
2008-03-17 18:32:06 +01:00
|
|
|
|
2015-09-01 23:57:00 +02:00
|
|
|
@classmethod
|
2021-08-21 09:09:55 +02:00
|
|
|
def get_currency(cls, journals, name):
|
2015-09-01 23:57:00 +02:00
|
|
|
pool = Pool()
|
|
|
|
|
Company = pool.get('company.company')
|
|
|
|
|
company_id = Transaction().context.get('company')
|
|
|
|
|
if company_id:
|
|
|
|
|
company = Company(company_id)
|
2021-08-21 09:09:55 +02:00
|
|
|
currency_id = company.currency.id
|
2015-09-01 23:57:00 +02:00
|
|
|
else:
|
2021-08-21 09:09:55 +02:00
|
|
|
currency_id = None
|
|
|
|
|
return dict.fromkeys([j.id for j in journals], currency_id)
|
2015-09-01 23:57:00 +02:00
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_debit_credit_balance(cls, journals, names):
|
|
|
|
|
pool = Pool()
|
|
|
|
|
MoveLine = pool.get('account.move.line')
|
|
|
|
|
Move = pool.get('account.move')
|
2018-11-07 19:01:14 +01:00
|
|
|
Account = pool.get('account.account')
|
2019-03-24 18:00:01 +01:00
|
|
|
AccountType = pool.get('account.account.type')
|
2018-11-07 19:01:14 +01:00
|
|
|
Company = pool.get('company.company')
|
2015-09-01 23:57:00 +02:00
|
|
|
context = Transaction().context
|
2016-03-03 21:53:17 +01:00
|
|
|
cursor = Transaction().connection.cursor()
|
2015-09-01 23:57:00 +02:00
|
|
|
|
|
|
|
|
result = {}
|
|
|
|
|
ids = [j.id for j in journals]
|
|
|
|
|
for name in ['debit', 'credit', 'balance']:
|
|
|
|
|
result[name] = dict.fromkeys(ids, 0)
|
|
|
|
|
|
2018-11-07 19:01:14 +01:00
|
|
|
company_id = Transaction().context.get('company')
|
|
|
|
|
if not company_id:
|
|
|
|
|
return result
|
|
|
|
|
company = Company(company_id)
|
|
|
|
|
|
2015-09-01 23:57:00 +02:00
|
|
|
line = MoveLine.__table__()
|
|
|
|
|
move = Move.__table__()
|
2018-11-07 19:01:14 +01:00
|
|
|
account = Account.__table__()
|
2019-03-24 18:00:01 +01:00
|
|
|
account_type = AccountType.__table__()
|
2018-11-07 19:01:14 +01:00
|
|
|
where = ((move.date >= context.get('start_date'))
|
|
|
|
|
& (move.date <= context.get('end_date'))
|
2019-03-24 18:00:01 +01:00
|
|
|
& ~account_type.receivable
|
|
|
|
|
& ~account_type.payable
|
2018-11-07 19:01:14 +01:00
|
|
|
& (move.company == company.id))
|
2015-09-01 23:57:00 +02:00
|
|
|
for sub_journals in grouped_slice(journals):
|
|
|
|
|
sub_journals = list(sub_journals)
|
|
|
|
|
red_sql = reduce_ids(move.journal, [j.id for j in sub_journals])
|
|
|
|
|
query = line.join(move, 'LEFT', condition=line.move == move.id
|
2018-11-07 19:01:14 +01:00
|
|
|
).join(account, 'LEFT', condition=line.account == account.id
|
2019-03-24 18:00:01 +01:00
|
|
|
).join(account_type, 'LEFT',
|
|
|
|
|
condition=account.type == account_type.id
|
2015-09-01 23:57:00 +02:00
|
|
|
).select(move.journal, Sum(line.debit), Sum(line.credit),
|
2018-11-07 19:01:14 +01:00
|
|
|
where=where & red_sql,
|
2015-09-01 23:57:00 +02:00
|
|
|
group_by=move.journal)
|
|
|
|
|
cursor.execute(*query)
|
2021-03-21 16:09:12 +01:00
|
|
|
for journal_id, debit, credit in cursor:
|
2015-09-01 23:57:00 +02:00
|
|
|
# SQLite uses float for SUM
|
|
|
|
|
if not isinstance(debit, Decimal):
|
|
|
|
|
debit = Decimal(str(debit))
|
|
|
|
|
if not isinstance(credit, Decimal):
|
|
|
|
|
credit = Decimal(str(credit))
|
2018-11-07 19:01:14 +01:00
|
|
|
result['debit'][journal_id] = company.currency.round(debit)
|
|
|
|
|
result['credit'][journal_id] = company.currency.round(credit)
|
|
|
|
|
result['balance'][journal_id] = company.currency.round(
|
|
|
|
|
debit - credit)
|
2015-09-01 23:57:00 +02:00
|
|
|
return result
|
|
|
|
|
|
2022-04-11 20:19:52 +02:00
|
|
|
@classmethod
|
2022-12-26 15:08:29 +01:00
|
|
|
def find(cls, pattern):
|
2024-02-12 23:22:23 +01:00
|
|
|
for journal in cls.search(
|
|
|
|
|
[],
|
|
|
|
|
order=[
|
|
|
|
|
('matching_sequence', 'ASC'),
|
|
|
|
|
('id', 'ASC'),
|
|
|
|
|
]):
|
2022-12-26 15:08:29 +01:00
|
|
|
if journal.match(pattern):
|
|
|
|
|
return journal
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2022-04-11 20:19:52 +02:00
|
|
|
def write(cls, *args):
|
|
|
|
|
pool = Pool()
|
|
|
|
|
Move = pool.get('account.move')
|
|
|
|
|
actions = iter(args)
|
|
|
|
|
for journals, values in zip(actions, actions):
|
|
|
|
|
if 'type' in values:
|
|
|
|
|
for sub_journals in grouped_slice(journals):
|
|
|
|
|
moves = Move.search([
|
|
|
|
|
('journal', 'in', [j.id for j in sub_journals]),
|
|
|
|
|
('state', '=', 'posted')
|
|
|
|
|
], order=[], limit=1)
|
|
|
|
|
if moves:
|
|
|
|
|
move, = moves
|
|
|
|
|
raise AccessError(gettext(
|
|
|
|
|
'account.msg_journal_account_moves',
|
|
|
|
|
journal=move.journal.rec_name))
|
|
|
|
|
super().write(*args)
|
|
|
|
|
|
2015-09-01 23:57:00 +02:00
|
|
|
|
2017-04-01 00:25:49 +02:00
|
|
|
class JournalSequence(ModelSQL, CompanyValueMixin):
|
|
|
|
|
"Journal Sequence"
|
|
|
|
|
__name__ = 'account.journal.sequence'
|
|
|
|
|
journal = fields.Many2One(
|
2022-10-11 00:44:48 +02:00
|
|
|
'account.journal', "Journal", ondelete='CASCADE',
|
2021-03-05 15:49:19 +01:00
|
|
|
context={
|
|
|
|
|
'company': Eval('company', -1),
|
|
|
|
|
},
|
2022-04-08 19:07:12 +02:00
|
|
|
depends={'company'})
|
2017-04-01 00:25:49 +02:00
|
|
|
sequence = fields.Many2One(
|
|
|
|
|
'ir.sequence', "Sequence",
|
|
|
|
|
domain=[
|
2021-03-11 23:00:09 +01:00
|
|
|
('sequence_type', '=',
|
|
|
|
|
Id('account', 'sequence_type_account_journal')),
|
2017-04-01 00:25:49 +02:00
|
|
|
('company', 'in', [Eval('company', -1), None]),
|
|
|
|
|
],
|
2022-04-08 19:07:12 +02:00
|
|
|
depends={'company'})
|
2017-04-01 00:25:49 +02:00
|
|
|
|
2022-02-03 22:34:01 +01:00
|
|
|
@classmethod
|
|
|
|
|
def default_sequence(cls):
|
|
|
|
|
pool = Pool()
|
|
|
|
|
ModelData = pool.get('ir.model.data')
|
|
|
|
|
try:
|
|
|
|
|
return ModelData.get_id('account', 'sequence_account_journal')
|
|
|
|
|
except KeyError:
|
|
|
|
|
return None
|
|
|
|
|
|
2017-04-01 00:25:49 +02:00
|
|
|
|
2016-05-20 15:34:29 +02:00
|
|
|
class JournalCashContext(ModelView):
|
|
|
|
|
'Journal Cash Context'
|
|
|
|
|
__name__ = 'account.journal.open_cash.context'
|
2015-09-01 23:57:00 +02:00
|
|
|
start_date = fields.Date('Start Date', required=True)
|
|
|
|
|
end_date = fields.Date('End Date', required=True)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def default_start_date(cls):
|
|
|
|
|
return Pool().get('ir.date').today()
|
|
|
|
|
default_end_date = default_start_date
|
|
|
|
|
|
2008-03-17 18:32:06 +01:00
|
|
|
|
2018-03-26 14:08:06 +02:00
|
|
|
class JournalPeriod(
|
|
|
|
|
DeactivableMixin, Workflow, ModelSQL, ModelView):
|
2008-03-17 18:32:06 +01:00
|
|
|
'Journal - Period'
|
2012-09-24 22:17:29 +02:00
|
|
|
__name__ = 'account.journal.period'
|
2008-03-17 18:32:06 +01:00
|
|
|
journal = fields.Many2One('account.journal', 'Journal', required=True,
|
2022-04-08 19:07:12 +02:00
|
|
|
ondelete='CASCADE', states=STATES)
|
2008-03-17 18:32:06 +01:00
|
|
|
period = fields.Many2One('account.period', 'Period', required=True,
|
2022-04-08 19:07:12 +02:00
|
|
|
ondelete='CASCADE', states=STATES)
|
2010-03-11 16:45:27 +01:00
|
|
|
icon = fields.Function(fields.Char('Icon'), 'get_icon')
|
2008-03-17 18:32:06 +01:00
|
|
|
state = fields.Selection([
|
|
|
|
|
('open', 'Open'),
|
2023-06-20 10:13:56 +02:00
|
|
|
('closed', 'Closed'),
|
2022-02-15 00:08:46 +01:00
|
|
|
], 'State', readonly=True, required=True, sort=False)
|
2008-03-17 18:32:06 +01:00
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@classmethod
|
|
|
|
|
def __setup__(cls):
|
|
|
|
|
super(JournalPeriod, cls).__setup__()
|
2015-06-22 16:58:24 +02:00
|
|
|
t = cls.__table__()
|
2012-09-24 22:17:29 +02:00
|
|
|
cls._sql_constraints += [
|
2015-06-22 16:58:24 +02:00
|
|
|
('journal_period_uniq', Unique(t, t.journal, t.period),
|
2018-12-29 14:20:28 +01:00
|
|
|
'account.msg_journal_period_unique'),
|
2012-09-24 22:17:29 +02:00
|
|
|
]
|
2017-04-02 12:46:28 +02:00
|
|
|
cls._transitions |= set((
|
2023-06-20 10:13:56 +02:00
|
|
|
('open', 'closed'),
|
|
|
|
|
('closed', 'open'),
|
2017-04-02 12:46:28 +02:00
|
|
|
))
|
|
|
|
|
cls._buttons.update({
|
|
|
|
|
'close': {
|
|
|
|
|
'invisible': Eval('state') != 'open',
|
2018-02-19 11:36:13 +01:00
|
|
|
'depends': ['state'],
|
2017-04-02 12:46:28 +02:00
|
|
|
},
|
|
|
|
|
'reopen': {
|
2023-06-20 10:13:56 +02:00
|
|
|
'invisible': Eval('state') != 'closed',
|
2018-02-19 11:36:13 +01:00
|
|
|
'depends': ['state'],
|
2017-04-02 12:46:28 +02:00
|
|
|
},
|
|
|
|
|
})
|
2018-03-26 14:08:06 +02:00
|
|
|
cls.active.states = STATES
|
2008-03-17 18:32:06 +01:00
|
|
|
|
2023-06-20 10:13:56 +02:00
|
|
|
@classmethod
|
|
|
|
|
def __register__(cls, module):
|
|
|
|
|
cursor = Transaction().connection.cursor()
|
|
|
|
|
t = cls.__table__()
|
|
|
|
|
super().__register__(module)
|
|
|
|
|
# Migration from 6.8: rename state close to closed
|
|
|
|
|
cursor.execute(
|
|
|
|
|
*t.update([t.state], ['closed'], where=t.state == 'close'))
|
|
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@staticmethod
|
|
|
|
|
def default_state():
|
2008-03-17 18:32:06 +01:00
|
|
|
return 'open'
|
|
|
|
|
|
2016-07-07 10:36:37 +02:00
|
|
|
def get_rec_name(self, name):
|
|
|
|
|
return '%s - %s' % (self.journal.rec_name, self.period.rec_name)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def search_rec_name(cls, name, clause):
|
|
|
|
|
if clause[1].startswith('!') or clause[1].startswith('not '):
|
|
|
|
|
bool_op = 'AND'
|
|
|
|
|
else:
|
|
|
|
|
bool_op = 'OR'
|
|
|
|
|
return [bool_op,
|
|
|
|
|
[('journal.rec_name',) + tuple(clause[1:])],
|
|
|
|
|
[('period.rec_name',) + tuple(clause[1:])],
|
|
|
|
|
]
|
|
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
def get_icon(self, name):
|
2017-04-02 12:46:28 +02:00
|
|
|
return {
|
2019-03-28 19:40:28 +01:00
|
|
|
'open': 'tryton-account-open',
|
2023-06-20 10:13:56 +02:00
|
|
|
'closed': 'tryton-account-close',
|
2017-04-02 12:46:28 +02:00
|
|
|
}.get(self.state)
|
2008-03-17 18:32:06 +01:00
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@classmethod
|
|
|
|
|
def _check(cls, periods):
|
|
|
|
|
Move = Pool().get('account.move')
|
|
|
|
|
for period in periods:
|
|
|
|
|
moves = Move.search([
|
|
|
|
|
('journal', '=', period.journal.id),
|
|
|
|
|
('period', '=', period.period.id),
|
|
|
|
|
], limit=1)
|
|
|
|
|
if moves:
|
2018-12-29 14:20:28 +01:00
|
|
|
raise AccessError(
|
|
|
|
|
gettext('account.msg_modify_delete_journal_period_moves',
|
|
|
|
|
journal_period=period.rec_name))
|
2008-03-17 18:32:06 +01:00
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@classmethod
|
2013-01-06 22:18:13 +01:00
|
|
|
def create(cls, vlist):
|
2012-09-24 22:17:29 +02:00
|
|
|
Period = Pool().get('account.period')
|
2013-01-06 22:18:13 +01:00
|
|
|
for vals in vlist:
|
|
|
|
|
if vals.get('period'):
|
|
|
|
|
period = Period(vals['period'])
|
2017-04-02 12:46:28 +02:00
|
|
|
if period.state != 'open':
|
2018-12-29 14:20:28 +01:00
|
|
|
raise AccessError(
|
|
|
|
|
gettext('account'
|
|
|
|
|
'.msg_create_journal_period_closed_period',
|
|
|
|
|
period=period.rec_name))
|
2013-01-06 22:18:13 +01:00
|
|
|
return super(JournalPeriod, cls).create(vlist)
|
2008-03-26 13:33:32 +01:00
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@classmethod
|
2013-12-19 14:44:33 +01:00
|
|
|
def write(cls, *args):
|
|
|
|
|
actions = iter(args)
|
|
|
|
|
for journal_periods, values in zip(actions, actions):
|
2023-06-20 10:13:56 +02:00
|
|
|
if (values != {'state': 'closed'}
|
2013-12-19 14:44:33 +01:00
|
|
|
and values != {'state': 'open'}):
|
|
|
|
|
cls._check(journal_periods)
|
|
|
|
|
if values.get('state') == 'open':
|
|
|
|
|
for journal_period in journal_periods:
|
2017-04-02 12:46:28 +02:00
|
|
|
if journal_period.period.state != 'open':
|
2018-12-29 14:20:28 +01:00
|
|
|
raise AccessError(
|
|
|
|
|
gettext('account'
|
|
|
|
|
'.msg_open_journal_period_closed_period',
|
|
|
|
|
journal_period=journal_period.rec_name,
|
|
|
|
|
period=journal_period.period.rec_name))
|
2013-12-19 14:44:33 +01:00
|
|
|
super(JournalPeriod, cls).write(*args)
|
2008-03-17 18:32:06 +01:00
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@classmethod
|
|
|
|
|
def delete(cls, periods):
|
|
|
|
|
cls._check(periods)
|
|
|
|
|
super(JournalPeriod, cls).delete(periods)
|
2008-03-17 18:32:06 +01:00
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@classmethod
|
2017-04-02 12:46:28 +02:00
|
|
|
@ModelView.button
|
2023-06-20 10:13:56 +02:00
|
|
|
@Workflow.transition('closed')
|
2012-09-24 22:17:29 +02:00
|
|
|
def close(cls, periods):
|
2008-09-19 15:00:26 +02:00
|
|
|
'''
|
|
|
|
|
Close journal - period
|
|
|
|
|
'''
|
2017-04-02 12:46:28 +02:00
|
|
|
pass
|
2008-09-19 15:00:26 +02:00
|
|
|
|
2012-09-24 22:17:29 +02:00
|
|
|
@classmethod
|
2017-04-02 12:46:28 +02:00
|
|
|
@ModelView.button
|
|
|
|
|
@Workflow.transition('open')
|
|
|
|
|
def reopen(cls, periods):
|
2012-01-02 17:54:33 +01:00
|
|
|
"Open journal - period"
|
2017-04-02 12:46:28 +02:00
|
|
|
pass
|