#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 __future__ import with_statement
from trytond.model import ModelView, ModelSQL, fields
from trytond.wizard import Wizard
from trytond.backend import TableHandler
from trytond.pyson import Equal, Eval, Not, Get, Or, And, Bool
from trytond.transaction import Transaction

STATES = {
    'readonly': Equal(Eval('state'), 'close'),
}
DEPENDS = ['state']

_ICONS = {
    'open': 'tryton-open',
    'close': 'tryton-readonly',
}


class Type(ModelSQL, ModelView):
    'Journal Type'
    _name = 'account.journal.type'
    _description = __doc__
    name = fields.Char('Name', size=None, required=True, translate=True)
    code = fields.Char('Code', size=None, required=True)

    def __init__(self):
        super(Type, self).__init__()
        self._sql_constraints += [
            ('code_uniq', 'UNIQUE(code)', 'The code must be unique!'),
        ]
        self._order.insert(0, ('code', 'ASC'))

Type()


class View(ModelSQL, ModelView):
    'Journal View'
    _name = 'account.journal.view'
    _description = __doc__
    name = fields.Char('Name', size=None, required=True)
    columns = fields.One2Many('account.journal.view.column', 'view', 'Columns')

    def __init__(self):
        super(View, self).__init__()
        self._order.insert(0, ('name', 'ASC'))

View()


class Column(ModelSQL, ModelView):
    'Journal View Column'
    _name = 'account.journal.view.column'
    _description = __doc__
    name = fields.Char('Name', size=None, required=True)
    field = fields.Many2One('ir.model.field', 'Field', required=True,
            domain=[('model.model', '=', 'account.move.line')])
    view = fields.Many2One('account.journal.view', 'View', select=1)
    sequence = fields.Integer('Sequence', select=2)
    required = fields.Boolean('Required')
    readonly = fields.Boolean('Readonly')

    def __init__(self):
        super(Column, self).__init__()
        self._order.insert(0, ('sequence', 'ASC'))

    def default_required(self):
        return False

    def default_readonly(self):
        return False

Column()


class Journal(ModelSQL, ModelView):
    'Journal'
    _name = 'account.journal'
    _description = __doc__

    name = fields.Char('Name', size=None, required=True, translate=True)
    code = fields.Char('Code', size=None)
    active = fields.Boolean('Active', select=2)
    type = fields.Selection('get_types', 'Type', required=True)
    view = fields.Many2One('account.journal.view', 'View')
    centralised = fields.Boolean('Centralised counterpart')
    update_posted = fields.Boolean('Allow cancelling moves')
    sequence = fields.Property(fields.Many2One('ir.sequence', 'Sequence',
        domain=[('code', '=', 'account.journal')],
        context={'code': 'account.journal'},
        states={
            'required': Bool(Get(Eval('context', {}), 'company', 0)),
        }))
    credit_account = fields.Property(fields.Many2One('account.account',
        'Default Credit Account', domain=[
            ('kind', '!=', 'view'),
            ('company', '=', Get(Eval('context', {}), 'company', 0)),
        ],
        states={
            'required': And(Or(Bool(Eval('centralised')),
                Equal(Eval('type'), 'cash')),
                Bool(Get(Eval('context', {}), 'company', 0))),
            'invisible': Not(Bool(Get(Eval('context', {}), 'company', 0))),
        }, depends=['type', 'centralised']))
    debit_account = fields.Property(fields.Many2One('account.account',
        'Default Debit Account', domain=[
            ('kind', '!=', 'view'),
            ('company', '=', Get(Eval('context', {}), 'company', 0)),
        ],
        states={
            'required': And(Or(Bool(Eval('centralised')),
                Equal(Eval('type'), 'cash')),
                Bool(Get(Eval('context', {}), 'company', 0))),
            'invisible': Not(Bool(Get(Eval('context', {}), 'company', 0))),
        }, depends=['type', 'centralised']))

    def __init__(self):
        super(Journal, self).__init__()
        self._order.insert(0, ('name', 'ASC'))

    def init(self, module_name):
        super(Journal, self).init(module_name)
        cursor = Transaction().cursor
        table = TableHandler(cursor, self, module_name)

        # Migration from 1.0 sequence Many2One change into Property
        if table.column_exist('sequence'):
            property_obj = self.pool.get('ir.property')
            cursor.execute('SELECT id, sequence FROM "' + self._table +'"')
            with Transaction().set_user(0):
                for journal_id, sequence_id in cursor.fetchall():
                    property_obj.set('sequence', self._name,
                            journal_id, (sequence_id and
                                'ir.sequence,' + str(sequence_id) or False))
            table.drop_column('sequence', exception=True)

    def default_active(self):
        return True

    def default_centralised(self):
        return False

    def default_update_posted(self):
        return False

    def default_sequence(self):
        return False

    def get_types(self):
        type_obj = self.pool.get('account.journal.type')
        type_ids = type_obj.search([])
        types = type_obj.browse(type_ids)
        return [(x.code, x.name) for x in types]

    def search_rec_name(self, name, clause):
        ids = self.search([
            ('code',) + clause[1:],
            ], limit=1, order=[])
        if ids:
            return [('code',) + clause[1:]]
        return [(self._rec_name,) + clause[1:]]

Journal()


class Period(ModelSQL, ModelView):
    'Journal - Period'
    _name = 'account.journal.period'
    _description = __doc__

    name = fields.Char('Name', size=None, required=True)
    journal = fields.Many2One('account.journal', 'Journal', required=True,
            ondelete='CASCADE', states=STATES, depends=DEPENDS)
    period = fields.Many2One('account.period', 'Period', required=True,
            ondelete='CASCADE', states=STATES, depends=DEPENDS)
    icon = fields.Function(fields.Char('Icon'), 'get_icon')
    active = fields.Boolean('Active', select=2, states=STATES, depends=DEPENDS)
    state = fields.Selection([
        ('open', 'Open'),
        ('close', 'Close'),
        ], 'State', readonly=True, required=True)

    def __init__(self):
        super(Period, self).__init__()
        self._sql_constraints += [
            ('journal_period_uniq', 'UNIQUE(journal, period)',
                'You can only open one journal per period!'),
        ]
        self._order.insert(0, ('name', 'ASC'))
        self._error_messages.update({
            'modify_del_journal_period': 'You can not modify/delete ' \
                    'a journal - period with moves!',
            'create_journal_period': 'You can not create ' \
                    'a journal - period on a closed period!',
            'open_journal_period': 'You can not open ' \
                    'a journal - period from a closed period!',
            })

    def default_active(self):
        return True

    def default_state(self):
        return 'open'

    def get_icon(self, ids, name):
        res = {}
        for period in self.browse(ids):
            res[period.id] = _ICONS.get(period.state, '')
        return res

    def _check(self, ids):
        move_obj = self.pool.get('account.move')
        for period in self.browse(ids):
            move_ids = move_obj.search([
                ('journal', '=', period.journal.id),
                ('period', '=', period.period.id),
                ], limit=1)
            if move_ids:
                self.raise_user_error('modify_del_journal_period')
        return

    def create(self, vals):
        period_obj = self.pool.get('account.period')
        if vals.get('period'):
            period = period_obj.browse(vals['period'])
            if period.state == 'close':
                self.raise_user_error('create_journal_period')
        return super(Period, self).create(vals)

    def write(self, ids, vals):
        if vals != {'state': 'close'} \
                and vals != {'state': 'open'}:
            self._check(ids)
        if vals.get('state') == 'open':
            for journal_period in self.browse(ids):
                if journal_period.period.state == 'close':
                    self.raise_user_error('open_journal_period')
        return super(Period, self).write(ids, vals)

    def delete(self, ids):
        self._check(ids)
        return super(Period, self).delete(ids)

    def close(self, ids):
        '''
        Close journal - period

        :param ids: the journal - period ids
        '''
        self.write(ids, {
            'state': 'close',
            })

Period()


class ClosePeriod(Wizard):
    'Close Journal - Period'
    _name = 'account.journal.close_period'
    states = {
        'init': {
            'actions': ['_close'],
            'result': {
                'type': 'state',
                'state': 'end',
            },
        },
    }

    def _close(self, data):
        journal_period_obj = self.pool.get('account.journal.period')
        journal_period_obj.close(data['ids'])
        return {}

ClosePeriod()


class ReOpenPeriod(Wizard):
    'Re-Open Journal - Period'
    _name = 'account.journal.reopen_period'
    states = {
        'init': {
            'actions': ['_reopen'],
            'result': {
                'type': 'state',
                'state': 'end',
            },
        },
    }

    def _reopen(self, data):
        journal_period_obj = self.pool.get('account.journal.period')
        journal_period_obj.write(data['ids'], {
            'state': 'open',
            })
        return {}

ReOpenPeriod()
