OCA/l10n-italy

View on GitHub
l10n_it_ricevute_bancarie/models/account/account.py

Summary

Maintainability
C
1 day
Test Coverage
# -*- coding: utf-8 -*-
##############################################################################
#
#    Copyright (C) 2012 Andrea Cometa.
#    Email: info@andreacometa.it
#    Web site: http://www.andreacometa.it
#    Copyright (C) 2012-2015 Lorenzo Battistini - Agile Business Group
#    Copyright (C) 2012 Domsense srl (<http://www.domsense.com>)
#    Copyright (C) 2012 Associazione OpenERP Italia
#    (<http://www.odoo-italia.org>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published
#    by the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from openerp.osv import fields, orm
from openerp import api, _, models
from openerp.exceptions import Warning as UserError
import openerp.addons.decimal_precision as dp


class AccountPaymentTerm(orm.Model):
    # flag riba utile a distinguere la modalità di pagamento
    _inherit = 'account.payment.term'

    _columns = {
        'riba': fields.boolean('Riba'),
        'riba_payment_cost': fields.float(
            'RiBa Payment Cost', digits_compute=dp.get_precision('Account'),
            help="Collection fees amount. If different from 0, "
                 "for each payment deadline an invoice line will be added "
                 "to invoice, with this amount"),
    }


class ResBankAddField(orm.Model):
    _inherit = 'res.bank'
    _columns = {
        'banca_estera': fields.boolean('Banca Estera'),
    }


class ResPartnerBankAdd(orm.Model):
    _inherit = 'res.partner.bank'
    _columns = {
        'codice_sia': fields.char(
            'Codice SIA', size=5,
            help="Identification Code of the Company in the System Interbank")
    }


# se distinta_line_ids == None allora non è stata emessa
class AccountMoveLine(orm.Model):
    _inherit = "account.move.line"

    _columns = {
        'distinta_line_ids': fields.one2many(
            'riba.distinta.move.line', 'move_line_id', "Dettaglio riba"),
        'riba': fields.related(
            'invoice', 'payment_term', 'riba', type='boolean', string='RiBa',
            store=False),
        'unsolved_invoice_ids': fields.many2many(
            'account.invoice', 'invoice_unsolved_line_rel', 'line_id',
            'invoice_id', 'Unsolved Invoices'),
        'iban': fields.related(
            'partner_id', 'bank_ids', 'iban', type='char', string='IBAN',
            store=False),
    }
    _defaults = {
        'distinta_line_ids': None,
    }

    def fields_view_get(
        self, cr, uid, view_id=None, view_type='form',
        context=None, toolbar=False, submenu=False
    ):
        # Special view for account.move.line object
        # (for ex. tree view contains user defined fields)
        result = super(AccountMoveLine, self).fields_view_get(
            cr, uid, view_id, view_type, context=context, toolbar=toolbar,
            submenu=submenu)
        try:
            view_payments_tree_id = self.pool.get(
                'ir.model.data').get_object_reference(
                cr, uid, 'l10n_it_ricevute_bancarie',
                'view_riba_da_emettere_tree')
        except ValueError:
            return result
        if view_id == view_payments_tree_id[1]:
            # Use RiBa list - grazie a eLBati @ account_due_list
            return super(orm.Model, self).fields_view_get(
                cr, uid, view_id, view_type, context, toolbar=toolbar,
                submenu=submenu)
        else:
            return result

    def get_riba_lines(self):
        riba_lines = self.env['riba.distinta.line']
        return riba_lines.search([
            ('acceptance_move_id', '=', self.move_id.id)
        ])

    def update_paid_riba_lines(self):
        # set paid only if not unsolved
        if not self.env.context.get('unsolved_reconciliation'):
            riba_lines = self.get_riba_lines()
            for riba_line in riba_lines:
                # allowed transitions:
                # accredited_to_paid and accepted_to_paid. See workflow
                if riba_line.state in ['confirmed', 'accredited']:
                    if riba_line.test_reconcilied():
                        riba_line.state = 'paid'
                        riba_line.distinta_id.signal_workflow('paid')

    @api.multi
    def reconcile(
        self, type='auto', writeoff_acc_id=False,
        writeoff_period_id=False, writeoff_journal_id=False
    ):
        res = super(AccountMoveLine, self).reconcile(
            type=type, writeoff_acc_id=writeoff_acc_id,
            writeoff_period_id=writeoff_period_id,
            writeoff_journal_id=writeoff_journal_id)
        for line in self:
            line.update_paid_riba_lines()
        return res


class AccountInvoice(orm.Model):

    def _get_is_unsolved(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for invoice in self.browse(cr, uid, ids, context=context):
            res[invoice.id] = False
            reconciled_unsolved = 0
            for unsolved_move_line in invoice.unsolved_move_line_ids:
                if unsolved_move_line.reconcile_id:
                    reconciled_unsolved += 1
            if len(invoice.unsolved_move_line_ids) != reconciled_unsolved:
                res[invoice.id] = True
        return res

    def _get_invoice_by_move_line(self, cr, uid, ids, context=None):
        result = []
        for move_line in self.pool['account.move.line'].browse(
            cr, uid, ids, context=context
        ):
            result.extend([i.id for i in move_line.unsolved_invoice_ids])
        return list(set(result))

    _inherit = "account.invoice"
    _columns = {
        'unsolved_move_line_ids': fields.many2many(
            'account.move.line', 'invoice_unsolved_line_rel', 'invoice_id',
            'line_id', 'Unsolved journal items'),
        'is_unsolved': fields.function(
            _get_is_unsolved, type='boolean',
            string="The unsolved is open",
            store={
                'account.invoice': (
                    lambda self, cr, uid, ids, c={}: ids, [
                        'unsolved_move_line_ids'], 10
                ),
                'account.move.line': (_get_invoice_by_move_line, [
                    'unsolved_invoice_ids', 'reconcile_id'], 10),
            }
        ),

    }

    def month_check(self, invoice_date_due, all_date_due):
        """
        :param invoice_date_due: first date due of invoice
        :param all_date_due: list of date of dues for partner
        :return: True if month of invoice_date_due is in a list of all_date_due
        """
        for d in all_date_due:
            if invoice_date_due[:7] == d[:7]:
                return True
        return False

    @api.multi
    def action_move_create(self):
        for invoice in self:
            # ---- Add a line with payment cost for each due only for fist due
            # ---- of the month
            if invoice.type != 'out_invoice' or not invoice.payment_term \
                    or not invoice.payment_term.riba \
                    or invoice.payment_term.riba_payment_cost == 0.0:
                continue
            if not invoice.company_id.due_cost_service_id:
                raise UserError('Set a Service for Due Cost in Company Config')
            # ---- Apply Due Cost on invoice only on first due of the month
            # ---- Get Date of first due
            move_line = self.env['account.move.line'].search([
                ('partner_id', '=', invoice.partner_id.id)])
            # ---- Filtered recordset with date_maturity
            move_line = move_line.filtered(
                lambda l: l.date_maturity is not False)
            # ---- Sorted
            move_line = move_line.sorted(key=lambda r: r.date_maturity)
            # ---- Get date
            previous_date_due = move_line.mapped('date_maturity')
            pterm = self.env['account.payment.term'].browse(
                self.payment_term.id)
            pterm_list = pterm.compute(value=1, date_ref=self.date_invoice)
            for pay_date in pterm_list[0]:
                if not self.month_check(pay_date[0], previous_date_due):
                    # ---- Get Line values for service product
                    service_prod = invoice.company_id.due_cost_service_id
                    line_obj = self.env['account.invoice.line']
                    line_vals = line_obj.product_id_change(
                        service_prod.id,
                        service_prod.uom_id.id,
                        partner_id=invoice.partner_id.id,
                        qty=1,
                    )
                    # ---- Update Line Values with product,
                    # ---- invoice and due cost
                    line_vals['value'].update({
                        'product_id': service_prod.id,
                        'invoice_id': invoice.id,
                        'price_unit': invoice.payment_term.riba_payment_cost,
                        'due_cost_line': True,
                        'name': _('{line_name} for {month}-{year}').format(
                            line_name=line_vals['value']['name'],
                            month=pay_date[0][5:7],
                            year=pay_date[0][:4],
                        )
                    })
                    # ---- Update Line Value with tax if is set on product
                    if invoice.company_id.due_cost_service_id.taxes_id:
                        tax = invoice.company_id.due_cost_service_id.taxes_id
                        line_vals['value'].update({
                            'invoice_line_tax_id': [(4, tax.id)]
                        })
                    line_obj.create(line_vals['value'])
                    # ---- recompute invoice taxes
                    invoice.button_reset_taxes()
        super(AccountInvoice, self).action_move_create()

    @api.multi
    def action_cancel_draft(self):
        # ---- Delete Due Cost Line of invoice when set Back to Draft
        # ---- line was added on new validate
        for invoice in self:
            for line in invoice.invoice_line:
                if line.due_cost_line:
                    line.unlink()
        super(AccountInvoice, self).action_cancel_draft()

    @api.multi
    def action_cancel(self):
        for invoice in self:
            # we get move_lines with date_maturity and check if they are
            # present in some riba_distinta_line
            move_line_model = self.env['account.move.line']
            rdml_model = self.env['riba.distinta.move.line']
            move_line_ids = move_line_model.search([
                ('move_id', '=', invoice.move_id.id),
                ('date_maturity', '!=', False)])
            if move_line_ids:
                riba_line_ids = rdml_model.search(
                    [('move_line_id', 'in', [m.id for m in move_line_ids])])
                if riba_line_ids:
                    if len(riba_line_ids) > 1:
                        riba_line_ids = riba_line_ids[0]
                    raise UserError(
                        _('Attention!'),
                        _('Invoice is linked to RI.BA. list nr {riba}').format(
                            riba=riba_line_ids.riba_line_id.distinta_id.name
                        ))
        super(AccountInvoice, self).action_cancel()

    @api.v7
    @api.one
    def copy(self, default=None):
        # Delete Due Cost Line of invoice when copying
        res = super(AccountInvoice, self).copy(default)
        if res:
            for line in res.invoice_line:
                if line.due_cost_line:
                    line.unlink()


class AccountInvoiceLine(orm.Model):

    _inherit = 'account.invoice.line'

    _columns = {
        'due_cost_line': fields.boolean('RiBa Due Cost Line'),
    }


class AccountMoveReconcile(models.Model):
    _inherit = 'account.move.reconcile'

    def get_riba_lines(self):
        riba_lines = self.env['riba.distinta.line']
        for move_line in self.line_id:
            riba_lines |= riba_lines.search([
                ('acceptance_move_id', '=', move_line.move_id.id)
            ])
        return riba_lines

    def unreconcile_riba_lines(self, riba_lines):
        for riba_line in riba_lines:
            # allowed transitions:
            # paid_to_cancel and unsolved_to_cancel. See workflow
            if riba_line.state in ['paid', 'unsolved']:
                if not riba_line.test_reconcilied():
                    if riba_line.distinta_id.accreditation_move_id:
                        riba_line.state = 'accredited'
                        riba_line.distinta_id.signal_workflow('accredited')
                    else:
                        riba_line.state = 'confirmed'
                        riba_line.distinta_id.signal_workflow('accepted')

    @api.multi
    def unlink(self):
        riba_lines = None
        for rec in self:
            riba_lines = rec.get_riba_lines()
        res = super(AccountMoveReconcile, self).unlink()
        if riba_lines:
            self.unreconcile_riba_lines(riba_lines)
        return res