Trust-Code/odoo-brasil

View on GitHub
br_account/models/account_tax.py

Summary

Maintainability
F
5 days
Test Coverage
# -*- coding: utf-8 -*-
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).


from odoo import api, fields, models


class AccountChartTemplate(models.Model):
    _inherit = 'account.chart.template'

    @api.multi
    def _load_template(self, company, code_digits=None,
                       transfer_account_id=None, account_ref=None,
                       taxes_ref=None):
        acc_ref, tax_ref = super(AccountChartTemplate, self)._load_template(
            company, code_digits, transfer_account_id, account_ref, taxes_ref)

        tax_tmpl_obj = self.env['account.tax.template']
        tax_obj = self.env['account.tax']
        for key, value in tax_ref.items():
            tax_tmpl_id = tax_tmpl_obj.browse(key)
            tax_obj.browse(value).write({
                'deduced_account_id': acc_ref.get(
                    tax_tmpl_id.deduced_account_id.id, False),
                'refund_deduced_account_id': acc_ref.get(
                    tax_tmpl_id.refund_deduced_account_id.id, False)
            })
        return acc_ref, tax_ref


class AccountTaxTemplate(models.Model):
    _inherit = 'account.tax.template'

    deduced_account_id = fields.Many2one(
        'account.account.template', string=u"Conta de Dedução da Venda")
    refund_deduced_account_id = fields.Many2one(
        'account.account.template', string=u"Conta de Dedução do Reembolso")
    domain = fields.Selection([('icms', 'ICMS'),
                               ('icmsst', 'ICMS ST'),
                               ('simples', 'Simples Nacional'),
                               ('pis', 'PIS'),
                               ('cofins', 'COFINS'),
                               ('ipi', 'IPI'),
                               ('issqn', 'ISSQN'),
                               ('ii', 'II'),
                               ('icms_inter', u'Difal - Alíquota Inter'),
                               ('icms_intra', u'Difal - Alíquota Intra'),
                               ('fcp', 'FCP'),
                               ('csll', 'CSLL'),
                               ('irrf', 'IRRF'),
                               ('inss', 'INSS'),
                               ('outros', 'Outros')], string="Tipo")
    amount_type = fields.Selection(selection_add=[('icmsst', 'ICMS ST')])

    def _get_tax_vals(self, company):
        res = super(AccountTaxTemplate, self)._get_tax_vals(company)
        res['domain'] = self.domain
        res['amount_type'] = self.amount_type
        return res


class AccountTax(models.Model):
    _inherit = 'account.tax'

    deduced_account_id = fields.Many2one(
        'account.account', string=u"Conta de Dedução da Venda")
    refund_deduced_account_id = fields.Many2one(
        'account.account', string=u"Conta de Dedução do Reembolso")
    domain = fields.Selection([('icms', 'ICMS'),
                               ('icmsst', 'ICMS ST'),
                               ('simples', 'Simples Nacional'),
                               ('pis', 'PIS'),
                               ('cofins', 'COFINS'),
                               ('ipi', 'IPI'),
                               ('issqn', 'ISSQN'),
                               ('ii', 'II'),
                               ('icms_inter', u'Difal - Alíquota Inter'),
                               ('icms_intra', u'Difal - Alíquota Intra'),
                               ('fcp', 'FCP'),
                               ('csll', 'CSLL'),
                               ('irrf', 'IRRF'),
                               ('inss', 'INSS'),
                               ('outros', 'Outros')], string="Tipo")
    amount_type = fields.Selection(selection_add=[('icmsst', 'ICMS ST')])
    difal_por_dentro = fields.Boolean(string="Calcular Difal por Dentro?")
    frete_base = fields.Boolean(string="Incluir o frete na base de cálculo?",
                                default=True)

    @api.onchange('domain')
    def _onchange_domain_tax(self):
        if self.domain in ('icms', 'simples', 'pis', 'cofins', 'issqn', 'ii',
                           'icms_inter', 'icms_intra', 'fcp'):
            self.price_include = True
            self.amount_type = 'division'
        if self.domain in ('icmsst', 'ipi'):
            self.price_include = False
            self.include_base_amount = False
            self.amount_type = 'division'
        if self.domain == 'icmsst':
            self.amount_type = 'icmsst'

    @api.onchange('deduced_account_id')
    def _onchange_deduced_account_id(self):
        self.refund_deduced_account_id = self.deduced_account_id

    def _tax_vals(self, tax):
        return {
            'id': tax.id,
            'name': tax.name,
            'sequence': tax.sequence,
            'account_id': tax.account_id.id,
            'refund_account_id': tax.refund_account_id.id,
            'analytic': tax.analytic
        }

    def _compute_ipi(self, price_base):
        ipi_tax = self.filtered(lambda x: x.domain == 'ipi')
        if not ipi_tax:
            return []
        vals = self._tax_vals(ipi_tax)
        reducao_ipi = 0.0
        if "ipi_reducao_bc" in self.env.context:
            reducao_ipi = self.env.context['ipi_reducao_bc']

        base_ipi = price_base
        if "valor_frete" in self.env.context and ipi_tax.frete_base:
            base_ipi += self.env.context["valor_frete"]
        if "valor_seguro" in self.env.context:
            base_ipi += self.env.context["valor_seguro"]
        if "outras_despesas" in self.env.context:
            base_ipi += self.env.context["outras_despesas"]

        base_tax = base_ipi * (1 - (reducao_ipi / 100.0))

        if 'ipi_base_calculo_manual' in self.env.context and\
                self.env.context['ipi_base_calculo_manual'] > 0:
            base_tax = self.env.context['ipi_base_calculo_manual']

        vals['base'] = base_tax
        vals['amount'] = ipi_tax._compute_amount(base_tax, 1.0)
        return [vals]

    def _compute_icms(self, price_base, ipi_value):
        icms_tax = self.filtered(lambda x: x.domain == 'icms')
        if not icms_tax:
            return []
        vals = self._tax_vals(icms_tax)
        base_icms = price_base
        incluir_ipi = False
        reducao_icms = 0.0
        if 'incluir_ipi_base' in self.env.context:
            incluir_ipi = self.env.context['incluir_ipi_base']
        if "icms_aliquota_reducao_base" in self.env.context:
            reducao_icms = self.env.context['icms_aliquota_reducao_base']

        if incluir_ipi:
            base_icms += ipi_value
        if "valor_frete" in self.env.context:
            base_icms += self.env.context["valor_frete"]
        if "valor_seguro" in self.env.context:
            base_icms += self.env.context["valor_seguro"]
        if "outras_despesas" in self.env.context:
            base_icms += self.env.context["outras_despesas"]

        base_icms *= 1 - (reducao_icms / 100.0)

        if 'icms_base_calculo_manual' in self.env.context and\
                self.env.context['icms_base_calculo_manual'] > 0:
            vals['amount'] = icms_tax._compute_amount(
                self.env.context['icms_base_calculo_manual'], 1.0)
            vals['base'] = self.env.context['icms_base_calculo_manual']
        else:
            vals['amount'] = icms_tax._compute_amount(base_icms, 1.0)
            vals['base'] = base_icms
        return [vals]

    def _compute_icms_st(self, price_base, ipi_value, icms_value):
        icmsst_tax = self.filtered(lambda x: x.domain == 'icmsst')
        if not icmsst_tax:
            return []
        vals = self._tax_vals(icmsst_tax)

        base_icmsst = price_base + ipi_value
        reducao_icmsst = 0.0
        aliquota_mva = 0.0
        if "icms_st_aliquota_reducao_base" in self.env.context:
            reducao_icmsst = self.env.context['icms_st_aliquota_reducao_base']
        if "icms_st_aliquota_mva" in self.env.context:
            aliquota_mva = self.env.context['icms_st_aliquota_mva']
        if "valor_frete" in self.env.context:
            base_icmsst += self.env.context["valor_frete"]
        if "valor_seguro" in self.env.context:
            base_icmsst += self.env.context["valor_seguro"]
        if "outras_despesas" in self.env.context:
            base_icmsst += self.env.context["outras_despesas"]

        base_icmsst *= 1 - (reducao_icmsst / 100.0)  # Redução

        deducao_st_simples = 0.0
        if "icms_st_aliquota_deducao" in self.env.context:
            deducao_st_simples = self.env.context["icms_st_aliquota_deducao"]

        if deducao_st_simples:
            icms_value = base_icmsst * (deducao_st_simples / 100.0)

        base_icmsst *= 1 + aliquota_mva / 100.0  # Aplica MVA

        if 'icms_st_base_calculo_manual' in self.env.context and\
                self.env.context['icms_st_base_calculo_manual'] > 0:
            icmsst = round(
                (self.env.context['icms_st_base_calculo_manual'] *
                 (icmsst_tax.amount / 100.0)) - icms_value, 2)
            vals['amount'] = icmsst if icmsst >= 0.0 else 0.0
            vals['base'] = self.env.context['icms_st_base_calculo_manual']
        else:
            icmsst = round(
                (base_icmsst * (icmsst_tax.amount / 100.0)) - icms_value, 2)
            vals['amount'] = icmsst if icmsst >= 0.0 else 0.0
            vals['base'] = base_icmsst
        return [vals]

    def _compute_difal(self, price_base, ipi_value):
        icms_inter = self.filtered(lambda x: x.domain == 'icms_inter')
        icms_intra = self.filtered(lambda x: x.domain == 'icms_intra')
        icms_fcp = self.filtered(lambda x: x.domain == 'fcp')
        if not icms_inter or not icms_intra:
            return []
        vals_fcp = None
        vals_inter = self._tax_vals(icms_inter)
        vals_intra = self._tax_vals(icms_intra)
        if icms_fcp:
            vals_fcp = self._tax_vals(icms_fcp)
        base_icms = price_base + ipi_value
        reducao_icms = 0.0
        if "icms_aliquota_reducao_base" in self.env.context:
            reducao_icms = self.env.context['icms_aliquota_reducao_base']

        if "valor_frete" in self.env.context:
            base_icms += self.env.context["valor_frete"]
        if "valor_seguro" in self.env.context:
            base_icms += self.env.context["valor_seguro"]
        if "outras_despesas" in self.env.context:
            base_icms += self.env.context["outras_despesas"]

        base_icms *= 1 - (reducao_icms / 100.0)
        interestadual = icms_inter._compute_amount(base_icms, 1.0)
        vals_inter['base'] = base_icms
        vals_intra['base'] = base_icms

        if icms_inter.difal_por_dentro or icms_intra.difal_por_dentro:
            base_icms = base_icms - interestadual
            base_icms = base_icms / (1 - (icms_intra.amount) / 100)

        interno = icms_intra._compute_amount(base_icms, 1.0)

        if 'icms_aliquota_inter_part' in self.env.context:
            icms_inter_part = self.env.context["icms_aliquota_inter_part"]
        else:
            icms_inter_part = 80.0

        vals_inter['amount'] = round((interno - interestadual) *
                                     (100 - icms_inter_part) / 100, 2)
        vals_inter['base'] = base_icms
        vals_intra['amount'] = round((interno - interestadual) *
                                     icms_inter_part / 100, 2)
        vals_intra['base'] = base_icms

        taxes = [vals_inter, vals_intra]
        if vals_fcp:
            fcp = icms_fcp._compute_amount(base_icms, 1.0)
            vals_fcp['amount'] = fcp
            vals_fcp['base'] = base_icms
            taxes += [vals_fcp]
        return taxes

    def _compute_simples(self, price_base):
        simples_tax = self.filtered(lambda x: x.domain == 'simples')
        if not simples_tax:
            return []
        taxes = []
        for tax in simples_tax:
            vals = self._tax_vals(tax)
            vals['amount'] = tax._compute_amount(price_base, 1.0)
            vals['base'] = price_base
            taxes.append(vals)
        return taxes

    def _compute_pis_cofins(self, price_base):
        pis_cofins_tax = self.filtered(lambda x: x.domain in ('pis', 'cofins'))
        if not pis_cofins_tax:
            return []
        taxes = []
        for tax in pis_cofins_tax:
            vals = self._tax_vals(tax)
            if tax.domain == 'pis':
                if 'pis_base_calculo_manual' in self.env.context and\
                        self.env.context['pis_base_calculo_manual'] > 0:
                    vals['amount'] = tax._compute_amount(
                        self.env.context['pis_base_calculo_manual'], 1.0)
                    vals['base'] = self.env.context['pis_base_calculo_manual']
                else:
                    vals['amount'] = tax._compute_amount(price_base, 1.0)
                    vals['base'] = price_base
            if tax.domain == 'cofins':
                if 'cofins_base_calculo_manual' in self.env.context and\
                        self.env.context['cofins_base_calculo_manual'] > 0:
                    vals['amount'] = tax._compute_amount(
                        self.env.context['cofins_base_calculo_manual'], 1.0)
                    vals['base'] = self.env.context[
                        'cofins_base_calculo_manual']
                else:
                    vals['amount'] = tax._compute_amount(price_base, 1.0)
                    vals['base'] = price_base
            taxes.append(vals)
        return taxes

    def _compute_ii(self, price_base):
        ii_tax = self.filtered(lambda x: x.domain == 'ii')
        if not ii_tax:
            return []
        vals = self._tax_vals(ii_tax)
        if "ii_base_calculo" in self.env.context:
            price_base = self.env.context["ii_base_calculo"]
        vals['amount'] = ii_tax._compute_amount(price_base, 1.0)
        vals['base'] = price_base
        return [vals]

    def _compute_issqn(self, price_base):
        issqn_tax = self.filtered(lambda x: x.domain == 'issqn')
        if not issqn_tax:
            return []
        vals = self._tax_vals(issqn_tax)
        vals['amount'] = issqn_tax._compute_amount(price_base, 1.0)
        vals['base'] = price_base
        return [vals]

    def _compute_retention(self, price_base):
        retention_tax = self.filtered(
            lambda x: x.domain in ('csll', 'irrf', 'inss'))
        if not retention_tax:
            return []
        taxes = []
        for tax in retention_tax:
            vals = self._tax_vals(tax)
            vals['amount'] = tax._compute_amount(price_base, 1.0)
            vals['base'] = price_base
            taxes.append(vals)
        return taxes

    @api.multi
    def compute_all(self, price_unit, currency=None, quantity=1.0,
                    product=None, partner=None):

        exists_br_tax = len(self.filtered(lambda x: x.domain)) > 0
        if not exists_br_tax:
            res = super(AccountTax, self).compute_all(
                price_unit, currency, quantity, product, partner)
            res['price_without_tax'] = round(price_unit * quantity, 2)
            return res

        price_base = price_unit * quantity
        ipi = self._compute_ipi(price_base)
        icms = self._compute_icms(
            price_base,
            ipi[0]['amount'] if ipi else 0.0)
        icmsst = self._compute_icms_st(
            price_base,
            ipi[0]['amount'] if ipi else 0.0,
            icms[0]['amount'] if icms else 0.0)
        difal = self._compute_difal(
            price_base, ipi[0]['amount'] if ipi else 0.0)

        taxes = icms + icmsst + difal + ipi
        taxes += self._compute_simples(price_base)
        taxes += self._compute_pis_cofins(price_base)
        taxes += self._compute_issqn(price_base)
        taxes += self._compute_ii(price_base)
        taxes += self._compute_retention(price_base)

        total_included = total_excluded = price_base
        for tax in taxes:
            tax_id = self.filtered(lambda x: x.id == tax['id'])
            if not tax_id.price_include:
                total_included += tax['amount']

        return {
            'taxes': sorted(taxes, key=lambda k: k['sequence']),
            'total_excluded': total_excluded,
            'total_included': total_included,
            'base': price_base,
        }