Trust-Code/odoo-brasil

View on GitHub
br_account/models/account_fiscal_position.py

Summary

Maintainability
D
1 day
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
from odoo.addons.br_account.models.cst import CST_ICMS
from odoo.addons.br_account.models.cst import CSOSN_SIMPLES
from odoo.addons.br_account.models.cst import CST_IPI
from odoo.addons.br_account.models.cst import CST_PIS_COFINS


class AccountFiscalPositionTaxRule(models.Model):
    _name = 'account.fiscal.position.tax.rule'
    _order = 'sequence'

    sequence = fields.Integer(string=u"Sequência")
    name = fields.Char(string=u"Descrição", size=100)
    domain = fields.Selection([('icms', 'ICMS'),
                               ('simples', 'Simples Nacional'),
                               ('pis', 'PIS'),
                               ('cofins', 'COFINS'),
                               ('ipi', 'IPI'),
                               ('issqn', 'ISSQN'),
                               ('ii', 'II'),
                               ('csll', 'CSLL'),
                               ('irrf', 'IRRF'),
                               ('inss', 'INSS'),
                               ('outros', 'Outros')], string=u"Tipo")
    fiscal_position_id = fields.Many2one(
        'account.fiscal.position', string=u"Posição Fiscal")

    state_ids = fields.Many2many('res.country.state', string=u"Estado Destino",
                                 domain=[('country_id.code', '=', 'BR')])
    fiscal_category_ids = fields.Many2many(
        'br_account.fiscal.category', string=u"Categorias Fiscais")
    product_category_ids = fields.Many2many(
        'product.category', string=u"Categoria de Produtos")
    tipo_produto = fields.Selection([('product', u'Produto'),
                                     ('service', u'Serviço')],
                                    string=u"Tipo produto", default="product")
    product_fiscal_classification_ids = fields.Many2many(
        'product.fiscal.classification', string=u"Classificação Fiscal",
        relation="account_fiscal_position_tax_rule_prod_fiscal_clas_relation")

    product_ids = fields.Many2many('product.product', string=u"Produtos")
    partner_ids = fields.Many2many('res.partner', string=u"Parceiros")

    cst_icms = fields.Selection(CST_ICMS, string=u"CST ICMS")
    csosn_icms = fields.Selection(CSOSN_SIMPLES, string=u"CSOSN ICMS")
    cst_pis = fields.Selection(CST_PIS_COFINS, string=u"CST PIS")
    cst_cofins = fields.Selection(CST_PIS_COFINS, string=u"CST COFINS")
    cst_ipi = fields.Selection(CST_IPI, string=u"CST IPI")
    cfop_id = fields.Many2one('br_account.cfop', string=u"CFOP")
    tax_id = fields.Many2one('account.tax', string=u"Imposto")
    tax_icms_st_id = fields.Many2one('account.tax', string=u"ICMS ST",
                                     domain=[('domain', '=', 'icmsst')])
    icms_aliquota_credito = fields.Float(string=u"% Crédito de ICMS")
    incluir_ipi_base = fields.Boolean(string=u"Incl. IPI na base ICMS")
    reducao_icms = fields.Float(string=u"Redução de base")
    reducao_icms_st = fields.Float(string=u"Redução de base ST")
    reducao_ipi = fields.Float(string=u"Redução de base IPI")
    aliquota_mva = fields.Float(string=u"Alíquota MVA")
    icms_st_aliquota_deducao = fields.Float(
        string=u"% ICMS Próprio",
        help=u"Alíquota interna ou interestadual aplicada \
         sobre o valor da operação para deduzir do ICMS ST - Para empresas \
         do Simples Nacional ou usado em casos onde existe apenas ST sem ICMS")
    tem_difal = fields.Boolean(string="Aplicar Difal?")
    tax_icms_inter_id = fields.Many2one(
        'account.tax', help=u"Alíquota utilizada na operação Interestadual",
        string=u"ICMS Inter", domain=[('domain', '=', 'icms_inter')])
    tax_icms_intra_id = fields.Many2one(
        'account.tax', help=u"Alíquota interna do produto no estado destino",
        string=u"ICMS Intra", domain=[('domain', '=', 'icms_intra')])
    tax_icms_fcp_id = fields.Many2one(
        'account.tax', string=u"% FCP", domain=[('domain', '=', 'fcp')])


class AccountFiscalPosition(models.Model):
    _inherit = 'account.fiscal.position'

    journal_id = fields.Many2one(
        'account.journal', string=u"Diário Contábil",
        help=u"Diário Contábil a ser utilizado na fatura.")
    account_id = fields.Many2one(
        'account.account', string=u"Conta Contábil",
        help=u"Conta Contábil a ser utilizada na fatura.")
    fiscal_observation_ids = fields.Many2many(
        'br_account.fiscal.observation', string=u"Mensagens Doc. Eletrônico")
    note = fields.Text(u'Observações')

    icms_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras ICMS", domain=[('domain', '=', 'icms')])
    simples_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras Simples Nacional", domain=[('domain', '=', 'simples')])
    ipi_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras IPI", domain=[('domain', '=', 'ipi')])
    pis_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras PIS", domain=[('domain', '=', 'pis')])
    cofins_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras COFINS", domain=[('domain', '=', 'cofins')])
    issqn_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras ISSQN", domain=[('domain', '=', 'issqn')])
    ii_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras II", domain=[('domain', '=', 'ii')])
    irrf_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras IRRF", domain=[('domain', '=', 'irrf')])
    csll_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras CSLL", domain=[('domain', '=', 'csll')])
    inss_tax_rule_ids = fields.One2many(
        'account.fiscal.position.tax.rule', 'fiscal_position_id',
        string=u"Regras INSS", domain=[('domain', '=', 'inss')])
    fiscal_type = fields.Selection([('saida', 'Saída'),
                                    ('entrada', 'Entrada')],
                                   string=u"Tipo da posição")

    def _filter_rules(self, fpos_id, type_tax, partner, product, state):
        rule_obj = self.env['account.fiscal.position.tax.rule']
        domain = [('fiscal_position_id', '=', fpos_id),
                  ('domain', '=', type_tax)]
        rules = rule_obj.search(domain)
        if rules:
            rules_points = {}
            for rule in rules:

                # Calcula a pontuacao da regra.
                # Quanto mais alto, mais adequada está a regra em relacao ao
                # faturamento
                rules_points[rule.id] = self._calculate_points(
                    rule, product, state, partner)

            # Calcula o maior valor para os resultados obtidos
            greater_rule = max([(v, k) for k, v in rules_points.items()])
            # Se o valor da regra for menor do que 0, a regra é descartada.
            if greater_rule[0] < 0:
                return {}

            # Procura pela regra associada ao id -> (greater_rule[1])
            rules = [rules.browse(greater_rule[1])]

            # Retorna dicionario com o valores dos campos de acordo com a regra
            return {
                ('%s_rule_id' % type_tax): rules[0],
                'cfop_id': rules[0].cfop_id,
                ('tax_%s_id' % type_tax): rules[0].tax_id,
                # ICMS
                'icms_cst_normal': rules[0].cst_icms,
                'icms_aliquota_reducao_base': rules[0].reducao_icms,
                'incluir_ipi_base': rules[0].incluir_ipi_base,
                # ICMS ST
                'tax_icms_st_id': rules[0].tax_icms_st_id,
                'icms_st_aliquota_mva': rules[0].aliquota_mva,
                'icms_st_aliquota_reducao_base': rules[0].reducao_icms_st,
                'icms_st_aliquota_deducao': rules[0].icms_st_aliquota_deducao,
                # ICMS Difal
                'tem_difal': rules[0].tem_difal,
                'tax_icms_inter_id': rules[0].tax_icms_inter_id,
                'tax_icms_intra_id': rules[0].tax_icms_intra_id,
                'tax_icms_fcp_id': rules[0].tax_icms_fcp_id,
                # Simples
                'icms_csosn_simples': rules[0].csosn_icms,
                'icms_aliquota_credito': rules[0].icms_aliquota_credito,
                # IPI
                'ipi_cst': rules[0].cst_ipi,
                'ipi_reducao_bc': rules[0].reducao_ipi,
                # PIS
                'pis_cst': rules[0].cst_pis,
                # PIS
                'cofins_cst': rules[0].cst_cofins,
            }
        else:
            return{}

    @api.model
    def map_tax_extra_values(self, company, product, partner):
        to_state = partner.state_id

        taxes = ('icms', 'simples', 'ipi', 'pis', 'cofins',
                 'issqn', 'ii', 'irrf', 'csll', 'inss')
        res = {}
        for tax in taxes:
            vals = self._filter_rules(
                self.id, tax, partner, product, to_state)
            res.update({k: v for k, v in vals.items() if v})

        return res

    def _calculate_points(self, rule, product, state, partner):
        """Calcula a pontuação das regras. A pontuação aumenta de acordo
        com os 'matches'. Não havendo match(exceto quando o campo não está
        definido) retorna o valor -1, que posteriormente será tratado como
        uma regra a ser descartada.
        """

        rule_points = 0

        # Verifica o tipo do produto. Se sim, avança para calculo da pontuação
        # Se não, retorna o valor -1 (a regra será descartada)
        if product.fiscal_type == rule.tipo_produto:

            # Verifica a categoria fiscal. Se contido, adiciona 1 ponto
            # Se não, retorna valor -1 (a regra será descartada)
            if product.categ_id in rule.product_category_ids:
                rule_points += 1
            elif len(rule.product_category_ids) > 0:
                return -1

            # Verifica a categoria fiscal. Se contido, adiciona 1 ponto
            # Se não, retorna valor -1 (a regra será descartada)
            if product.fiscal_category_id in rule.fiscal_category_ids:
                rule_points += 1
            elif len(rule.fiscal_category_ids) > 0:
                return -1

            # Verifica produtos. Se contido, adiciona 1 ponto
            # Se não, retorna -1
            if product.fiscal_classification_id in \
                    rule.product_fiscal_classification_ids:
                rule_points += 1
            elif len(rule.product_fiscal_classification_ids) > 0:
                return -1

            # Verifica produtos. Se contido, adiciona 1 ponto
            # Se não, retorna -1
            if product in rule.product_ids:
                rule_points += 1
            elif len(rule.product_ids) > 0:
                return -1

            # Verifica o estado. Se contido, adiciona 1 ponto
            # Se não, retorna -1
            if state in rule.state_ids:
                rule_points += 1
            elif len(rule.state_ids) > 0:
                return -1

            # Verifica o cliente. Se está contido, adiciona 1 ponto
            # Se não, retorna -1
            if partner in rule.partner_ids:
                rule_points += 1
            elif len(rule.partner_ids) > 0:
                return -1
        else:
            return -1

        return rule_points