Trust-Code/odoo-brasil

View on GitHub
br_sale/models/sale.py

Summary

Maintainability
D
3 days
Test Coverage
# -*- coding: utf-8 -*-
# © 2009  Renato Lima - Akretion
# © 2012  Raphaël Valyi - Akretion
# © 2016 Danimar Ribeiro, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from datetime import timedelta

from odoo import models, fields, api
from odoo.addons import decimal_precision as dp


class SaleOrder(models.Model):
    _inherit = 'sale.order'

    @api.depends('order_line.price_total', 'order_line.valor_desconto')
    def _amount_all(self):
        super(SaleOrder, self)._amount_all()
        for order in self:
            price_total = sum(l.price_total for l in order.order_line)
            price_subtotal = sum(l.price_subtotal for l in order.order_line)
            order.update({
                'total_tax': price_total - price_subtotal,
                'total_desconto': sum(l.valor_desconto
                                      for l in order.order_line),
                'total_bruto': sum(l.valor_bruto
                                   for l in order.order_line),
            })

    @api.multi
    def _prepare_invoice(self):
        res = super(SaleOrder, self)._prepare_invoice()
        if self.fiscal_position_id and self.fiscal_position_id.account_id:
            res['account_id'] = self.fiscal_position_id.account_id.id
        if self.fiscal_position_id and self.fiscal_position_id.journal_id:
            res['journal_id'] = self.fiscal_position_id.journal_id.id
        if self.fiscal_position_id.fiscal_observation_ids:
            res['fiscal_observation_ids'] = [
                (6, None, self.fiscal_position_id.fiscal_observation_ids.ids)]
        return res

    total_bruto = fields.Float(
        string='Total Bruto ( = )', readonly=True, compute='_amount_all',
        digits=dp.get_precision('Account'), store=True)
    total_tax = fields.Float(
        string='Impostos ( + )', readonly=True, compute='_amount_all',
        digits=dp.get_precision('Account'), store=True)
    total_desconto = fields.Float(
        string='Desconto Total ( - )', readonly=True, compute='_amount_all',
        digits=dp.get_precision('Account'), store=True,
        help="The discount amount.")


class SaleOrderLine(models.Model):
    _inherit = 'sale.order.line'

    def _prepare_tax_context(self):
        return {
            'incluir_ipi_base': self.incluir_ipi_base,
            'icms_st_aliquota_mva': self.icms_st_aliquota_mva,
            'aliquota_icms_proprio': self.aliquota_icms_proprio,
            'icms_aliquota_reducao_base': self.icms_aliquota_reducao_base,
            'icms_st_aliquota_reducao_base':
            self.icms_st_aliquota_reducao_base,
            'ipi_reducao_bc': self.ipi_reducao_bc,
            'icms_st_aliquota_deducao': self.icms_st_aliquota_deducao,
        }

    @api.multi
    def _prepare_order_line_procurement(self, group_id=False):
        vals = super(SaleOrderLine, self)._prepare_order_line_procurement(
            group_id=group_id)

        confirm = fields.Date.from_string(self.order_id.confirmation_date)
        vals["date_planned"] = confirm + timedelta(days=self.customer_lead)
        return vals

    @api.depends('product_uom_qty', 'discount', 'price_unit', 'tax_id',
                 'icms_st_aliquota_mva', 'incluir_ipi_base',
                 'icms_aliquota_reducao_base', 'icms_st_aliquota_reducao_base',
                 'ipi_reducao_bc', 'icms_st_aliquota_deducao')
    def _compute_amount(self):
        for line in self:
            price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
            ctx = line._prepare_tax_context()
            tax_ids = line.tax_id.with_context(**ctx)
            taxes = tax_ids.compute_all(
                price, line.order_id.currency_id,
                line.product_uom_qty, product=line.product_id,
                partner=line.order_id.partner_id)

            valor_bruto = line.price_unit * line.product_uom_qty
            desconto = valor_bruto * line.discount / 100.0
            desconto = line.order_id.pricelist_id.currency_id.round(desconto)
            line.update({
                'price_tax': taxes['total_included'] - taxes['total_excluded'],
                'price_total': taxes['total_included'],
                'price_subtotal': taxes['total_excluded'],
                'valor_bruto': valor_bruto,
                'valor_desconto': desconto,
            })

    @api.depends('cfop_id', 'icms_st_aliquota_mva', 'aliquota_icms_proprio',
                 'incluir_ipi_base', 'icms_aliquota_reducao_base', 'tem_difal',
                 'icms_st_aliquota_reducao_base', 'ipi_reducao_bc',
                 'icms_st_aliquota_deducao')
    def _compute_detalhes(self):
        for line in self:
            msg = []
            if line.cfop_id:
                msg += [u'CFOP: %s' % line.cfop_id.code]
            msg += [u'IPI na base ICMS: %s' % (
                u'Sim' if line.incluir_ipi_base else u'Não')]
            if line.icms_st_aliquota_mva:
                msg += [u'MVA (%%): %.2f' % line.icms_st_aliquota_mva]
            if line.aliquota_icms_proprio:
                msg += [u'ICMS Intra (%%): %.2f' % line.aliquota_icms_proprio]
            if line.icms_aliquota_reducao_base:
                msg += [u'Red. Base ICMS (%%): %.2f' %
                        line.icms_aliquota_reducao_base]
            if line.icms_st_aliquota_reducao_base:
                msg += [u'Red. Base ICMS ST (%%): %.2f' %
                        line.icms_st_aliquota_reducao_base]
            if line.ipi_reducao_bc:
                msg += [u'Red. Base IPI (%%): %.2f' % line.ipi_reducao_bc]

            line.detalhes_calculo = u'\n'.join(msg)

    icms_rule_id = fields.Many2one(
        'account.fiscal.position.tax.rule', u'Regra ICMS')
    ipi_rule_id = fields.Many2one(
        'account.fiscal.position.tax.rule', u'Regra IPI')
    pis_rule_id = fields.Many2one(
        'account.fiscal.position.tax.rule', u'Regra PIS')
    cofins_rule_id = fields.Many2one(
        'account.fiscal.position.tax.rule', u'Regra COFINS')
    issqn_rule_id = fields.Many2one(
        'account.fiscal.position.tax.rule', u'Regra ISSQN')
    ii_rule_id = fields.Many2one(
        'account.fiscal.position.tax.rule', u'Regra II')

    cfop_id = fields.Many2one('br_account.cfop', string=u"CFOP")

    icms_cst_normal = fields.Char(string=u"CST ICMS", size=5)
    icms_csosn_simples = fields.Char(string=u"CSOSN ICMS", size=5)
    icms_st_aliquota_mva = fields.Float(string=u'Alíquota MVA (%)',
                                        digits=dp.get_precision('Account'))
    aliquota_icms_proprio = fields.Float(
        string=u'Alíquota ICMS Próprio (%)',
        digits=dp.get_precision('Account'))
    incluir_ipi_base = fields.Boolean(string="Incluir IPI na Base ICMS")
    icms_aliquota_reducao_base = fields.Float(
        string=u'Redução Base ICMS (%)', digits=dp.get_precision('Account'))
    icms_st_aliquota_reducao_base = fields.Float(
        string=u'Redução Base ICMS ST(%)', digits=dp.get_precision('Account'))
    icms_st_aliquota_deducao = fields.Float(
        string=u"% Dedução", help=u"Alíquota interna ou interestadual aplicada \
         sobre o valor da operação para deduzir do ICMS ST - Para empresas \
         do Simples Nacional", digits=dp.get_precision('Account'))
    tem_difal = fields.Boolean(string=u"Possui Difal")

    ipi_cst = fields.Char(string=u'CST IPI', size=5)
    ipi_reducao_bc = fields.Float(
        string=u'Redução Base IPI (%)', digits=dp.get_precision('Account'))

    pis_cst = fields.Char(string=u'CST PIS', size=5)
    cofins_cst = fields.Char(string=u'CST COFINS', size=5)

    valor_desconto = fields.Float(
        compute='_compute_amount', string=u'Vlr. Desc. (-)', store=True,
        digits=dp.get_precision('Sale Price'))
    valor_bruto = fields.Float(
        compute='_compute_amount', string=u'Vlr. Bruto', store=True,
        digits=dp.get_precision('Sale Price'))
    price_without_tax = fields.Float(
        compute='_compute_amount', string=u'Preço Base', store=True,
        digits=dp.get_precision('Sale Price'))

    detalhes_calculo = fields.Text(
        string=u"Detalhes Cálculo", compute='_compute_detalhes', store=True)

    def _update_tax_from_ncm(self):
        if self.product_id:
            ncm = self.product_id.fiscal_classification_id
            taxes = ncm.tax_icms_st_id | ncm.tax_ipi_id
            self.update({
                'icms_st_aliquota_mva': ncm.icms_st_aliquota_mva,
                'icms_st_aliquota_reducao_base':
                ncm.icms_st_aliquota_reducao_base,
                'ipi_cst': ncm.ipi_cst,
                'ipi_reducao_bc': ncm.ipi_reducao_bc,
                'tax_id': [(6, None, [x.id for x in taxes if x])]
            })

    @api.multi
    def _compute_tax_id(self):
        res = super(SaleOrderLine, self)._compute_tax_id()
        for line in self:
            line._update_tax_from_ncm()
            fpos = line.order_id.fiscal_position_id or \
                line.order_id.partner_id.property_account_position_id
            if fpos:
                vals = fpos.map_tax_extra_values(
                    line.company_id, line.product_id, line.order_id.partner_id)

                for key, value in vals.iteritems():
                    if value and key in line._fields:
                        line.update({key: value})

                empty = line.env['account.tax'].browse()
                ipi = line.tax_id.filtered(lambda x: x.domain == 'ipi')
                icmsst = line.tax_id.filtered(lambda x: x.domain == 'icmsst')
                tax_ids = vals.get('tax_icms_id', empty) | \
                    vals.get('tax_icms_st_id', icmsst) | \
                    vals.get('tax_icms_inter_id', empty) | \
                    vals.get('tax_icms_intra_id', empty) | \
                    vals.get('tax_icms_fcp_id', empty) | \
                    vals.get('tax_simples_id', empty) | \
                    vals.get('tax_ipi_id', ipi) | \
                    vals.get('tax_pis_id', empty) | \
                    vals.get('tax_cofins_id', empty) | \
                    vals.get('tax_ii_id', empty) | \
                    vals.get('tax_issqn_id', empty)

                line.update({
                    'tax_id': [(6, None, [x.id for x in tax_ids if x])]
                })

        return res

    @api.multi
    def _prepare_invoice_line(self, qty):
        res = super(SaleOrderLine, self)._prepare_invoice_line(qty)

        res['valor_desconto'] = self.valor_desconto
        res['valor_bruto'] = self.valor_bruto

        # Improve this one later
        icms = self.tax_id.filtered(lambda x: x.domain == 'icms')
        icmsst = self.tax_id.filtered(lambda x: x.domain == 'icmsst')
        icms_inter = self.tax_id.filtered(lambda x: x.domain == 'icms_inter')
        icms_intra = self.tax_id.filtered(lambda x: x.domain == 'icms_intra')
        icms_fcp = self.tax_id.filtered(lambda x: x.domain == 'icms_fcp')
        simples = self.tax_id.filtered(lambda x: x.domain == 'simples')
        ipi = self.tax_id.filtered(lambda x: x.domain == 'ipi')
        pis = self.tax_id.filtered(lambda x: x.domain == 'pis')
        cofins = self.tax_id.filtered(lambda x: x.domain == 'cofins')
        ii = self.tax_id.filtered(lambda x: x.domain == 'ii')
        issqn = self.tax_id.filtered(lambda x: x.domain == 'issqn')

        res['icms_cst_normal'] = self.icms_cst_normal
        res['icms_csosn_simples'] = self.icms_csosn_simples

        res['tax_icms_id'] = icms and icms.id or False
        res['tax_icms_st_id'] = icmsst and icmsst.id or False
        res['tax_icms_inter_id'] = icms_inter and icms_inter.id or False
        res['tax_icms_intra_id'] = icms_intra and icms_intra.id or False
        res['tax_icms_fcp_id'] = icms_fcp and icms_fcp.id or False
        res['tax_simples_id'] = simples and simples.id or False
        res['tax_ipi_id'] = ipi and ipi.id or False
        res['tax_pis_id'] = pis and pis.id or False
        res['tax_cofins_id'] = cofins and cofins.id or False
        res['tax_ii_id'] = ii and ii.id or False
        res['tax_issqn_id'] = issqn and issqn.id or False

        res['product_type'] = self.product_id.fiscal_type
        res['company_fiscal_type'] = self.company_id.fiscal_type
        res['cfop_id'] = self.cfop_id.id
        ncm = self.product_id.fiscal_classification_id
        service = self.product_id.service_type_id
        res['fiscal_classification_id'] = ncm.id
        res['service_type_id'] = service.id
        res['icms_origem'] = self.product_id.origin

        res['icms_rule_id'] = self.icms_rule_id.id
        res['ipi_rule_id'] = self.ipi_rule_id.id
        res['pis_rule_id'] = self.pis_rule_id.id
        res['cofins_rule_id'] = self.cofins_rule_id.id
        res['issqn_rule_id'] = self.issqn_rule_id.id
        res['ii_rule_id'] = self.ii_rule_id.id

        if self.product_id.fiscal_type == 'service':
            res['tributos_estimados_federais'] = \
                self.price_subtotal * (service.federal_nacional / 100)
            res['tributos_estimados_estaduais'] = \
                self.price_subtotal * (service.estadual_imposto / 100)
            res['tributos_estimados_municipais'] = \
                self.price_subtotal * (service.municipal_imposto / 100)
        else:
            federal = ncm.federal_nacional if self.product_id.origin in \
                ('1', '2', '3', '8') else ncm.federal_importado

            res['tributos_estimados_federais'] = \
                self.price_subtotal * (federal / 100)
            res['tributos_estimados_estaduais'] = \
                self.price_subtotal * (ncm.estadual_imposto / 100)
            res['tributos_estimados_municipais'] = \
                self.price_subtotal * (ncm.municipal_imposto / 100)

        res['tributos_estimados'] = res['tributos_estimados_federais'] + \
            res['tributos_estimados_estaduais'] + \
            res['tributos_estimados_municipais']

        res['incluir_ipi_base'] = self.incluir_ipi_base
        res['icms_aliquota'] = icms.amount or 0.0
        res['icms_st_aliquota_mva'] = self.icms_st_aliquota_mva
        res['icms_st_aliquota'] = icmsst.amount or 0.0
        res['icms_aliquota_reducao_base'] = self.icms_aliquota_reducao_base
        res['icms_st_aliquota_reducao_base'] = \
            self.icms_st_aliquota_reducao_base
        res['icms_st_aliquota_deducao'] = self.icms_st_aliquota_deducao
        res['tem_difal'] = self.tem_difal
        res['icms_uf_remet'] = icms_inter.amount or 0.0
        res['icms_uf_dest'] = icms_intra.amount or 0.0
        res['icms_fcp_uf_dest'] = icms_fcp.amount or 0.0

        res['ipi_cst'] = self.ipi_cst
        res['ipi_aliquota'] = ipi.amount or 0.0
        res['ipi_reducao_bc'] = self.ipi_reducao_bc

        res['pis_cst'] = self.pis_cst
        res['pis_aliquota'] = pis.amount or 0.0

        res['cofins_cst'] = self.cofins_cst
        res['cofins_aliquota'] = cofins.amount or 0.0

        res['issqn_aliquota'] = issqn.amount or 0.0

        res['ii_aliquota'] = ii.amount or 0.0
        return res