#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 decimal import Decimal
import datetime
import time
try:
    import hashlib
except ImportError:
    hashlib = None
    import md5
from trytond.model import ModelView, ModelSQL, fields
from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \
    Button
from trytond.report import Report
from trytond.backend import TableHandler, FIELDS
from trytond.pyson import Eval, PYSONEncoder
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.config import CONFIG

_MOVE_STATES = {
    'readonly': Eval('state') == 'posted',
    }
_MOVE_DEPENDS = ['state']
_LINE_STATES = {
    'readonly': Eval('state') == 'valid',
    }
_LINE_DEPENDS = ['state']


class Move(ModelSQL, ModelView):
    'Account Move'
    _name = 'account.move'
    _description = __doc__

    name = fields.Char('Name', size=None, required=True, states=_MOVE_STATES,
            depends=_MOVE_DEPENDS)
    reference = fields.Char('Reference', size=None, readonly=True,
            help='Also known as Folio Number')
    period = fields.Many2One('account.period', 'Period', required=True,
            states=_MOVE_STATES, depends=_MOVE_DEPENDS, select=True)
    journal = fields.Many2One('account.journal', 'Journal', required=True,
            states=_MOVE_STATES, depends=_MOVE_DEPENDS)
    date = fields.Date('Effective Date', required=True, states=_MOVE_STATES,
        depends=_MOVE_DEPENDS, on_change_with=['period', 'journal', 'date'])
    post_date = fields.Date('Post Date', readonly=True)
    state = fields.Selection([
        ('draft', 'Draft'),
        ('posted', 'Posted'),
        ], 'State', required=True, readonly=True, select=True)
    lines = fields.One2Many('account.move.line', 'move', 'Lines',
            states=_MOVE_STATES, depends=_MOVE_DEPENDS,
            context={
                'journal': Eval('journal'),
                'period': Eval('period'),
                'date': Eval('date'),
            })

    def __init__(self):
        super(Move, self).__init__()
        self._check_modify_exclude = ['state']
        self._constraints += [
            ('check_centralisation', 'period_centralized_journal'),
            ('check_company', 'company_in_move'),
            ('check_date', 'date_outside_period'),
        ]
        self._order.insert(0, ('date', 'DESC'))
        self._order.insert(1, ('reference', 'DESC'))
        self._error_messages.update({
            'del_posted_move': 'You can not delete posted moves!',
            'post_empty_move': 'You can not post an empty move!',
            'post_unbalanced_move': 'You can not post an unbalanced move!',
            'draft_posted_move_journal': 'You can not set a posted move ' \
                    'to draft in this journal!',
            'modify_posted_move': 'Move "%s" is already posted!\n'
                    'You can not modify a posted move.',
            'period_centralized_journal': 'You can not create more than ' \
                    'one move per period\n' \
                    'in a centralized journal!',
            'company_in_move': 'You can not create lines on accounts\n' \
                    'of different companies in the same move!',
            'date_outside_period': 'You can not create move ' \
                    'with a date outside the period!',
            })
        self._buttons.update({
                'post': {
                    'invisible': Eval('state') == 'posted',
                    },
                'draft': {
                    'invisible': Eval('state') == 'draft',
                    },
                })

    def init(self, module_name):
        super(Move, self).init(module_name)

        cursor = Transaction().cursor
        table = TableHandler(cursor, self, module_name)
        table.index_action(['journal', 'period'], 'add')

        # Add index on create_date
        table.index_action('create_date', action='add')

    def default_period(self):
        period_obj = Pool().get('account.period')
        return period_obj.find(Transaction().context.get('company'),
                exception=False)

    def default_state(self):
        return 'draft'

    def default_date(self):
        period_obj = Pool().get('account.period')
        date_obj = Pool().get('ir.date')
        period_id = self.default_period()
        if period_id:
            period = period_obj.browse(period_id)
            return period.start_date
        return date_obj.today()

    def on_change_with_date(self, vals):
        line_obj = Pool().get('account.move.line')
        period_obj = Pool().get('account.period')
        res = vals['date']
        line_ids = line_obj.search([
            ('journal', '=', vals.get('journal')),
            ('period', '=', vals.get('period')),
            ], order=[('id', 'DESC')], limit=1)
        if line_ids:
            line = line_obj.browse(line_ids[0])
            res = line.date
        elif vals.get('period'):
            period = period_obj.browse(vals['period'])
            res = period.start_date
        return res

    def check_centralisation(self, ids):
        for move in self.browse(ids):
            if move.journal.centralised:
                move_ids = self.search([
                    ('period', '=', move.period.id),
                    ('journal', '=', move.journal.id),
                    ('state', '!=', 'posted'),
                    ], limit=2)
                if len(move_ids) > 1:
                    return False
        return True

    def check_company(self, ids):
        for move in self.browse(ids):
            company_id = -1
            for line in move.lines:
                if company_id < 0:
                    company_id = line.account.company.id
                if line.account.company.id != company_id:
                    return False
        return True

    def check_date(self, ids):
        for move in self.browse(ids):
            if move.date < move.period.start_date:
                return False
            if move.date > move.period.end_date:
                return False
        return True

    def check_modify(self, ids):
        'Check posted moves for modifications.'
        if isinstance(ids, (int, long)):
            ids = [ids]
        for move in self.browse(ids):
            if move.state == 'posted':
                self.raise_user_error('modify_posted_move',
                    error_args=(move.name,))
        return

    def search_rec_name(self, name, clause):
        ids = self.search(['OR',
            ('reference',) + clause[1:],
            (self._rec_name,) + clause[1:],
            ])
        return [('id', 'in', ids)]

    def write(self, ids, vals):
        keys = vals.keys()
        for key in self._check_modify_exclude:
            if key in keys:
                keys.remove(key)
        if len(keys):
            self.check_modify(ids)
        res = super(Move, self).write(ids, vals)
        self.validate(ids)
        return res

    def create(self, vals):
        pool = Pool()
        move_line_obj = pool.get('account.move.line')
        sequence_obj = pool.get('ir.sequence')
        journal_obj = pool.get('account.journal')

        if not vals.get('name'):
            journal_id = (vals.get('journal')
                    or Transaction().context.get('journal'))
            if journal_id:
                vals = vals.copy()
                journal = journal_obj.browse(journal_id)
                vals['name'] = sequence_obj.get_id(journal.sequence.id)

        res = super(Move, self).create(vals)
        move = self.browse(res)
        if move.journal.centralised:
            line_id = move_line_obj.create({
                'account': move.journal.credit_account.id,
                'move': move.id,
                'name': 'Centralised Counterpart',
                })
            self.write(move.id, {
                'centralised_line': line_id,
                })
        if 'lines' in vals:
            self.validate([res])
        return res

    def delete(self, ids):
        if isinstance(ids, (int, long)):
            ids = [ids]
        move_line_obj = Pool().get('account.move.line')
        self.check_modify(ids)
        for move in self.browse(ids):
            if move.lines:
                move_line_ids = [x.id for x in move.lines]
                move_line_obj.delete(move_line_ids)
        return super(Move, self).delete(ids)

    def copy(self, ids, default=None):
        line_obj = Pool().get('account.move.line')

        int_id = False
        if isinstance(ids, (int, long)):
            int_id = True
            ids = [ids]

        if default is None:
            default = {}
        default = default.copy()
        default['reference'] = None
        default['state'] = self.default_state()
        default['post_date'] = None
        default['lines'] = None

        new_ids = []
        for move in self.browse(ids):
            new_id = super(Move, self).copy(move.id, default=default)
            line_obj.copy([x.id for x in move.lines], default={
                'move': new_id,
                })
            new_ids.append(new_id)

        if int_id:
            return new_ids[0]
        return new_ids

    def validate(self, ids):
        '''
        Validate balanced move and centralise it if in centralised journal
        '''
        currency_obj = Pool().get('currency.currency')
        move_line_obj = Pool().get('account.move.line')
        if isinstance(ids, (int, long)):
            ids = [ids]
        for move in self.browse(ids):
            if not move.lines:
                continue
            amount = Decimal('0.0')
            company = None
            draft_lines = []
            for line in move.lines:
                amount += line.debit - line.credit
                if not company:
                    company = line.account.company
                if line.state == 'draft':
                    draft_lines.append(line)
            if not currency_obj.is_zero(company.currency, amount):
                if not move.journal.centralised:
                    move_line_obj.write(
                            [x.id for x in move.lines if x.state != 'draft'], {
                                'state': 'draft',
                                })
                else:
                    if not move.centralised_line:
                        centralised_amount = - amount
                    else:
                        centralised_amount = move.centralised_line.debit \
                                    - move.centralised_line.credit \
                                    - amount
                    if centralised_amount >= Decimal('0.0'):
                        debit = centralised_amount
                        credit = Decimal('0.0')
                        account_id = move.journal.debit_account.id
                    else:
                        debit = Decimal('0.0')
                        credit = - centralised_amount
                        account_id = move.journal.credit_account.id
                    if not move.centralised_line:
                        centralised_line_id = move_line_obj.create({
                            'debit': debit,
                            'credit': credit,
                            'account': account_id,
                            'move': move.id,
                            'name': 'Centralised Counterpart',
                            })
                        self.write(move.id, {
                            'centralised_line': centralised_line_id,
                            })
                    else:
                        move_line_obj.write(move.centralised_line.id, {
                                'debit': debit,
                                'credit': credit,
                                'account': account_id,
                                })
                continue
            if not draft_lines:
                continue
            move_line_obj.write([x.id for x in draft_lines], {
                    'state': 'valid',
                    })
        return

    @ModelView.button
    def post(self, ids):
        pool = Pool()
        currency_obj = pool.get('currency.currency')
        sequence_obj = pool.get('ir.sequence')
        date_obj = pool.get('ir.date')

        if isinstance(ids, (int, long)):
            ids = [ids]

        moves = self.browse(ids)
        for move in moves:
            amount = Decimal('0.0')
            if not move.lines:
                self.raise_user_error('post_empty_move')
            company = None
            for line in move.lines:
                amount += line.debit - line.credit
                if not company:
                    company = line.account.company
            if not currency_obj.is_zero(company.currency, amount):
                self.raise_user_error('post_unbalanced_move')
        for move in moves:
            reference = sequence_obj.get_id(move.period.post_move_sequence.id)
            self.write(move.id, {
                'reference': reference,
                'state': 'posted',
                'post_date': date_obj.today(),
                })
        return

    @ModelView.button
    def draft(self, ids):
        for move in self.browse(ids):
            if not move.journal.update_posted:
                self.raise_user_error('draft_posted_move_journal')
        return self.write(ids, {
            'state': 'draft',
            })

Move()


class Reconciliation(ModelSQL, ModelView):
    'Account Move Reconciliation Lines'
    _name = 'account.move.reconciliation'
    _description = __doc__

    name = fields.Char('Name', size=None, required=True)
    lines = fields.One2Many('account.move.line', 'reconciliation',
            'Lines')

    def __init__(self):
        super(Reconciliation, self).__init__()
        self._constraints += [
            ('check_lines', 'invalid_reconciliation'),
        ]
        self._error_messages.update({
            'modify': 'You can not modify a reconciliation!',
            'invalid_reconciliation': 'You can not create reconciliation ' \
                    'where lines are not balanced, nor valid, ' \
                    'nor in the same account, nor in account to reconcile, ' \
                    'nor from the same party!',
            })

    def create(self, vals):
        sequence_obj = Pool().get('ir.sequence')

        if 'name' not in vals:
            vals = vals.copy()
            vals['name'] = sequence_obj.get('account.move.reconciliation')

        return super(Reconciliation, self).create(vals)

    def write(self, ids, vals):
        self.raise_user_error('modify')

    def check_lines(self, ids):
        currency_obj = Pool().get('currency.currency')
        for reconciliation in self.browse(ids):
            amount = Decimal('0.0')
            account = None
            party = None
            for line in reconciliation.lines:
                if line.state != 'valid':
                    return False
                amount += line.debit - line.credit
                if not account:
                    account = line.account
                elif account.id != line.account.id:
                    return False
                if not account.reconcile:
                    return False
                if not party:
                    party = line.party
                elif line.party and party.id != line.party.id:
                    return False
            if not currency_obj.is_zero(account.company.currency, amount):
                return False
        return True

Reconciliation()


class Line(ModelSQL, ModelView):
    'Account Move Line'
    _name = 'account.move.line'
    _description = __doc__

    name = fields.Char('Name', size=None, required=True)
    debit = fields.Numeric('Debit', digits=(16, Eval('currency_digits', 2)),
        required=True,
        on_change=['account', 'debit', 'credit', 'tax_lines', 'journal',
            'move'],
        depends=['currency_digits', 'credit', 'tax_lines', 'journal'])
    credit = fields.Numeric('Credit', digits=(16, Eval('currency_digits', 2)),
        required=True,
        on_change=['account', 'debit', 'credit', 'tax_lines', 'journal',
            'move'],
        depends=['currency_digits', 'debit', 'tax_lines', 'journal'])
    account = fields.Many2One('account.account', 'Account', required=True,
            domain=[('kind', '!=', 'view')],
            select=True,
            on_change=['account', 'debit', 'credit', 'tax_lines',
                'journal', 'move'])
    move = fields.Many2One('account.move', 'Move', select=True, required=True,
        states={
            'required': False,
            'readonly': Eval('state') == 'valid',
            },
        depends=['state'])
    journal = fields.Function(fields.Many2One('account.journal', 'Journal'),
            'get_move_field', setter='set_move_field',
            searcher='search_move_field')
    period = fields.Function(fields.Many2One('account.period', 'Period'),
            'get_move_field', setter='set_move_field',
            searcher='search_move_field')
    date = fields.Function(fields.Date('Effective Date', required=True),
            'get_move_field', setter='set_move_field',
            searcher='search_move_field')
    reference = fields.Char('Reference', size=None)
    amount_second_currency = fields.Numeric('Amount Second Currency',
            digits=(16, Eval('second_currency_digits', 2)),
            help='The amount expressed in a second currency',
            depends=['second_currency_digits'])
    second_currency = fields.Many2One('currency.currency', 'Second Currency',
            help='The second currency')
    party = fields.Many2One('party.party', 'Party',
            on_change=['move', 'party', 'account', 'debit', 'credit',
                'journal'], select=True, depends=['debit', 'credit', 'account',
                    'journal'])
    maturity_date = fields.Date('Maturity Date',
            help='This field is used for payable and receivable lines. \n' \
                    'You can put the limit date for the payment.')
    state = fields.Selection([
        ('draft', 'Draft'),
        ('valid', 'Valid'),
        ], 'State', readonly=True, required=True, select=True)
    active = fields.Boolean('Active', select=True)
    reconciliation = fields.Many2One('account.move.reconciliation',
            'Reconciliation', readonly=True, ondelete='SET NULL', select=True)
    tax_lines = fields.One2Many('account.tax.line', 'move_line', 'Tax Lines')
    move_state = fields.Function(fields.Selection([
        ('draft', 'Draft'),
        ('posted', 'Posted'),
        ], 'Move State'), 'get_move_field', searcher='search_move_field')
    currency_digits = fields.Function(fields.Integer('Currency Digits'),
            'get_currency_digits')
    second_currency_digits = fields.Function(fields.Integer(
        'Second Currency Digits'), 'get_currency_digits')

    def __init__(self):
        super(Line, self).__init__()
        self._sql_constraints += [
            ('credit_debit',
                'CHECK(credit * debit = 0.0)',
                'Wrong credit/debit values!'),
        ]
        self._constraints += [
            ('check_account', 'move_view_inactive_account'),
        ]
        self._rpc.update({
            'on_write': False,
        })
        self._order[0] = ('id', 'DESC')
        self._error_messages.update({
            'add_modify_closed_journal_period': 'You can not ' \
                    'add/modify lines in a closed journal period!',
            'modify_posted_move': 'You can not modify lines of a posted move!',
            'modify_reconciled': 'You can not modify reconciled lines!',
            'no_journal': 'No journal defined!',
            'move_view_inactive_account': 'You can not create move lines\n' \
                    'on view/inactive accounts!',
            'already_reconciled': 'Line "%s" (%d) already reconciled!',
            })

    def init(self, module_name):
        super(Line, self).init(module_name)

        cursor = Transaction().cursor
        table = TableHandler(cursor, self, module_name)
        # Index for General Ledger
        table.index_action(['move', 'account'], 'add')

        # Migration from 1.2
        table.not_null_action('blocked', action='remove')

    def default_date(self):
        '''
        Return the date of the last line for journal, period
        or the starting date of the period
        or today
        '''
        period_obj = Pool().get('account.period')
        date_obj = Pool().get('ir.date')

        res = date_obj.today()
        line_ids = self.search([
            ('journal', '=', Transaction().context.get('journal')),
            ('period', '=', Transaction().context.get('period')),
            ], order=[('id', 'DESC')], limit=1)
        if line_ids:
            line = self.browse(line_ids[0])
            res = line.date
        elif Transaction().context.get('period'):
            period = period_obj.browse(Transaction().context['period'])
            res = period.start_date
        if Transaction().context.get('date'):
            res = Transaction().context['date']
        return res

    def default_state(self):
        return 'draft'

    def default_active(self):
        return True

    def default_currency_digits(self):
        return 2

    def default_debit(self):
        return Decimal(0)

    def default_credit(self):
        return Decimal(0)

    def default_get(self, fields, with_rec_name=True):
        pool = Pool()
        move_obj = pool.get('account.move')
        tax_obj = pool.get('account.tax')
        account_obj = pool.get('account.account')
        tax_code_obj = pool.get('account.tax.code')
        currency_obj = pool.get('currency.currency')
        values = super(Line, self).default_get(fields,
                with_rec_name=with_rec_name)

        if 'move' not in fields:
            #Not manual entry
            if 'date' in values:
                values = values.copy()
                del values['date']
            return values

        if (Transaction().context.get('journal')
                and Transaction().context.get('period')):
            line_ids = self.search([
                ('move.journal', '=', Transaction().context['journal']),
                ('move.period', '=', Transaction().context['period']),
                ('create_uid', '=', Transaction().user),
                ('state', '=', 'draft'),
                ], order=[('id', 'DESC')], limit=1)
            if not line_ids:
                return values
            line = self.browse(line_ids[0])
            values['move'] = line.move.id

        if 'move' not in values:
            return values

        move = move_obj.browse(values['move'])
        total = Decimal('0.0')
        taxes = {}
        no_code_taxes = []
        for line in move.lines:
            total += line.debit - line.credit
            if line.party and 'party' in fields:
                values.setdefault('party', line.party.id)
            if 'reference' in fields:
                values.setdefault('reference', line.reference)
            if 'name' in fields:
                values.setdefault('name', line.name)
            if move.journal.type in ('expense', 'revenue'):
                line_code_taxes = [x.code.id for x in line.tax_lines]
                for tax in line.account.taxes:
                    if move.journal.type == 'revenue':
                        if line.debit:
                            base_id = tax.credit_note_base_code.id
                            code_id = tax.credit_note_tax_code.id
                            account_id = tax.credit_note_account.id
                        else:
                            base_id = tax.invoice_base_code.id
                            code_id = tax.invoice_tax_code.id
                            account_id = tax.invoice_account.id
                    else:
                        if line.debit:
                            base_id = tax.invoice_base_code.id
                            code_id = tax.invoice_tax_code.id
                            account_id = tax.invoice_account.id
                        else:
                            base_id = tax.credit_note_base_code.id
                            code_id = tax.credit_note_tax_code.id
                            account_id = tax.credit_note_account.id
                    if base_id in line_code_taxes or not base_id:
                        taxes.setdefault((account_id, code_id, tax.id), None)
                for tax_line in line.tax_lines:
                    taxes[(line.account.id, tax_line.code.id,
                            tax_line.tax.id)] = True
                if not line.tax_lines:
                    no_code_taxes.append(line.account.id)
        for no_code_account_id in no_code_taxes:
            for (account_id, code_id, tax_id), test in \
                    taxes.iteritems():
                if (not test
                        and not code_id
                        and no_code_account_id == account_id):
                    taxes[(account_id, code_id, tax_id)] = True

        if 'account' in fields:
            if total >= Decimal('0.0'):
                values['account'] = (move.journal.credit_account \
                        and move.journal.credit_account.id or None)
            else:
                values['account'] = (move.journal.debit_account \
                        and move.journal.debit_account.id or None)

        if ('debit' in fields) or ('credit' in fields):
            values['debit'] = total < 0 and - total or Decimal(0)
            values['credit'] = total > 0 and total or Decimal(0)

        if move.journal.type in ('expense', 'revenue'):
            for account_id, code_id, tax_id in taxes:
                if taxes[(account_id, code_id, tax_id)]:
                    continue
                for line in move.lines:
                    if move.journal.type == 'revenue':
                        if line.debit:
                            key = 'credit_note'
                        else:
                            key = 'invoice'
                    else:
                        if line.debit:
                            key = 'invoice'
                        else:
                            key = 'credit_note'
                    line_amount = Decimal('0.0')
                    tax_amount = Decimal('0.0')
                    for tax_line in tax_obj.compute(
                            [x.id for x in line.account.taxes],
                            line.debit or line.credit, 1):
                        if (tax_line['tax'][key + '_account'].id \
                                or line.account.id) == account_id \
                                and tax_line['tax'][key + '_tax_code'].id \
                                    == code_id \
                                and tax_line['tax'].id == tax_id:
                            if line.debit:
                                line_amount += tax_line['amount']
                            else:
                                line_amount -= tax_line['amount']
                            tax_amount += tax_line['amount'] * \
                                    tax_line['tax'][key + '_tax_sign']
                    line_amount = currency_obj.round(
                            line.account.company.currency, line_amount)
                    tax_amount = currency_obj.round(
                            line.account.company.currency, tax_amount)
                    if ('debit' in fields):
                        values['debit'] = line_amount > Decimal('0.0') \
                                and line_amount or Decimal('0.0')
                    if ('credit' in fields):
                        values['credit'] = line_amount < Decimal('0.0') \
                                and - line_amount or Decimal('0.0')
                    if 'account' in fields:
                        values['account'] = account_id
                        values['account.rec_name'] = account_obj.browse(
                                account_id).rec_name
                    if 'tax_lines' in fields and code_id:
                        values['tax_lines'] = [
                            {
                                'amount': tax_amount,
                                'currency_digits': line.currency_digits,
                                'code': code_id,
                                'code.rec_name': tax_code_obj.browse(
                                    code_id).rec_name,
                                'tax': tax_id,
                                'tax.rec_name': tax_obj.browse(tax_id).rec_name
                            },
                        ]
        return values

    def get_currency_digits(self, ids, names):
        res = {}
        for line in self.browse(ids):
            for name in names:
                res.setdefault(name, {})
                res[name].setdefault(line.id, 2)
                if name == 'currency_digits':
                    res[name][line.id] = line.account.currency_digits
                elif name == 'second_currency_digits':
                    second_currency = line.account.second_currency
                    if second_currency:
                        res[name][line.id] = second_currency.digits
        return res

    def on_change_debit(self, vals):
        res = {}
        journal_obj = Pool().get('account.journal')
        if vals.get('journal') or Transaction().context.get('journal'):
            journal = journal_obj.browse(vals.get('journal')
                    or Transaction().context.get('journal'))
            if journal.type in ('expense', 'revenue'):
                res['tax_lines'] = self._compute_tax_lines(vals, journal.type)
                if not res['tax_lines']:
                    del res['tax_lines']
        if vals.get('debit'):
            res['credit'] = Decimal('0.0')
        return res

    def on_change_credit(self, vals):
        res = {}
        journal_obj = Pool().get('account.journal')
        if vals.get('journal') or Transaction().context.get('journal'):
            journal = journal_obj.browse(vals.get('journal')
                    or Transaction().context.get('journal'))
            if journal.type in ('expense', 'revenue'):
                res['tax_lines'] = self._compute_tax_lines(
                        vals, journal.type)
                if not res['tax_lines']:
                    del res['tax_lines']
        if vals.get('credit'):
            res['debit'] = Decimal('0.0')
        return res

    def on_change_account(self, vals):
        account_obj = Pool().get('account.account')
        journal_obj = Pool().get('account.journal')

        res = {}
        if Transaction().context.get('journal'):
            journal = journal_obj.browse(Transaction().context['journal'])
            if journal.type in ('expense', 'revenue'):
                res['tax_lines'] = self._compute_tax_lines(vals, journal.type)
                if not res['tax_lines']:
                    del res['tax_lines']

        if vals.get('account'):
            account = account_obj.browse(vals['account'])
            res['currency_digits'] = account.currency_digits
            if account.second_currency:
                res['second_currency_digits'] = account.second_currency.digits
        return res

    def _compute_tax_lines(self, vals, journal_type):
        res = {}
        pool = Pool()
        account_obj = pool.get('account.account')
        tax_code_obj = pool.get('account.tax.code')
        tax_obj = pool.get('account.tax')
        tax_line_obj = pool.get('account.tax.line')

        if vals.get('move'):
            #Only for first line
            return res
        if vals.get('tax_lines'):
            res['remove'] = [x['id'] for x in vals['tax_lines']]
        if vals.get('account'):
            account = account_obj.browse(vals['account'])
            debit = vals.get('debit', Decimal('0.0'))
            credit = vals.get('credit', Decimal('0.0'))
            for tax in account.taxes:
                if journal_type == 'revenue':
                    if debit:
                        key = 'credit_note'
                    else:
                        key = 'invoice'
                else:
                    if debit:
                        key = 'invoice'
                    else:
                        key = 'credit_note'
                base_amounts = {}
                for tax_line in tax_obj.compute(
                        [x.id for x in account.taxes],
                        debit or credit, 1):
                    code_id = tax_line['tax'][key + '_base_code'].id
                    if not code_id:
                        continue
                    tax_id = tax_line['tax'].id
                    base_amounts.setdefault((code_id, tax_id), Decimal('0.0'))
                    base_amounts[code_id, tax_id] += tax_line['base'] * \
                            tax_line['tax'][key + '_tax_sign']
                for code_id, tax_id in base_amounts:
                    if not base_amounts[code_id, tax_id]:
                        continue
                    value = tax_line_obj.default_get(
                        tax_line_obj._columns.keys())
                    value.update({
                            'amount': base_amounts[code_id, tax_id],
                            'currency_digits': account.currency_digits,
                            'code': code_id,
                            'code.rec_name': tax_code_obj.browse(
                                code_id).rec_name,
                            'tax': tax_id,
                            'tax.rec_name': tax_obj.browse(tax_id).rec_name,
                            })
                    res.setdefault('add', []).append(value)
        return res

    def on_change_party(self, vals):
        pool = Pool()
        party_obj = pool.get('party.party')
        journal_obj = pool.get('account.journal')
        currency_obj = pool.get('currency.currency')
        cursor = Transaction().cursor
        res = {}
        if (not vals.get('party')) or vals.get('account'):
            return res
        party = party_obj.browse(vals.get('party'))

        if not party.account_receivable \
                or not party.account_payable:
            return res

        if party and (not vals.get('debit')) and (not vals.get('credit')):
            type_name = FIELDS[self.debit._type].sql_type(self.debit)[0]
            query = 'SELECT ' \
                        'CAST(COALESCE(SUM(' \
                            '(COALESCE(debit, 0) - COALESCE(credit, 0))' \
                        '), 0) AS ' + type_name + ') ' \
                    'FROM account_move_line ' \
                    'WHERE reconciliation IS NULL ' \
                        'AND party = %s ' \
                        'AND account = %s'
            cursor.execute(query, (party.id, party.account_receivable.id))
            amount = cursor.fetchone()[0]
            # SQLite uses float for SUM
            if not isinstance(amount, Decimal):
                amount = Decimal(str(amount))
            if not currency_obj.is_zero(party.account_receivable.currency,
                    amount):
                if amount > Decimal('0.0'):
                    res['credit'] = currency_obj.round(
                            party.account_receivable.currency, amount)
                    res['debit'] = Decimal('0.0')
                else:
                    res['credit'] = Decimal('0.0')
                    res['debit'] = - currency_obj.round(
                            party.account_receivable.currency, amount)
                res['account'] = party.account_receivable.id
                res['account.rec_name'] = party.account_receivable.rec_name
            else:
                cursor.execute(query, (party.id, party.account_payable.id))
                amount = cursor.fetchone()[0]
                if not currency_obj.is_zero(party.account_payable.currency,
                        amount):
                    if amount > Decimal('0.0'):
                        res['credit'] = currency_obj.round(
                                party.account_payable.currency, amount)
                        res['debit'] = Decimal('0.0')
                    else:
                        res['credit'] = Decimal('0.0')
                        res['debit'] = - currency_obj.round(
                                party.account_payable.currency, amount)
                    res['account'] = party.account_payable.id
                    res['account.rec_name'] = party.account_payable.rec_name

        if party and vals.get('debit'):
            if vals['debit'] > Decimal('0.0'):
                if 'account' not in res:
                    res['account'] = party.account_receivable.id
                    res['account.rec_name'] = party.account_receivable.rec_name
            else:
                if 'account' not in res:
                    res['account'] = party.account_payable.id
                    res['account.rec_name'] = party.account_payable.id

        if party and vals.get('credit'):
            if vals['credit'] > Decimal('0.0'):
                if 'account' not in res:
                    res['account'] = party.account_payable.id
                    res['account.rec_name'] = party.account_payable.rec_name
            else:
                if 'account' not in res:
                    res['account'] = party.account_receivable.id
                    res['account.rec_name'] = party.account_receivable.rec_name

        journal_id = (vals.get('journal')
                or Transaction().context.get('journal'))
        if journal_id and party:
            journal = journal_obj.browse(journal_id)
            if journal.type == 'revenue':
                if 'account' not in res:
                    res['account'] = party.account_receivable.id
                    res['account.rec_name'] = party.account_receivable.rec_name
            elif journal.type == 'expense':
                if 'account' not in res:
                    res['account'] = party.account_payable.id
                    res['account.rec_name'] = party.account_payable.rec_name
        return res

    def get_move_field(self, ids, name):
        if name == 'move_state':
            name = 'state'
        if name not in ('period', 'journal', 'date', 'state'):
            raise Exception('Invalid name')
        res = {}
        for line in self.browse(ids):
            if name in ('date', 'state'):
                res[line.id] = line.move[name]
            else:
                res[line.id] = line.move[name].id
        return res

    def set_move_field(self, ids, name, value):
        if name == 'move_state':
            name = 'state'
        if name not in ('period', 'journal', 'date', 'state'):
            raise Exception('Invalid name')
        if not value:
            return
        move_obj = Pool().get('account.move')
        lines = self.browse(ids)
        move_obj.write([line.move.id for line in lines], {
            name: value,
            })

    def search_move_field(self, name, clause):
        if name == 'move_state':
            name = 'state'
        return [('move.' + name,) + tuple(clause[1:])]

    def query_get(self, obj='l'):
        '''
        Return SQL clause and fiscal years for account move line
        depending of the context.

        :param obj: the SQL alias of account_move_line in the query
        :return: a tuple with the SQL clause and the fiscalyear ids
        '''
        fiscalyear_obj = Pool().get('account.fiscalyear')

        if Transaction().context.get('date'):
            time.strptime(str(Transaction().context['date']), '%Y-%m-%d')
            fiscalyear_ids = fiscalyear_obj.search([
                ('start_date', '<=', Transaction().context['date']),
                ('end_date', '>=', Transaction().context['date']),
                ], limit=1)

            fiscalyear_id = fiscalyear_ids and fiscalyear_ids[0] or 0

            if Transaction().context.get('posted'):
                return (obj + '.active ' \
                        'AND ' + obj + '.state != \'draft\' ' \
                        'AND ' + obj + '.move IN (' \
                            'SELECT m.id FROM account_move AS m, ' \
                                'account_period AS p ' \
                                'WHERE m.period = p.id ' \
                                    'AND p.fiscalyear = ' + \
                                        str(fiscalyear_id) + ' ' \
                                    'AND m.date <= date(\'' + \
                                        str(Transaction().context['date']) + \
                                        '\') ' \
                                    'AND m.state = \'posted\' ' \
                            ')', fiscalyear_ids)
            else:
                return (obj + '.active ' \
                        'AND ' + obj + '.state != \'draft\' ' \
                        'AND ' + obj + '.move IN (' \
                            'SELECT m.id FROM account_move AS m, ' \
                                'account_period AS p ' \
                                'WHERE m.period = p.id ' \
                                    'AND p.fiscalyear = ' + \
                                        str(fiscalyear_id) + ' ' \
                                    'AND m.date <= date(\'' + \
                                        str(Transaction().context['date']) + \
                                        '\')' \
                            ')', fiscalyear_ids)

        if not Transaction().context.get('fiscalyear'):
            fiscalyear_ids = fiscalyear_obj.search([
                ('state', '=', 'open'),
                ])
            fiscalyear_clause = (','.join(map(str, fiscalyear_ids))) or '0'
        else:
            fiscalyear_ids = [int(Transaction().context.get('fiscalyear'))]
            fiscalyear_clause = '%s' % int(
                    Transaction().context.get('fiscalyear'))

        if Transaction().context.get('periods'):
            ids = ','.join(
                    str(int(x)) for x in Transaction().context['periods'])
            if Transaction().context.get('posted'):
                return (obj + '.active '
                    'AND ' + obj + '.state != \'draft\' '
                    'AND ' + obj + '.move IN ('
                        'SELECT id FROM account_move '
                        'WHERE period IN (' + ids + ') '
                            'AND state = \'posted\' '
                        ')', [])
            else:
                return (obj + '.active '
                    'AND ' + obj + '.state != \'draft\' '
                    'AND ' + obj + '.move IN ('
                        'SELECT id FROM account_move '
                        'WHERE period IN (' + ids + ')'
                        ')', [])
        else:
            if Transaction().context.get('posted'):
                return (obj + '.active '
                    'AND ' + obj + '.state != \'draft\' '
                    'AND ' + obj + '.move IN ('
                        'SELECT id FROM account_move '
                        'WHERE period IN ('
                            'SELECT id FROM account_period '
                            'WHERE fiscalyear IN (' + fiscalyear_clause + ')'
                            ') '
                            'AND state = \'posted\' '
                        ')', fiscalyear_ids)
            else:
                return (obj + '.active '
                    'AND ' + obj + '.state != \'draft\' '
                    'AND ' + obj + '.move IN ('
                        'SELECT id FROM account_move '
                        'WHERE period IN ('
                            'SELECT id FROM account_period '
                            'WHERE fiscalyear IN (' + fiscalyear_clause + ')'
                            ')'
                        ')', fiscalyear_ids)

    def on_write(self, ids):
        lines = self.browse(ids)
        res = []
        for line in lines:
            res.extend([x.id for x in line.move.lines])
        return list({}.fromkeys(res))

    def check_account(self, ids):
        for line in self.browse(ids):
            if line.account.kind in ('view',):
                return False
            if not line.account.active:
                return False
        return True

    def check_journal_period_modify(self, period_id, journal_id):
        '''
        Check if the lines can be modified or created for the journal - period
        and if there is no journal - period, create it
        '''
        pool = Pool()
        journal_period_obj = pool.get('account.journal.period')
        journal_obj = pool.get('account.journal')
        period_obj = pool.get('account.period')
        journal_period_ids = journal_period_obj.search([
            ('journal', '=', journal_id),
            ('period', '=', period_id),
            ], limit=1)
        if journal_period_ids:
            journal_period = journal_period_obj.browse(journal_period_ids[0])
            if journal_period.state == 'close':
                self.raise_user_error('add_modify_closed_journal_period')
        else:
            journal = journal_obj.browse(journal_id)
            period = period_obj.browse(period_id)
            journal_period_obj.create({
                'name': journal.name + ' - ' + period.name,
                'journal': journal.id,
                'period': period.id,
                })
        return

    def check_modify(self, ids):
        '''
        Check if the lines can be modified
        '''
        journal_period_done = []
        for line in self.browse(ids):
            if line.move.state == 'posted':
                self.raise_user_error('modify_posted_move')
            if line.reconciliation:
                self.raise_user_error('modify_reconciled')
            journal_period = (line.journal.id, line.period.id)
            if journal_period not in journal_period_done:
                self.check_journal_period_modify(line.period.id,
                        line.journal.id)
                journal_period_done.append(journal_period)
        return

    def delete(self, ids):
        move_obj = Pool().get('account.move')
        if isinstance(ids, (int, long)):
            ids = [ids]
        self.check_modify(ids)
        lines = self.browse(ids)
        move_ids = [x.move.id for x in lines]
        res = super(Line, self).delete(ids)
        move_obj.validate(move_ids)
        return res

    def write(self, ids, vals):
        move_obj = Pool().get('account.move')

        if isinstance(ids, (int, long)):
            ids = [ids]

        if len(vals) > 1 or 'reconciliation' not in vals:
            self.check_modify(ids)
        lines = self.browse(ids)
        move_ids = [x.move.id for x in lines]
        res = super(Line, self).write(ids, vals)

        Transaction().timestamp = {}

        lines = self.browse(ids)
        for line in lines:
            if line.move.id not in move_ids:
                move_ids.append(line.move.id)
        move_obj.validate(move_ids)
        return res

    def create(self, vals):
        journal_obj = Pool().get('account.journal')
        move_obj = Pool().get('account.move')
        vals = vals.copy()
        if not vals.get('move'):
            journal_id = (vals.get('journal')
                    or Transaction().context.get('journal'))
            if not journal_id:
                self.raise_user_error('no_journal')
            journal = journal_obj.browse(journal_id)
            if journal.centralised:
                move_ids = move_obj.search([
                    ('period', '=', vals.get('period')
                        or Transaction().context.get('period')),
                    ('journal', '=', journal_id),
                    ('state', '!=', 'posted'),
                    ], limit=1)
                if move_ids:
                    vals['move'] = move_ids[0]
            if not vals.get('move'):
                vals['move'] = move_obj.create({
                    'period': (vals.get('period')
                        or Transaction().context.get('period')),
                    'journal': journal_id,
                    'date': vals.get('date'),
                    })
        res = super(Line, self).create(vals)
        line = self.browse(res)
        self.check_journal_period_modify(line.period.id, line.journal.id)
        move_obj.validate([vals['move']])
        return res

    def copy(self, ids, default=None):
        if default is None:
            default = {}
        if 'move' not in default:
            default['move'] = None
        if 'reconciliation' not in default:
            default['reconciliation'] = None
        return super(Line, self).copy(ids, default=default)

    def view_header_get(self, value, view_type='form'):
        journal_period_obj = Pool().get('account.journal.period')
        if (not Transaction().context.get('journal')
                or not Transaction().context.get('period')):
            return value
        journal_period_ids = journal_period_obj.search([
            ('journal', '=', Transaction().context['journal']),
            ('period', '=', Transaction().context['period']),
            ], limit=1)
        if not journal_period_ids:
            return value
        journal_period = journal_period_obj.browse(journal_period_ids[0])
        return value + ': ' + journal_period.rec_name

    def fields_view_get(self, view_id=None, view_type='form', hexmd5=None):
        journal_obj = Pool().get('account.journal')
        result = super(Line, self).fields_view_get(view_id=view_id,
            view_type=view_type, hexmd5=hexmd5)
        if view_type == 'tree' and 'journal' in Transaction().context:
            title = self.view_header_get('', view_type=view_type)
            journal = journal_obj.browse(Transaction().context['journal'])

            if not journal.view:
                return result

            xml = '<?xml version="1.0"?>\n' \
                    '<tree string="%s" editable="top" on_write="on_write" ' \
                    'colors="red:state==\'draft\'">\n' % title
            fields = set()
            for column in journal.view.columns:
                fields.add(column.field.name)
                attrs = []
                if column.field.name == 'debit':
                    attrs.append('sum="Debit"')
                elif column.field.name == 'credit':
                    attrs.append('sum="Credit"')
                if column.readonly:
                    attrs.append('readonly="1"')
                if column.required:
                    attrs.append('required="1"')
                else:
                    attrs.append('required="0"')
                xml += ('<field name="%s" %s/>\n'
                    % (column.field.name, ' '.join(attrs)))
                for depend in getattr(self, column.field.name).depends:
                    fields.add(depend)
            fields.add('state')
            xml += '</tree>'
            result['arch'] = xml
            result['fields'] = self.fields_get(fields_names=list(fields))
            del result['md5']
            if hashlib:
                result['md5'] = hashlib.md5(str(result)).hexdigest()
            else:
                result['md5'] = md5.new(str(result)).hexdigest()
            if hexmd5 == result['md5']:
                return True
        return result

    def reconcile(self, ids, journal_id=None, date=None, account_id=None):
        pool = Pool()
        move_obj = pool.get('account.move')
        currency_obj = pool.get('currency.currency')
        reconciliation_obj = pool.get('account.move.reconciliation')
        period_obj = pool.get('account.period')
        date_obj = pool.get('ir.date')
        translation_obj = pool.get('ir.translation')

        for line in self.browse(ids):
            if line.reconciliation:
                self.raise_user_error('already_reconciled',
                        error_args=(line.name, line.id,))

        ids = ids[:]
        if journal_id and account_id:
            if not date:
                date = date_obj.today()
            account = None
            amount = Decimal('0.0')
            for line in self.browse(ids):
                amount += line.debit - line.credit
                if not account:
                    account = line.account
            amount = currency_obj.round(account.currency, amount)
            period_id = period_obj.find(account.company.id, date=date)
            lang_code = CONFIG['language']
            if account.company.lang:
                lang_code = account.company.lang.code
            writeoff = translation_obj.get_source(
                    'account.move.reconcile_lines.writeoff', 'view', lang_code,
                    'Write-Off') or 'Write-Off'
            move_id = move_obj.create({
                'journal': journal_id,
                'period': period_id,
                'date': date,
                'lines': [
                    ('create', {
                        'name': writeoff,
                        'account': account.id,
                        'debit': amount < Decimal('0.0') and - amount \
                                or Decimal('0.0'),
                        'credit': amount > Decimal('0.0') and amount \
                                or Decimal('0.0'),
                    }),
                    ('create', {
                        'name': writeoff,
                        'account': account_id,
                        'debit': amount > Decimal('0.0') and amount \
                                or Decimal('0.0'),
                        'credit': amount < Decimal('0.0') and - amount \
                                or Decimal('0.0'),
                    }),
                ],
                })
            ids += self.search([
                ('move', '=', move_id),
                ('account', '=', account.id),
                ('debit', '=', amount < Decimal('0.0') and - amount \
                        or Decimal('0.0')),
                ('credit', '=', amount > Decimal('0.0') and amount \
                        or Decimal('0.0')),
                ], limit=1)
        return reconciliation_obj.create({
            'lines': [('add', x) for x in ids],
            })

Line()


class Move2(ModelSQL, ModelView):
    _name = 'account.move'
    centralised_line = fields.Many2One('account.move.line', 'Centralised Line',
            readonly=True)

Move2()


class OpenJournalAsk(ModelView):
    'Open Journal Ask'
    _name = 'account.move.open_journal.ask'
    _description = __doc__
    journal = fields.Many2One('account.journal', 'Journal', required=True)
    period = fields.Many2One('account.period', 'Period', required=True,
        domain=[
            ('state', '!=', 'close'),
            ('fiscalyear.company.id', '=',
                Eval('context', {}).get('company', 0)),
            ])

    def default_period(self):
        period_obj = Pool().get('account.period')
        return period_obj.find(Transaction().context.get('company'),
                exception=False)

OpenJournalAsk()


class OpenJournal(Wizard):
    'Open Journal'
    _name = 'account.move.open_journal'

    start = StateTransition()
    ask = StateView('account.move.open_journal.ask',
        'account.open_journal_ask_view_form', [
            Button('Cancel', 'end', 'tryton-cancel'),
            Button('Open', 'open_', 'tryton-ok', default=True),
            ])
    open_ = StateAction('account.act_move_line_form')

    def transition_start(self, session):
        if (Transaction().context.get('active_model', '')
                == 'account.journal.period'
                and Transaction().context.get('active_id')):
            return 'open_'
        return 'ask'

    def default_ask(self, session, fields):
        journal_period_obj = Pool().get('account.journal.period')
        if (Transaction().context.get('active_model', '') ==
                'account.journal.period'
                and Transaction().context.get('active_id')):
            journal_period = \
                journal_period_obj.browse(Transaction().context['active_id'])
            return {
                'journal': journal_period.journal.id,
                'period': journal_period.period.id,
            }
        return {}

    def do_open_(self, session, action):
        pool = Pool()
        journal_period_obj = pool.get('account.journal.period')
        journal_obj = pool.get('account.journal')
        period_obj = pool.get('account.period')

        if (Transaction().context.get('active_model', '') ==
                'account.journal.period'
                and Transaction().context.get('active_id')):
            journal_period = \
                journal_period_obj.browse(Transaction().context['active_id'])
            journal_id = journal_period.journal.id
            period_id = journal_period.period.id
        else:
            journal_id = session.ask.journal.id
            period_id = session.ask.period.id
        journal_period_ids = journal_period_obj.search([
            ('journal', '=', journal_id),
            ('period', '=', period_id),
            ])
        if not journal_period_ids:
            journal = journal_obj.browse(journal_id)
            period = period_obj.browse(period_id)
            journal_period_id = journal_period_obj.create({
                    'name': journal.name + ' - ' + period.name,
                    'journal': journal.id,
                    'period': period.id,
                    })
        else:
            journal_period_id = journal_period_ids[0]
        journal_period = journal_period_obj.browse(journal_period_id)

        action['name'] += ' - %s' % journal_period.rec_name
        action['pyson_domain'] = PYSONEncoder().encode([
            ('journal', '=', journal_id),
            ('period', '=', period_id),
            ])
        action['pyson_context'] = PYSONEncoder().encode({
            'journal': journal_id,
            'period': period_id,
            })
        return action, {}

    def transition_open_(self, session):
        return 'end'

OpenJournal()


class OpenAccount(Wizard):
    'Open Account'
    _name = 'account.move.open_account'
    start_state = 'open_'
    open_ = StateAction('account.act_move_line_form')

    def do_open_(self, session, action):
        pool = Pool()
        fiscalyear_obj = pool.get('account.fiscalyear')

        if not Transaction().context.get('fiscalyear'):
            fiscalyear_ids = fiscalyear_obj.search([
                ('state', '=', 'open'),
                ])
        else:
            fiscalyear_ids = [Transaction().context['fiscalyear']]

        period_ids = []
        for fiscalyear in fiscalyear_obj.browse(fiscalyear_ids):
            for period in fiscalyear.periods:
                period_ids.append(period.id)

        action['pyson_domain'] = [
            ('period', 'in', period_ids),
            ('account', '=', Transaction().context['active_id']),
            ]
        if Transaction().context.get('posted'):
            action['pyson_domain'].append(('move.state', '=', 'posted'))
        action['pyson_domain'] = PYSONEncoder().encode(action['pyson_domain'])
        action['pyson_context'] = PYSONEncoder().encode({
            'fiscalyear': Transaction().context.get('fiscalyear'),
        })
        return action, {}

OpenAccount()


class ReconcileLinesWriteOff(ModelView):
    'Reconcile Lines Write-Off'
    _name = 'account.move.reconcile_lines.writeoff'
    _description = __doc__
    journal = fields.Many2One('account.journal', 'Journal', required=True)
    date = fields.Date('Date', required=True)
    account = fields.Many2One('account.account', 'Account', required=True,
            domain=[('kind', '!=', 'view')])

    def default_date(self):
        date_obj = Pool().get('ir.date')
        return date_obj.today()

ReconcileLinesWriteOff()


class ReconcileLines(Wizard):
    'Reconcile Lines'
    _name = 'account.move.reconcile_lines'

    start = StateTransition()
    writeoff = StateView('account.move.reconcile_lines.writeoff',
        'account.reconcile_lines_writeoff_view_form', [
            Button('Cancel', 'end', 'tryton-cancel'),
            Button('Reconcile', 'reconcile', 'tryton-ok', default=True),
            ])
    reconcile = StateTransition()

    def transition_start(self, session):
        line_obj = Pool().get('account.move.line')
        currency_obj = Pool().get('currency.currency')

        company = None
        amount = Decimal('0.0')
        for line in line_obj.browse(Transaction().context['active_ids']):
            amount += line.debit - line.credit
            if not company:
                company = line.account.company
        if not company:
            return 'end'
        if currency_obj.is_zero(company.currency, amount):
            return 'reconcile'
        return 'writeoff'

    def transition_reconcile(self, session):
        line_obj = Pool().get('account.move.line')

        journal_id = None
        date = None
        account_id = None
        if session.writeoff.journal:
            journal_id = session.writeoff.journal.id
        if session.writeoff.date:
            date = session.writeoff.date
        if session.writeoff.account:
            account_id = session.writeoff.account.id
        line_obj.reconcile(Transaction().context['active_ids'], journal_id,
            date, account_id)
        return 'end'

ReconcileLines()


class UnreconcileLinesStart(ModelView):
    'Unreconcile Lines'
    _name = 'account.move.unreconcile_lines.start'
    _description = __doc__

UnreconcileLinesStart()


class UnreconcileLines(Wizard):
    'Unreconcile Lines'
    _name = 'account.move.unreconcile_lines'
    start_state = 'unreconcile'

    unreconcile = StateTransition()

    def transition_unreconcile(self, session):
        line_obj = Pool().get('account.move.line')
        reconciliation_obj = Pool().get('account.move.reconciliation')

        lines = line_obj.browse(Transaction().context['active_ids'])
        reconciliation_ids = [x.reconciliation.id for x in lines \
                if x.reconciliation]
        if reconciliation_ids:
            reconciliation_obj.delete(reconciliation_ids)
        return 'end'

UnreconcileLines()


class OpenReconcileLinesStart(ModelView):
    'Open Reconcile Lines'
    _name = 'account.move.open_reconcile_lines.start'
    _description = __doc__
    account = fields.Many2One('account.account', 'Account', required=True,
            domain=[('kind', '!=', 'view'), ('reconcile', '=', True)])

OpenReconcileLinesStart()


class OpenReconcileLines(Wizard):
    'Open Reconcile Lines'
    _name = 'account.move.open_reconcile_lines'

    start = StateView('account.move.open_reconcile_lines.start',
        'account.open_reconcile_lines_start_view_form', [
            Button('Cancel', 'end', 'tryton-cancel'),
            Button('Open', 'open_', 'tryton-ok', default=True),
            ])
    open_ = StateAction('account.act_move_line_form')

    def do_open_(self, session, action):
        action['pyson_domain'] = PYSONEncoder().encode([
            ('account', '=', session.start.account.id),
            ('reconciliation', '=', None),
            ])
        return action, {}

OpenReconcileLines()


class FiscalYearLine(ModelSQL):
    'Fiscal Year - Move Line'
    _name = 'account.fiscalyear-account.move.line'
    _table = 'account_fiscalyear_line_rel'
    _description = __doc__
    fiscalyear = fields.Many2One('account.fiscalyear', 'Fiscal Year',
            ondelete='CASCADE', select=True)
    line = fields.Many2One('account.move.line', 'Line', ondelete='RESTRICT',
            select=True, required=True)

FiscalYearLine()


class FiscalYear(ModelSQL, ModelView):
    _name = 'account.fiscalyear'
    close_lines = fields.Many2Many('account.fiscalyear-account.move.line',
            'fiscalyear', 'line', 'Close Lines')

FiscalYear()


class PrintGeneralJournalStart(ModelView):
    'Print General Journal'
    _name = 'account.move.print_general_journal.start'
    _description = __doc__
    from_date = fields.Date('From Date', required=True)
    to_date = fields.Date('To Date', required=True)
    company = fields.Many2One('company.company', 'Company', required=True)
    posted = fields.Boolean('Posted Move', help='Show only posted move')

    def default_from_date(self):
        date_obj = Pool().get('ir.date')
        return datetime.date(date_obj.today().year, 1, 1)

    def default_to_date(self):
        date_obj = Pool().get('ir.date')
        return date_obj.today()

    def default_company(self):
        return Transaction().context.get('company')

    def default_posted(self):
        return False

PrintGeneralJournalStart()


class PrintGeneralJournal(Wizard):
    'Print General Journal'
    _name = 'account.move.print_general_journal'

    start = StateView('account.move.print_general_journal.start',
        'account.print_general_journal_start_view_form', [
            Button('Cancel', 'end', 'tryton-cancel'),
            Button('Print', 'print_', 'tryton-print', default=True),
            ])
    print_ = StateAction('account.report_general_journal')

    def do_print_(self, session, action):
        data = {
            'company': session.start.company.id,
            'from_date': session.start.from_date,
            'to_date': session.start.to_date,
            'posted': session.start.posted,
            }
        return action, data

PrintGeneralJournal()


class GeneralJournal(Report):
    _name = 'account.move.general_journal'

    def _get_objects(self, ids, model, data):
        move_obj = Pool().get('account.move')

        clause = [
            ('date', '>=', data['from_date']),
            ('date', '<=', data['to_date']),
            ]
        if data['posted']:
            clause.append(('state', '=', 'posted'))
        move_ids = move_obj.search(clause,
                order=[('date', 'ASC'), ('reference', 'ASC'), ('id', 'ASC')])
        return move_obj.browse(move_ids)

    def parse(self, report, objects, data, localcontext):
        company_obj = Pool().get('company.company')

        company = company_obj.browse(data['company'])

        localcontext['company'] = company
        localcontext['digits'] = company.currency.digits
        localcontext['from_date'] = data['from_date']
        localcontext['to_date'] = data['to_date']

        return super(GeneralJournal, self).parse(report, objects, data,
            localcontext)

GeneralJournal()
