Trust-Code/odoo-brasil

View on GitHub
br_account_einvoice/models/account_invoice.py

Summary

Maintainability
C
1 day
Test Coverage
# -*- coding: utf-8 -*-
# © 2016 Danimar Ribeiro <danimaribeiro@gmail.com>, Trustcode
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from datetime import datetime
from random import SystemRandom

from odoo import api, fields, models
from odoo.report.render import render
from odoo.exceptions import UserError

TYPE2EDOC = {
    'out_invoice': 'saida',        # Customer Invoice
    'in_invoice': 'entrada',          # Vendor Bill
    'out_refund': 'entrada',        # Customer Refund
    'in_refund': 'saida',          # Vendor Refund
}


class external_pdf(render):
    def __init__(self, pdf):
        render.__init__(self)
        self.pdf = pdf
        self.output_type = 'pdf'

    def _render(self):
        return self.pdf


class AccountInvoice(models.Model):
    _inherit = 'account.invoice'

    @api.multi
    def _compute_total_edocs(self):
        for item in self:
            item.total_edocs = self.env['invoice.eletronic'].search_count(
                [('invoice_id', '=', item.id)])

    invoice_eletronic_ids = fields.One2many(
        'invoice.eletronic', 'invoice_id',
        u'Documentos Eletrônicos', readonly=True)
    invoice_model = fields.Char(
        string="Modelo de Fatura", related="fiscal_document_id.code",
        readonly=True)
    total_edocs = fields.Integer(string="Total NFe",
                                 compute=_compute_total_edocs)
    internal_number = fields.Integer(
        'Invoice Number', readonly=True,
        states={'draft': [('readonly', False)]},
        help=u"""Unique number of the invoice, computed
            automatically when the invoice is created.""")

    @api.multi
    def action_view_edocs(self):
        if self.total_edocs == 1:
            dummy, act_id = self.env['ir.model.data'].get_object_reference(
                'br_account_einvoice', 'action_sped_base_eletronic_doc')
            dummy, view_id = self.env['ir.model.data'].get_object_reference(
                'br_account_einvoice', 'br_account_invoice_eletronic_form')
            vals = self.env['ir.actions.act_window'].browse(act_id).read()[0]
            vals['view_id'] = (view_id, u'sped.eletronic.doc.form')
            vals['views'][1] = (view_id, u'form')
            vals['views'] = [vals['views'][1], vals['views'][0]]
            edoc = self.env['invoice.eletronic'].search(
                [('invoice_id', '=', self.id)], limit=1)
            vals['res_id'] = edoc.id
            return vals
        else:
            dummy, act_id = self.env['ir.model.data'].get_object_reference(
                'br_account_einvoice', 'action_sped_base_eletronic_doc')
            vals = self.env['ir.actions.act_window'].browse(act_id).read()[0]
            return vals

    @api.multi
    def action_number(self):
        for invoice in self:
            if invoice.is_eletronic:
                if not invoice.document_serie_id.internal_sequence_id.id:
                    raise UserError(
                        u'Configure a sequência para a numeração da nota')

                seq_number = \
                    invoice.document_serie_id.internal_sequence_id.next_by_id()
                self.write(
                    {'internal_number': seq_number})
        return True

    def _return_pdf_invoice(self, doc):
        return None

    def action_preview_danfe(self):
        docs = self.env['invoice.eletronic'].search(
            [('invoice_id', '=', self.id)])

        if not docs:
            raise UserError(u'Não existe um E-Doc relacionado à esta fatura')

        for doc in docs:
            if doc.state not in ('done', 'cancel'):
                raise UserError('Nota Fiscal na fila de envio. Aguarde!')

        report = self._return_pdf_invoice(docs[0])
        if not report:
            raise UserError(
                'Nenhum relatório implementado para este modelo de documento')

        if not isinstance(report, str):
            return report

        action = self.env['report'].get_action(
            docs.ids, report)
        return action

    def _prepare_edoc_item_vals(self, line):
        vals = {
            'name': line.name,
            'product_id': line.product_id.id,
            'tipo_produto': line.product_type,
            'cfop': line.cfop_id.code,
            'uom_id': line.uom_id.id,
            'quantidade': line.quantity,
            'preco_unitario': line.price_unit,
            'valor_bruto': line.valor_bruto,
            'desconto': line.valor_desconto,
            'valor_liquido': line.price_subtotal,
            'origem': line.icms_origem,
            'tributos_estimados': line.tributos_estimados,
            'ncm': line.fiscal_classification_id.code,
            'item_pedido_compra': line.item_pedido_compra,
            # - ICMS -
            'icms_cst': line.icms_cst,
            'icms_aliquota': line.icms_aliquota,
            'icms_tipo_base': line.icms_tipo_base,
            'icms_aliquota_reducao_base': line.icms_aliquota_reducao_base,
            'icms_base_calculo': line.icms_base_calculo,
            'icms_valor': line.icms_valor,
            # - ICMS ST -
            'icms_st_aliquota': line.icms_st_aliquota,
            'icms_st_aliquota_mva': line.icms_st_aliquota_mva,
            'icms_st_aliquota_reducao_base': line.\
            icms_st_aliquota_reducao_base,
            'icms_st_base_calculo': line.icms_st_base_calculo,
            'icms_st_valor': line.icms_st_valor,
            'icms_pst': line.icms_pst,
            'icms_substituto': line.icms_substituto,
            'icms_st_retido': line.icms_st_retido,
            'icms_st_base_retido': line.icms_st_base_retido,
            # - Simples Nacional -
            'icms_aliquota_credito': line.icms_aliquota_credito,
            'icms_valor_credito': line.icms_valor_credito,
            # - IPI -
            'ipi_cst': line.ipi_cst,
            'ipi_aliquota': line.ipi_aliquota,
            'ipi_base_calculo': line.ipi_base_calculo,
            'ipi_reducao_bc': line.ipi_reducao_bc,
            'ipi_valor': line.ipi_valor,
            # - II -
            'ii_base_calculo': line.ii_base_calculo,
            'ii_valor_despesas': line.ii_valor_despesas,
            'ii_valor': line.ii_valor,
            'ii_valor_iof': line.ii_valor_iof,
            # - PIS -
            'pis_cst': line.pis_cst,
            'pis_aliquota': abs(line.pis_aliquota),
            'pis_base_calculo': line.pis_base_calculo,
            'pis_valor': abs(line.pis_valor),
            'pis_valor_retencao':
            abs(line.pis_valor) if line.pis_valor < 0 else 0,
            # - COFINS -
            'cofins_cst': line.cofins_cst,
            'cofins_aliquota': abs(line.cofins_aliquota),
            'cofins_base_calculo': line.cofins_base_calculo,
            'cofins_valor': abs(line.cofins_valor),
            'cofins_valor_retencao':
            abs(line.cofins_valor) if line.cofins_valor < 0 else 0,
            # - ISSQN -
            'issqn_codigo': line.service_type_id.code,
            'issqn_aliquota': abs(line.issqn_aliquota),
            'issqn_base_calculo': line.issqn_base_calculo,
            'issqn_valor': abs(line.issqn_valor),
            'issqn_valor_retencao':
            abs(line.issqn_valor) if line.issqn_valor < 0 else 0,
            # - RETENÇÔES -
            'csll_base_calculo': line.csll_base_calculo,
            'csll_aliquota': abs(line.csll_aliquota),
            'csll_valor_retencao':
            abs(line.csll_valor) if line.csll_valor < 0 else 0,
            'irrf_base_calculo': line.irrf_base_calculo,
            'irrf_aliquota': abs(line.irrf_aliquota),
            'irrf_valor_retencao':
            abs(line.irrf_valor) if line.irrf_valor < 0 else 0,
            'inss_base_calculo': line.inss_base_calculo,
            'inss_aliquota': abs(line.inss_aliquota),
            'inss_valor_retencao':
            abs(line.inss_valor) if line.inss_valor < 0 else 0,
        }
        return vals

    def _prepare_edoc_vals(self, invoice):
        num_controle = int(''.join([str(SystemRandom().randrange(9))
                                    for i in range(8)]))
        vals = {
            'invoice_id': invoice.id,
            'code': invoice.number,
            'name': u'Documento Eletrônico: nº %d' % invoice.internal_number,
            'company_id': invoice.company_id.id,
            'state': 'draft',
            'tipo_operacao': TYPE2EDOC[invoice.type],
            'model': invoice.fiscal_document_id.code,
            'serie': invoice.document_serie_id.id,
            'serie_documento': invoice.document_serie_id.code,
            'numero': invoice.internal_number,
            'numero_controle': num_controle,
            'numero_nfe': invoice.internal_number,
            'data_emissao': datetime.now(),
            'data_fatura': datetime.now(),
            'finalidade_emissao': '1',
            'partner_id': invoice.partner_id.id,
            'payment_term_id': invoice.payment_term_id.id,
            'fiscal_position_id': invoice.fiscal_position_id.id,
            'valor_icms': invoice.icms_value,
            'valor_icmsst': invoice.icms_st_value,
            'valor_ipi': invoice.ipi_value,
            'valor_pis': invoice.pis_value,
            'valor_cofins': invoice.cofins_value,
            'valor_ii': invoice.ii_value,
            'valor_bruto': invoice.total_bruto,
            'valor_desconto': invoice.total_desconto,
            'valor_final': invoice.amount_total,
            'valor_bc_icms': invoice.icms_base,
            'valor_bc_icmsst': invoice.icms_st_base,
            'valor_servicos': invoice.issqn_base,
            'valor_bc_issqn': invoice.issqn_base,
            'valor_issqn': invoice.issqn_value,
            'valor_estimado_tributos': invoice.total_tributos_estimados,
            'valor_retencao_issqn': invoice.issqn_retention,
            'valor_retencao_pis': invoice.pis_retention,
            'valor_retencao_cofins': invoice.cofins_retention,
            'valor_bc_irrf': invoice.irrf_base,
            'valor_retencao_irrf': invoice.irrf_retention,
            'valor_bc_csll': invoice.csll_base,
            'valor_retencao_csll': invoice.csll_retention,
            'valor_bc_inss': invoice.inss_base,
            'valor_retencao_inss': invoice.inss_retention,
        }

        eletronic_items = []
        for inv_line in invoice.invoice_line_ids:
            eletronic_items.append((0, 0,
                                    self._prepare_edoc_item_vals(inv_line)))

        vals['eletronic_item_ids'] = eletronic_items
        return vals

    @api.multi
    def invoice_validate(self):
        res = super(AccountInvoice, self).invoice_validate()
        self.action_number()
        for item in self:
            if item.is_eletronic and\
                    (item.issuer == '1' or
                     item.type in ['out_invoice', 'out_refund']):
                edoc_vals = self._prepare_edoc_vals(item)
                if edoc_vals:
                    eletronic = self.env['invoice.eletronic'].create(edoc_vals)
                    eletronic.validate_invoice()
                    eletronic.action_post_validate()
        return res

    @api.multi
    def action_cancel(self):
        res = super(AccountInvoice, self).action_cancel()
        for item in self:
            edocs = self.env['invoice.eletronic'].search(
                [('invoice_id', '=', item.id)])
            for edoc in edocs:
                if edoc.state == 'done':
                    raise UserError(u'Documento eletrônico emitido - Cancele o \
                                    documento para poder cancelar a fatura')
                if edoc.can_unlink():
                    edoc.unlink()
        return res


class AccountInvoiceLine(models.Model):
    _inherit = 'account.invoice.line'

    item_pedido_compra = fields.Char(
        string=u'Item do pedido de compra do cliente')