#This file is part of Tryton.  The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from trytond.model import fields
from trytond.pyson import Eval
from trytond.pool import Pool, PoolMeta

__all__ = ['FiscalYear', 'Period', 'Move', 'Reconciliation']
__metaclass__ = PoolMeta


class FiscalYear:
    __name__ = 'account.fiscalyear'
    out_invoice_sequence = fields.Many2One('ir.sequence.strict',
        'Customer Invoice Sequence', required=True,
        domain=[
            ('code', '=', 'account.invoice'),
            ['OR',
                ('company', '=', Eval('company')),
                ('company', '=', None),
                ],
            ],
        context={
            'code': 'account.invoice',
            'company': Eval('company'),
            },
        depends=['company'])
    in_invoice_sequence = fields.Many2One('ir.sequence.strict',
        'Supplier Invoice Sequence', required=True,
        domain=[
            ('code', '=', 'account.invoice'),
            ['OR',
                ('company', '=', Eval('company')),
                ('company', '=', None),
                ],
            ],
        context={
            'code': 'account.invoice',
            'company': Eval('company'),
            },
        depends=['company'])
    out_credit_note_sequence = fields.Many2One('ir.sequence.strict',
        'Customer Credit Note Sequence', required=True,
        domain=[
            ('code', '=', 'account.invoice'),
            ['OR',
                ('company', '=', Eval('company')),
                ('company', '=', None),
                ],
            ],
        context={
            'code': 'account.invoice',
            'company': Eval('company'),
            }, depends=['company'])
    in_credit_note_sequence = fields.Many2One('ir.sequence.strict',
        'Supplier Credit Note Sequence', required=True,
        domain=[
            ('code', '=', 'account.invoice'),
            ['OR',
                ('company', '=', Eval('company')),
                ('company', '=', None),
                ],
            ],
        context={
            'code': 'account.invoice',
            'company': Eval('company'),
            }, depends=['company'])

    @classmethod
    def __setup__(cls):
        super(FiscalYear, cls).__setup__()
        cls._error_messages.update({
                'change_invoice_sequence': 'You can not change '
                    'invoice sequence in fiscal year "%s" because there are '
                    'already posted invoices in this fiscal year.',
                'different_invoice_sequence': 'Fiscal year "%(first)s" and '
                    '"%(second)s" have the same invoice sequence.',
                })

    @classmethod
    def validate(cls, years):
        super(FiscalYear, cls).validate(years)
        for year in years:
            year.check_invoice_sequences()

    def check_invoice_sequences(self):
        for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
                'out_credit_note_sequence', 'in_credit_note_sequence'):
            fiscalyears = self.search([
                    (sequence, '=', getattr(self, sequence).id),
                    ('id', '!=', self.id),
                    ])
            if fiscalyears:
                self.raise_user_error('different_invoice_sequences', {
                        'first': self.rec_name,
                        'second': fiscalyears[0].rec_name,
                        })

    @classmethod
    def write(cls, fiscalyears, vals):
        Invoice = Pool().get('account.invoice')

        for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
                'out_credit_note_sequence', 'in_credit_note_sequence'):
            if vals.get(sequence):
                for fiscalyear in fiscalyears:
                    if (getattr(fiscalyear, sequence)
                            and (getattr(fiscalyear, sequence).id !=
                                vals[sequence])):
                        if Invoice.search([
                                    ('invoice_date', '>=',
                                        fiscalyear.start_date),
                                    ('invoice_date', '<=',
                                        fiscalyear.end_date),
                                    ('number', '!=', None),
                                    ('type', '=', sequence[:-9]),
                                    ]):
                            cls.raise_user_error('change_invoice_sequence',
                                (fiscalyear.rec_name,))
        super(FiscalYear, cls).write(fiscalyears, vals)


class Period:
    __name__ = 'account.period'
    out_invoice_sequence = fields.Many2One('ir.sequence.strict',
        'Customer Invoice Sequence',
        domain=[('code', '=', 'account.invoice')],
        context={'code': 'account.invoice'},
        states={
            'invisible': Eval('type') != 'standard',
            },
        depends=['type'])
    in_invoice_sequence = fields.Many2One('ir.sequence.strict',
        'Supplier Invoice Sequence',
        domain=[('code', '=', 'account.invoice')],
        context={'code': 'account.invoice'},
        states={
            'invisible': Eval('type') != 'standard',
            },
        depends=['type'])
    out_credit_note_sequence = fields.Many2One('ir.sequence.strict',
        'Customer Credit Note Sequence',
        domain=[('code', '=', 'account.invoice')],
        context={'code': 'account.invoice'},
        states={
            'invisible': Eval('type') != 'standard',
            },
        depends=['type'])
    in_credit_note_sequence = fields.Many2One('ir.sequence.strict',
        'Supplier Credit Note Sequence',
        domain=[('code', '=', 'account.invoice')],
        context={'code': 'account.invoice'},
        states={
            'invisible': Eval('type') != 'standard',
            },
        depends=['type'])

    @classmethod
    def __setup__(cls):
        super(Period, cls).__setup__()
        cls._error_messages.update({
                'change_invoice_sequence': 'You can not change the invoice '
                    'sequence in period "%s" because there is already an '
                    'invoice posted in this period',
                'different_invoice_sequence': 'Period "%(first)s" and '
                    '"%(second)s" have the same invoice sequence.',
                'different_period_fiscalyear_company': 'Period "%(period)s" '
                    'must have the same company as its fiscal year '
                    '(%(fiscalyear)s).'
                })

    @classmethod
    def validate(cls, periods):
        super(Period, cls).validate(periods)
        for period in periods:
            period.check_invoice_sequences()

    def check_invoice_sequences(self):
        for sequence_name in ('out_invoice_sequence', 'in_invoice_sequence',
                'out_credit_note_sequence', 'in_credit_note_sequence'):
            sequence = getattr(self, sequence_name)
            if not sequence:
                continue
            periods = self.search([
                    (sequence_name, '=', sequence.id),
                    ('fiscalyear', '!=', self.fiscalyear.id),
                    ])
            if periods:
                self.raise_user_error('different_invoice_sequence', {
                        'first': self.rec_name,
                        'second': periods[0].rec_name,
                        })
            if (sequence.company
                    and sequence.company != self.fiscalyear.company):
                self.raise_user_error('different_period_fiscalyear_company', {
                        'period': self.rec_name,
                        'fiscalyear': self.fiscalyear.rec_name,
                        })

    @classmethod
    def create(cls, vlist):
        FiscalYear = Pool().get('account.fiscalyear')
        vlist = [v.copy() for v in vlist]
        for vals in vlist:
            if vals.get('fiscalyear'):
                fiscalyear = FiscalYear(vals['fiscalyear'])
                for sequence in ('out_invoice_sequence', 'in_invoice_sequence',
                        'out_credit_note_sequence', 'in_credit_note_sequence'):
                    if not vals.get(sequence):
                        vals[sequence] = getattr(fiscalyear, sequence).id
        return super(Period, cls).create(vlist)

    @classmethod
    def write(cls, periods, vals):
        Invoice = Pool().get('account.invoice')

        for sequence_name in ('out_invoice_sequence', 'in_invoice_sequence',
                'out_credit_note_sequence', 'in_credit_note_sequence'):
            if vals.get(sequence_name):
                for period in periods:
                    sequence = getattr(period, sequence_name)
                    if (sequence and
                            sequence.id != vals[sequence]):
                        if Invoice.search([
                                    ('invoice_date', '>=', period.start_date),
                                    ('invoice_date', '<=', period.end_date),
                                    ('number', '!=', None),
                                    ('type', '=', sequence_name[:-9]),
                                    ]):
                            cls.raise_user_error('change_invoice_sequence',
                                (period.rec_name,))
        super(Period, cls).write(periods, vals)

    def get_invoice_sequence(self, invoice_type):
        sequence = getattr(self, invoice_type + '_sequence')
        if sequence:
            return sequence
        return getattr(self.fiscalyear, invoice_type + '_sequence')


class Move:
    __name__ = 'account.move'

    @classmethod
    def _get_origin(cls):
        return super(Move, cls)._get_origin() + ['account.invoice']


class Reconciliation:
    __name__ = 'account.move.reconciliation'

    @classmethod
    def create(cls, vlist):
        Invoice = Pool().get('account.invoice')
        reconciliations = super(Reconciliation, cls).create(vlist)
        move_ids = set()
        for reconciliation in reconciliations:
            move_ids |= set(l.move.id for l in reconciliation.lines)
        invoices = Invoice.search([
                ('move', 'in', list(move_ids)),
                ])
        Invoice.process(invoices)
        return reconciliations

    @classmethod
    def delete(cls, reconciliations):
        Invoice = Pool().get('account.invoice')

        move_ids = set(l.move.id for r in reconciliations for l in r.lines)
        invoices = Invoice.search([
                ('move', 'in', list(move_ids)),
                ])
        super(Reconciliation, cls).delete(reconciliations)
        Invoice.process(invoices)
