Example #1
0
    def action_verify_transaction(self):
        if self.acquirer_id.provider != "apiboletointer":
            return
        if not self.acquirer_reference:
            raise UserError(
                "Esta transação não foi enviada a nenhum gateway de pagamento")

        with ArquivoCertificado(self.acquirer_id, "w") as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=(
                    self.acquirer_id.journal_id.bank_account_id.acc_number +
                    self.acquirer_id.journal_id.bank_account_id.acc_number_dig
                ),
            )
            data = self.api.boleto_recupera(self.acquirer_reference)

        # EMABERTO, BAIXADO e VENCIDO e PAGO
        if "errors" in data or not data:
            raise UserError(data)
        if data["situacao"] == "EMABERTO" and self.state in ("draft"):
            self._set_transaction_pending()

        if data["situacao"] == "PAGO" and self.state not in ("done",
                                                             "authorized"):
            self._set_transaction_done()
            self._post_process_after_done()
Example #2
0
    def generate_pdf_boleto(self):
        """
        Creates a new attachment with the Boleto PDF
        """

        # _logger.error("my acquirer_reference : %r", self.acquirer_reference)

        if self.acquirer_reference and self.pdf_boleto_id:
            return

        with ArquivoCertificado(self.acquirer_id, "w") as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=(
                    self.acquirer_id.journal_id.bank_account_id.acc_number +
                    self.acquirer_id.journal_id.bank_account_id.acc_number_dig
                ),
            )
            datas = self.api.boleto_pdf(self.acquirer_reference)
            if self._isBase64(datas):
                self.pdf_boleto_id = self.env["ir.attachment"].create({
                    # "res_model": "payment.transaction",
                    "name": ("Boleto %s.pdf" % self.display_name),
                    "datas":
                    datas,
                    "type":
                    "binary",
                })
            else:
                raise ValidationError(
                    "Cannot download invalid base64 (.pdf) file")
Example #3
0
    def generate_pdf_boleto(self):
        """
        Creates a new attachment with the Boleto PDF
        """
        if self.own_number and self.pdf_boleto_id:
            return

        order_id = self.payment_line_ids[0].order_id
        with ArquivoCertificado(order_id.journal_id, 'w') as (key, cert):
            partner_bank_id = self.journal_id.bank_account_id
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=(
                    order_id.company_partner_bank_id.acc_number +
                    order_id.company_partner_bank_id.acc_number_dig))
            datas = self.api.boleto_pdf(self.own_number)
            self.pdf_boleto_id = self.env['ir.attachment'].create({
                'name': ("Boleto %s" % self.bank_payment_line_id.display_name),
                'datas':
                datas,
                'datas_fname':
                ("boleto_%s.pdf" % self.bank_payment_line_id.display_name),
                'type':
                'binary'
            })
    def action_gerar_boleto(self):
        journal = self.env['account.journal'].search([('code', '=', 'Inter')])
        with ArquivoCertificado(journal, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=journal.bank_account_id.acc_number + journal.bank_account_id.acc_number_dig
            )

            if self.pdf_boletos_id:
                raise exceptions.UserError(
                    'Já existe um boleto dessa fatura. Você pode utilizar a opção Download ou enviar por email. '
                    'Caso deseje de fato gerar um novo boleto, você pode usar a opção Atualizar Boleto, ele fará a baixa'
                    ' no banco Inter (Cancelamento) e emitirá um novo boleto!')

            if self.state == 'draft':
                raise exceptions.UserError(
                    'O status da fatura ainda é provisório. Valide a fatura para que então você '
                    'consiga gerar o boleto. Impossível gerar boletos para fatura com status Provisório!')

            data = self.dados_boleto()
            for item in data:
                # Se a fatura ainda não tiver boleto (Nosso numero), então gera.
                if not self.pdf_boletos_id:

                    if self.date_due < datetime.now().date():
                        raise exceptions.UserError(
                            'A data de vencimento da fatura é menor que a data de emissão do boleto'
                            ' (Data atual). Impossível gerar um boleto com a data de vencimento menor'
                            ' que a data de emissão!')

                    # Inclusão do boleto e envio para a API do Inter
                    # os dados do emitente e do pagador estão no dicionarios no inicio do codigo
                    resposta = self.api.boleto_inclui(item._emissao_data())

                    # Salva o nosso numero do boleto na respectiva fatura
                    self.nossonumero = resposta['nossoNumero']

                    # Seta a flag para true, indicando que foi emitido boleto dessa fatura
                    self.boleto_emitido = True

                    # Consultar a API do Inter para localizar e baixar o PDF do boleto gerado
                    # A pesquisa é feita pelo nossonumero e o boleto é codificado em base64
                    boleto_base64 = self.api.boleto_pdf(self.nossonumero)

                    self.pdf_boletos_id = self.env['ir.attachment'].create(
                        {
                            'name': (
                                    "Boleto %s" % self.display_name.replace('/', '-')),
                            'datas': boleto_base64,
                            'datas_fname': ("boleto_%s.pdf" %
                                            self.display_name.replace('/', '-')),
                            'type': 'binary'
                        }
                    )
Example #5
0
 def cancel_transaction_in_inter(self):
     if not self.acquirer_reference:
         raise UserError(
             "Esta transação não foi enviada a nenhum gateway de pagamento")
     with ArquivoCertificado(self.acquirer_id, "w") as (key, cert):
         self.api = ApiInter(
             cert=(cert, key),
             conta_corrente=(
                 self.acquirer_id.journal_id.bank_account_id.acc_number +
                 self.acquirer_id.journal_id.bank_account_id.acc_number_dig
             ),
         )
         data = self.api.boleto_baixa(self.acquirer_reference,
                                      "SUBISTITUICAO")
 def _generate_bank_inter_boleto(self):
     payment_provider = self.env['payment.acquirer'].search([('provider', '=', 'apiboletointer')])
     with ArquivoCertificado(payment_provider, 'w') as (key, cert):
         self.api = ApiInter(
             cert=(cert, key),
             conta_corrente=(self.payment_journal_id.bank_account_id.acc_number +
                             self.payment_journal_id.bank_account_id.acc_number_dig)
         )
         data = self._generate_bank_inter_boleto_data()
         for item in data:
             print(item._emissao_data())
             resposta = self.api.boleto_inclui(item._emissao_data())
         # o pacote python tem error handle que retorna para a aplicação
     return resposta
    def setUp(self):
        certificado_cert = os.environ.get('certificado_inter_cert')
        certificado_key = os.environ.get('certificado_inter_key')

        self.api = ApiInter(cert=(certificado_cert, certificado_key),
                            conta_corrente='14054310')
        self.dados = []

        myself = User(
            name='KMEE INFORMATICA LTDA',
            identifier='23130935000198',
            bank=UserBank(bankId="341",
                          branchCode="1234",
                          accountNumber="33333",
                          accountVerifier="4",
                          bankName="BANCO ITAU SA"),
        )
        now = datetime.now()
        for i in range(3):
            payer = User(name="Sacado Teste",
                         identifier="26103754097",
                         email="*****@*****.**",
                         personType="FISICA",
                         phone="35988763663",
                         address=UserAddress(
                             streetLine1="Rua dos TESTES",
                             district="CENTRO",
                             city="SAO PAULO",
                             stateCode="SP",
                             zipCode="31327130",
                             streetNumber="15",
                         ))
            slip = BoletoInter(sender=myself,
                               amount_in_cents="100.00",
                               payer=payer,
                               issue_date=now,
                               due_date=now,
                               identifier="456" + str(i),
                               instructions=[
                                   'TESTE 1',
                                   'TESTE 2',
                                   'TESTE 3',
                                   'TESTE 4',
                               ])
            self.dados.append(slip)
Example #8
0
class AccountMoveLine(models.Model):

    _inherit = 'account.move.line'

    pdf_boleto_id = fields.Many2one(comodel_name='ir.attachment',
                                    string='PDF Boleto',
                                    ondelete='cascade')

    own_number = fields.Integer(string='Nosso número', help="Nosso número")

    def generate_pdf_boleto(self):
        """
        Creates a new attachment with the Boleto PDF
        """
        if self.own_number and self.pdf_boleto_id:
            return

        order_id = self.payment_line_ids[0].order_id
        with ArquivoCertificado(order_id.journal_id, 'w') as (key, cert):
            partner_bank_id = self.journal_id.bank_account_id
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=(
                    order_id.company_partner_bank_id.acc_number +
                    order_id.company_partner_bank_id.acc_number_dig))
            datas = self.api.boleto_pdf(self.own_number)
            self.pdf_boleto_id = self.env['ir.attachment'].create({
                'name': ("Boleto %s" % self.bank_payment_line_id.display_name),
                'datas':
                datas,
                'datas_fname':
                ("boleto_%s.pdf" % self.bank_payment_line_id.display_name),
                'type':
                'binary'
            })

    def print_pdf_boleto(self):
        """
        Generates and downloads Boletos PDFs
        :return: actions.act_url
        """

        self.generate_pdf_boleto()

        if self.own_number:
            boleto_id = self.pdf_boleto_id
            base_url = self.env['ir.config_parameter'].get_param(
                'web.base.url')
            download_url = '/web/content/%s/%s?download=True' % (str(
                boleto_id.id), boleto_id.name)

            return {
                "type": "ir.actions.act_url",
                "url": str(base_url) + str(download_url),
                "target": "new",
            }
Example #9
0
 def _generate_bank_inter_boleto(self):
     with ArquivoCertificado(self.journal_id, 'w') as (key, cert):
         self.api = ApiInter(
             cert=(cert, key),
             conta_corrente=(self.company_partner_bank_id.acc_number +
                             self.company_partner_bank_id.acc_number_dig)
         )
         data = self._generate_bank_inter_boleto_data()
         for item in data:
             resposta = self.api.boleto_inclui(item._emissao_data())
             payment_line_id = self.payment_line_ids.filtered(
                 lambda line: line.document_number == item._identifier)
             if payment_line_id:
                 payment_line_id.move_line_id.own_number = \
                     resposta['nossoNumero']
             # bank_line_id.seu_numero = resposta['seuNumero']
             # bank_line_id.linha_digitavel = resposta['linhaDigitavel']
             # bank_line_id.barcode = resposta['codigoBarras']
     return False, False
    def action_atualiza_boleto(self):
        """
        Essa função faz a atualização de boleto. Basicamente ela cancela o boleto atual e gera um novo boleto atualizado.
        Provavelmente existe formas melhores de realizar essa operação, porém na API do Banco Inter existe apenas 4 endpoint
        (Emissão, consulta, baixa e PDF), então o que encontrei no momento foi cancelar com a baixa e emitir um novo boleto.

        Essa operação pode ser cobrada por boletos gerados.
        """
        journal = self.env['account.journal'].search([('code', '=', 'Inter')])
        with ArquivoCertificado(journal, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=journal.bank_account_id.acc_number + journal.bank_account_id.acc_number_dig
            )

            if not self.pdf_boletos_id:
                raise exceptions.UserError(
                    'Ainda não existe um boleto gerado dessa fatura. Impossível atualizar um '
                    'boleto inexistente!')

            if self.date < datetime.now().date() + timedelta(days=2):
                raise exceptions.UserError(
                    'O boleto em questão tem menos de dois dias que foi emitido. É provavel que '
                    'ainda não esteja registrado. Aguarde pelo menos dois dias para atualizá-lo')

            # Realiza a baixa do boleto atual
            resposta_baixa = self.api.boleto_baixa(self.nossonumero, 'SUBISTITUICAO')

            # Depois que realizo a baixo lá no Inter, eu apago a justificativa do banco de dados, porque la vai ta o nosso numero
            # do novo boleto, e ele nao foi baixado. Sei que não é a melhor forma, mas futuramente podemos criar uma tabela
            # somente para armazenar os boletos, inclusive para consultas, podemos fazer isso quando for fazer o painel de boleto.
            self.boleto_codigo_baixa = None

            # Na função de de geração de boleto, só é gerado, se não tiver pdf_boletos_id. Esse tratamento existe para
            # a pessoa não gerar duas vezes. POrém, só nosso caso de atualização, precisaremos, então seto como Null
            # e abaixo quando a função for chamada, será gerado normalmente. Eu poderia excluir o registro do boleot,
            # mas futuramente, pode ser que queremos exibi-los em consultas no peinel do boleto.
            self.pdf_boletos_id = None

            # Realiza a emissão do novo boleto
            self.action_gerar_boleto()
    def action_baixa_boleto(self):
        """
        Essa função realiza operações de Baixa de boleto na API do banco inter. Ela disponibiliza uma janela com todas
        as opções de baixa. Ai escolher o codigo da baixa e clicar em salvar, a API do inter é chamada e enviado a
        requisição.
        """

        journal = self.env['account.journal'].search([('code', '=', 'Inter')])
        with ArquivoCertificado(journal, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=journal.bank_account_id.acc_number + journal.bank_account_id.acc_number_dig
            )

            if not self.pdf_boletos_id:
                raise exceptions.UserError(
                    'Ainda não existe um boleto gerado dessa fatura. Impossível imprimir/baixar pdf '
                    'de um boleto inexistente!')

            if abs((self.date_invoice - datetime.now().date()).days) <= 2:
                raise exceptions.UserError(
                    'O boleto em questão tem menos de dois dias que foi emitido. É provavel que '
                    'ainda não esteja registrado. Aguarde pelo menos dois dias para realizar a baixa')

            # resposta = self.api.boleto_baixa(self.nossonumero, self.boleto_codigo_baixa)

            view_id = self.env.ref('sismais_account_payment_inter_boleto.baixa_boleto_invoice_form').id
            context = self._context.copy()
            return {
                'name': 'Baixa Boleto Inter',
                'view_type': 'form',
                'view_mode': 'tree',
                'views': [(view_id, 'form')],
                'res_model': 'account.invoice',
                'view_id': view_id,
                'type': 'ir.actions.act_window',
                'res_id': self.id,
                'target': 'new',
                'context': context,
            }
    def baixa_recorrente_boleto(self):
        """
        O Odoo possui um proprio sistema de Cron, usarei ele para acionar essa função diariamente.
        O cron pode ser feito na propria interface do Odoo ou definida por XML, nesse caso, usuario um XML,
        Assim que o modulo for instalado, o cron será criado.
        """

        journal = self.env['account.journal'].search([('code', '=', 'Inter')])
        with ArquivoCertificado(journal, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=journal.bank_account_id.acc_number + journal.bank_account_id.acc_number_dig
            )

            #  Data inicial usada na consulta de boleto no no formato string com hifen no lugar de ponto (AAAA-MM-DD)
            #  Uma data bem antiga para que qualquer boleto não pago, independente do tempo, venha no filtro.
            dt_inicial = '1990-01-01'

            #  Data final usada na consulta de boleto no no formato string com hifen no lugar de ponto (AAAA-MM-DD)
            dt_final = str(datetime.now().date()).replace('.', '-')

            resposta = self.api.boleto_consulta('PAGOS', dt_inicial, dt_final)
            for i in range(len(resposta['content'])):
                invoice = self.env['account.invoice'].search([('nossonumero', '=', resposta['content'][i]['nossoNumero'])])
                # invoice = self.env['account.invoice'].search([('nossonumero', '=', '00663999953')]) # usada para teste
                if invoice and invoice.state == 'open':
                    Payment = self.env['account.payment'].with_context(default_invoice_ids=[(4, invoice.id, False)])
                    payment = Payment.create({
                        'payment_date': datetime.now(),
                        'payment_method_id': 1,
                        'payment_type': 'inbound',
                        'partner_type': 'customer',
                        'partner_id': invoice.partner_id.id,
                        'amount': invoice.amount_total,
                        'journal_id': journal.id,
                        'company_id': invoice.company_id,
                        'currency_id': 6,
                        'payment_difference_handling': 'reconcile'
                    })
                    payment.post()
Example #13
0
class AccountPaymentOrder(models.Model):
    _inherit = 'account.payment.order'

    def _generate_bank_inter_boleto_data(self):
        dados = []
        myself = User(
            name=self.company_id.legal_name,
            identifier=misc.punctuation_rm(self.company_id.cnpj_cpf),
            bank=UserBank(
                bankId=self.company_partner_bank_id.bank_id.code_bc,
                branchCode=self.company_partner_bank_id.bra_number,
                accountNumber=self.company_partner_bank_id.acc_number,
                accountVerifier=self.company_partner_bank_id.acc_number_dig,
                bankName=self.company_partner_bank_id.bank_id.name,
            ),
        )
        for line in self.bank_line_ids:
            payer = User(
                name=line.partner_id.legal_name,
                identifier=misc.punctuation_rm(
                    line.partner_id.cnpj_cpf
                ),
                email=line.partner_id.email or '',
                personType=(
                    "FISICA" if line.partner_id.company_type == 'person'
                    else 'JURIDICA'),
                phone=misc.punctuation_rm(
                    line.partner_id.phone).replace(" ", ""),
                address=UserAddress(
                    streetLine1=line.partner_id.street or '',
                    district=line.partner_id.district or '',
                    city=line.partner_id.city_id.name or '',
                    stateCode=line.partner_id.state_id.code or '',
                    zipCode=misc.punctuation_rm(line.partner_id.zip),
                    streetNumber=line.partner_id.street_number,
                )
            )
            slip = BoletoInter(
                sender=myself,
                amount=line.amount_currency,
                payer=payer,
                issue_date=line.create_date,
                due_date=line.date,
                identifier=line.document_number,
                instructions=[
                    'TESTE 1',
                    'TESTE 2',
                    'TESTE 3',
                    'TESTE 4',
                ]
            )
            dados.append(slip)
        return dados

    def _generate_bank_inter_boleto(self):
        with ArquivoCertificado(self.journal_id, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=(self.company_partner_bank_id.acc_number +
                                self.company_partner_bank_id.acc_number_dig)
            )
            data = self._generate_bank_inter_boleto_data()
            for item in data:
                resposta = self.api.boleto_inclui(item._emissao_data())
                payment_line_id = self.payment_line_ids.filtered(
                    lambda line: line.document_number == item._identifier)
                if payment_line_id:
                    payment_line_id.move_line_id.own_number = \
                        resposta['nossoNumero']
                # bank_line_id.seu_numero = resposta['seuNumero']
                # bank_line_id.linha_digitavel = resposta['linhaDigitavel']
                # bank_line_id.barcode = resposta['codigoBarras']
        return False, False

    def _gererate_bank_inter_api(self):
        """ Realiza a conexão com o a API do banco inter"""
        if self.payment_type == 'inbound':
            return self._generate_bank_inter_boleto()
        else:
            raise NotImplementedError

    
    def generate_payment_file(self):
        self.ensure_one()
        if (self.company_partner_bank_id.bank_id ==
                self.env.ref('l10n_br_base.res_bank_077') and
                self.payment_method_id.code == 'electronic'):
            return self._gererate_bank_inter_api()
        else:
            return super().generate_payment_file()
Example #14
0
class PaymentTransaction(models.Model):
    _inherit = "payment.transaction"

    transaction_url = fields.Char(string="Url de Pagamento", size=256)
    origin_move_line_id = fields.Many2one("account.move.line")
    date_maturity = fields.Date(string="Data de Vencimento")

    pdf_boleto_id = fields.Many2one(comodel_name="ir.attachment",
                                    string="PDF Boleto",
                                    ondelete="cascade")

    def _isBase64(self, sb):
        try:
            if isinstance(sb, str):
                # If there's any unicode here, an exception will be thrown and the function will return false
                sb_bytes = bytes(sb, "ascii")
                # _logger.error("my sb_bytes : %r", sb_bytes)
            elif isinstance(sb, bytes):
                sb_bytes = sb
                # _logger.error("my sb_bytes : %r", sb_bytes)
            else:
                raise ValidationError(
                    "Cannot download invalid base64 (.pdf) file")
            return base64.b64encode(base64.b64decode(sb_bytes)) == sb_bytes
        except Exception:
            raise ValidationError("Cannot download invalid base64 (.pdf) file")
            return False

    def generate_pdf_boleto(self):
        """
        Creates a new attachment with the Boleto PDF
        """

        # _logger.error("my acquirer_reference : %r", self.acquirer_reference)

        if self.acquirer_reference and self.pdf_boleto_id:
            return

        with ArquivoCertificado(self.acquirer_id, "w") as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=(
                    self.acquirer_id.journal_id.bank_account_id.acc_number +
                    self.acquirer_id.journal_id.bank_account_id.acc_number_dig
                ),
            )
            datas = self.api.boleto_pdf(self.acquirer_reference)
            if self._isBase64(datas):
                self.pdf_boleto_id = self.env["ir.attachment"].create({
                    # "res_model": "payment.transaction",
                    "name": ("Boleto %s.pdf" % self.display_name),
                    "datas":
                    datas,
                    "type":
                    "binary",
                })
            else:
                raise ValidationError(
                    "Cannot download invalid base64 (.pdf) file")

    def print_pdf_boleto(self):
        """
        Generates and downloads Boletos PDFs
        :return: actions.act_url
        """

        self.generate_pdf_boleto()

        if self.acquirer_reference:
            boleto_id = self.pdf_boleto_id
            iddoboleto = boleto_id.id
            base_url = self.env["ir.config_parameter"].get_param(
                "web.base.url")
            download_url = "/web/content/%s/%s?download=True" % (
                str(boleto_id.id),
                boleto_id.name.replace("/", "_"),
            )

            return {
                "type": "ir.actions.act_url",
                "url": str(base_url) + str(download_url),
                "target": "new",
            }

    def cron_verify_transaction(self):
        documents = self.search(
            [
                ("state", "in", ["draft", "pending"]),
            ],
            limit=50,
        )
        for doc in documents:
            try:
                doc.action_verify_transaction()
                self.env.cr.commit()
            except Exception as e:
                self.env.cr.rollback()
                _logger.exception(
                    "Payment Transaction ID {}: {}.".format(doc.id, str(e)),
                    exc_info=True,
                )

    def action_verify_transaction(self):
        if self.acquirer_id.provider != "apiboletointer":
            return
        if not self.acquirer_reference:
            raise UserError(
                "Esta transação não foi enviada a nenhum gateway de pagamento")

        with ArquivoCertificado(self.acquirer_id, "w") as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=(
                    self.acquirer_id.journal_id.bank_account_id.acc_number +
                    self.acquirer_id.journal_id.bank_account_id.acc_number_dig
                ),
            )
            data = self.api.boleto_recupera(self.acquirer_reference)

        # EMABERTO, BAIXADO e VENCIDO e PAGO
        if "errors" in data or not data:
            raise UserError(data)
        if data["situacao"] == "EMABERTO" and self.state in ("draft"):
            self._set_transaction_pending()

        if data["situacao"] == "PAGO" and self.state not in ("done",
                                                             "authorized"):
            self._set_transaction_done()
            self._post_process_after_done()
            # if self.origin_move_line_id:
            # self.origin_move_line_id._create_bank_tax_move(
            #    (data.get('taxes_paid_cents') or 0) / 100)
        # else:
        # self.iugu_status = data['status']

    def cancel_transaction_in_inter(self):
        if not self.acquirer_reference:
            raise UserError(
                "Esta transação não foi enviada a nenhum gateway de pagamento")
        with ArquivoCertificado(self.acquirer_id, "w") as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=(
                    self.acquirer_id.journal_id.bank_account_id.acc_number +
                    self.acquirer_id.journal_id.bank_account_id.acc_number_dig
                ),
            )
            data = self.api.boleto_baixa(self.acquirer_reference,
                                         "SUBISTITUICAO")

    def action_cancel_transaction(self):
        self._set_transaction_cancel()
        if self.acquirer_id.provider == "apiboletointer":
            self.cancel_transaction_in_inter()
Example #15
0
class AccountMove(models.Model):
    _inherit = 'account.move'

    @api.depends('line_ids')
    def _compute_receivables(self):
        for move in self:
            move.receivable_move_line_ids = move.line_ids.filtered(
                lambda m: m.account_id.user_type_id.type == 'receivable'
            ).sorted(key=lambda m: m.date_maturity)

    @api.depends('line_ids')
    def _compute_payables(self):
        for move in self:
            move.payable_move_line_ids = move.line_ids.filtered(
                lambda m: m.account_id.user_type_id.type == 'payable')

    receivable_move_line_ids = fields.Many2many(
        'account.move.line', string='Receivable Move Lines',
        compute='_compute_receivables')

    payable_move_line_ids = fields.Many2many(
        'account.move.line', string='Payable Move Lines',
        compute='_compute_payables')

    payment_journal_id = fields.Many2one(
        'account.journal', string='Forma de pagamento')

    def validate_data_iugu(self):
        errors = []
        for invoice in self:
            #if not invoice.payment_journal_id.receive_by_iugu:
            if not invoice.payment_mode_id.display_name == 'BOLETO INTER':
                continue
            partner = invoice.partner_id.commercial_partner_id
            #if not self.env.company.iugu_api_token:
            #if not self.payment_mode_id.fixed_journal_id.bank_inter_cert:
            #    errors.append('Configure o certificado de API')
            #if not self.payment_mode_id.fixed_journal_id.bank_inter_key:
            #    errors.append('Configure a chave de API')
            if partner.is_company and not partner.l10n_br_legal_name:
                errors.append('Destinatário - Razão Social')
            if not partner.street:
                errors.append('Destinatário / Endereço - Rua')
            if not partner.l10n_br_number:
                errors.append('Destinatário / Endereço - Número')
            if not partner.zip or len(re.sub(r"\D", "", partner.zip)) != 8:
                errors.append('Destinatário / Endereço - CEP')
            if not partner.state_id:
                errors.append(u'Destinatário / Endereço - Estado')
            if not partner.city_id:
                errors.append(u'Destinatário / Endereço - Município')
            if not partner.country_id:
                errors.append(u'Destinatário / Endereço - País')
        if len(errors) > 0:
            msg = "\n".join(
                ["Por favor corrija os erros antes de prosseguir"] + errors)
            raise ValidationError(msg)

    def send_information_to_iugu(self):
        if not self.payment_journal_id.receive_by_boletointer:
        #if not self.payment_mode_id.display_name == 'BOLETO INTER':
            return

        for moveline in self.receivable_move_line_ids:
            #self.partner_id.action_synchronize_iugu()

            #iugu_p = self.env['payment.acquirer'].search([('provider', '=', 'iugu')])
            #_acquirer = self.env['payment.acquirer'].search([('provider', '=', 'transfer')])
            payment_provider = self.env['payment.acquirer'].search([('provider', '=', 'apiboletointer')])
            transaction = self.env['payment.transaction'].sudo().create({
                'acquirer_id': payment_provider.id,
                'amount': moveline.amount_residual,
                'currency_id': moveline.move_id.currency_id.id,
                'partner_id': moveline.partner_id.id,
                'type': 'server2server',
                'date_maturity': moveline.date_maturity,
                'origin_move_line_id': moveline.id,
                'invoice_ids': [(6, 0, self.ids)]
            })
            #raise UserError(transaction)
            vals = {
                'email': self.partner_id.email,
                'due_date': moveline.date_maturity.strftime('%Y-%m-%d'),
                'ensure_workday_due_date': True,
                'items': [{
                    'description': 'Fatura Ref: %s' % moveline.name,
                    'quantity': 1,
                    'price_cents': int(moveline.amount_residual * 100),
                }],
                #'return_url': '%s/my/invoices/%s' % (base_url, self.id),
                #'notification_url': '%s/iugu/webhook?id=%s' % (base_url, self.id),
                'fines': True,
                'late_payment_fine': 2,
                'per_day_interest': True,
                'customer_id': self.partner_id,
                'early_payment_discount': False,
                'order_id': transaction.reference,
            }
            #data = iugu_invoice_api.create(vals)
            #data = None
            #if not data:
            #    data = {}
            #    data['nossoNumero'] = 123
            #    data['linhaDigitavel'] = 12345
            data = self._generate_bank_inter_boleto()

            #catch error to-do
            #if "errors" in data:
            if 0:
                if isinstance(data['errors'], str):
                    raise UserError('Erro na integração com IUGU:\n%s' % data['errors'])

                msg = "\n".join(
                    ["A integração com IUGU retornou os seguintes erros"] +
                    ["Field: %s %s" % (x[0], x[1][0])
                        for x in data['errors'].items()])
                raise UserError(msg)

            transaction.write({
                #'acquirer_reference': data['id'],
                'acquirer_reference': data['nossoNumero'] or '',
                #'transaction_url': data['secure_url'],
            })
            #transaction._set_transaction_pending()
            moveline.write({
                #'iugu_id': data['nossoNumero'] or '',
                #'iugu_secure_payment_url': data['secure_url'],
                'boleto_digitable_line': data['linhaDigitavel'] or '',
                #'iugu_barcode_url': data['bank_slip']['barcode'],
                #'transaction_ids': transaction.id,
            })
            self.transaction_ids = [(6, 0, [transaction.id])]

        ###

    def generate_payment_transactions(self):
        self.ensure_one()
        for item in self:
            item.send_information_to_iugu()

    def action_post(self):
        self.validate_data_iugu()
        result = super(AccountMove, self).action_post()
        self.generate_payment_transactions()
        #raise ValidationError('interrupção antes do post')
        return result

    #################  inter methods ###########

    def _generate_bank_inter_boleto_data(self):

        dados = []
        myself = User(
            name=self.company_id.l10n_br_legal_name,
            identifier=misc.punctuation_rm(self.company_id.l10n_br_cnpj_cpf),
            bank=UserBank(
                bankId=self.payment_mode_id.fixed_journal_id.bank_id.code_bc,
                branchCode=self.payment_mode_id.fixed_journal_id.bank_account_id.bra_number,
                accountNumber=self.payment_mode_id.fixed_journal_id.bank_account_id.acc_number,
                accountVerifier=self.payment_mode_id.fixed_journal_id.bank_account_id.acc_number_dig,
                bankName=self.payment_mode_id.fixed_journal_id.bank_id.name,
            ),
        )
        #raise ValidationError(f"{self.company_id.l10n_br_legal_name} {misc.punctuation_rm(self.company_id.l10n_br_cnpj_cpf)} {self.payment_mode_id.fixed_journal_id.bank_id.code_bc} {self.payment_mode_id.fixed_journal_id.bank_account_id.bra_number} {self.payment_mode_id.fixed_journal_id.bank_account_id.acc_number} {self.payment_mode_id.fixed_journal_id.bank_account_id.acc_number_dig} {self.payment_mode_id.fixed_journal_id.bank_id.name} ")
        for moveline in self.receivable_move_line_ids:
        #if 1:
            payer = User(
                #name=moveline.partner_id.l10n_br_legal_name,
                name=moveline.partner_id.l10n_br_legal_name or moveline.partner_id.name,
                identifier=misc.punctuation_rm(
                    moveline.partner_id.l10n_br_cnpj_cpf
                ),
                email=moveline.partner_id.email or '',
                personType=(
                    "FISICA" if moveline.partner_id.company_type == 'person'
                    else 'JURIDICA'),
                phone=misc.punctuation_rm(
                    moveline.partner_id.phone).replace(" ", "")[2:],
                address=UserAddress(
                    streetLine1=moveline.partner_id.street or '',
                    district=moveline.partner_id.l10n_br_district or '',
                    city=moveline.partner_id.city_id.name or '',
                    stateCode=moveline.partner_id.state_id.code or '',
                    zipCode=misc.punctuation_rm(moveline.partner_id.zip),
                    streetNumber=moveline.partner_id.l10n_br_number,
                )
            )
            #raise ValidationError(f"{moveline.partner_id.l10n_br_legal_name} {moveline.partner_id.l10n_br_cnpj_cpf} {moveline.partner_id.email} {moveline.partner_id.company_type} {moveline.partner_id.phone} {moveline.partner_id.street} {moveline.partner_id.l10n_br_district} {moveline.partner_id.city_id.name} {moveline.partner_id.state_id.code} {moveline.partner_id.zip} {moveline.partner_id.l10n_br_number} ")
            _instructions = str(self.invoice_payment_term_id.note).split('\n')
            invoice_payment_term_id = self.invoice_payment_term_id
            codigoMora = invoice_payment_term_id.interst_mode
            mora_valor = invoice_payment_term_id.interst_value if codigoMora == 'VALORDIA' else 0
            mora_taxa  = invoice_payment_term_id.interst_value if codigoMora == 'TAXAMENSAL' else 0
            data_mm = (moveline.date_maturity +  timedelta(days=1)).strftime('%Y-%m-%d') if not codigoMora == 'ISENTO' else ''
            codigoMulta = invoice_payment_term_id.fine_mode
            multa_valor = invoice_payment_term_id.fine_value if codigoMulta == 'VALORFIXO' else 0
            multa_taxa  = invoice_payment_term_id.fine_value if codigoMulta == 'PERCENTUAL' else 0
            mora = dict(
                codigoMora=codigoMora,
                valor=mora_valor,
                taxa=mora_taxa,
                data=data_mm
                )
            multa = dict(
                codigoMulta=codigoMulta,
                valor=multa_valor,
                taxa=multa_taxa,
                data=data_mm
                )

            slip = BoletoInter(
                sender=myself,
                #amount=moveline.amount_currency,
                amount=moveline.amount_residual,
                payer=payer,
                issue_date=moveline.date,
                due_date=moveline.date_maturity,
                identifier=moveline.move_name,
                mora=mora,
                multa=multa,
                #identifier='999999999999999',
                instructions=_instructions,
            )
            #raise ValidationError(f"{moveline.amount_residual} {moveline.create_date} {moveline.date} {moveline.move_name} ")
            dados.append(slip)
        return dados

    def _generate_bank_inter_boleto(self):
        payment_provider = self.env['payment.acquirer'].search([('provider', '=', 'apiboletointer')])
        with ArquivoCertificado(payment_provider, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=(self.payment_journal_id.bank_account_id.acc_number +
                                self.payment_journal_id.bank_account_id.acc_number_dig)
            )
            data = self._generate_bank_inter_boleto_data()
            for item in data:
                print(item._emissao_data())
                resposta = self.api.boleto_inclui(item._emissao_data())
            # o pacote python tem error handle que retorna para a aplicação
        return resposta
class GenerateBoletoInvoice(models.Model):

    _inherit = 'account.invoice'

    nossonumero = fields.Char('Nosso Numero', size=30)  # Armazena o nosso numero do boleto
    boleto_codigo_baixa = fields.Selection([
        ('ACERTOS', 'ACERTOS'),
        ('PROTESTADO', 'PROTESTADO'),
        ('DEVOLUCAO', 'DEVOLUCAO'),
        ('PROTESTOAPOSBAIXA', 'PROTESTOAPOSBAIXA'),
        ('PAGODIRETOAOCLIENTE', 'PAGODIRETOAOCLIENTE'),
        ('SUBISTITUICAO', 'SUBISTITUICAO'),
        ('FALTADESOLUCAO', 'FALTADESOLUCAO'),
        ('APEDIDODOCLIENTE', 'APEDIDODOCLIENTE')],
        string='Codigo da Baixa')  # Em caso de baixas no boleto, armazena o codigo da baixa.

    def dados_boleto(self):
        journal = self.env['account.journal'].search([('code', '=', 'Inter')])
        dados = []
        myself = User(
            name=self.company_id.legal_name,
            identifier=misc.punctuation_rm(self.company_id.cnpj_cpf),
            bank=UserBank(
                bankId=journal.bank_account_id.bank_id.bic,
                branchCode=journal.bank_account_id.bra_number,
                accountNumber=journal.bank_account_id.acc_number,
                accountVerifier=journal.bank_account_id.acc_number_dig,
                bankName=journal.bank_account_id.bank_id.name
            ),
        )
        payer = User(
            name=self.partner_id.name,
            identifier=misc.punctuation_rm(self.partner_id.cnpj_cpf),
            email=self.partner_id.email,
            personType='FISICA' if self.partner_id.company_type == 'person' else 'JURIDICA',
            phone=caracteres_rm(self.partner_id.phone),
            address=UserAddress(
                streetLine1=self.partner_id.street,
                district=self.partner_id.district,
                city=self.partner_id.city_id.name,
                stateCode=self.partner_id.state_id.code,
                zipCode=misc.punctuation_rm(self.partner_id.zip),
                streetNumber=self.partner_id.number,
            )
        )

        slip = BoletoInter(
            sender=myself,
            amount=self.amount_total,
            payer=payer,
            issue_date=datetime.now(),
            identifier=misc.punctuation_rm(self.partner_id.cnpj_cpf),
            instructions=[
                journal.instrucao1,
                journal.instrucao2,
                journal.instrucao3,
                journal.instrucao4,
            ],
            multa=dict(
                codigoMulta=journal.codigo_multa,
                data=str(self.date_due + timedelta(days=journal.dia_carencia_multa)),
                valor=0 if journal.codigo_multa == 'PERCENTUAL' else journal.valor_multa,
                taxa=0 if journal.codigo_multa == 'VALORFIXO' else journal.taxa_multa
            ),
            mora=dict(
                codigoMora=journal.codigo_mora,
                data=str(self.date_due + timedelta(days=journal.dia_carencia_mora)),
                valor=0 if journal.codigo_mora == 'TAXAMENSAL' else journal.valor_mora,
                taxa=0 if journal.codigo_mora == 'VALORFIXO' else journal.taxa_mora
            ),
            due_date=self.date_due,
        )
        dados.append(slip)
        return dados

    def action_gerar_boleto(self):
        journal = self.env['account.journal'].search([('code', '=', 'Inter')])
        with ArquivoCertificado(journal, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=journal.bank_account_id.acc_number + journal.bank_account_id.acc_number_dig
            )

            if self.pdf_boletos_id:
                raise exceptions.UserError(
                    'Já existe um boleto dessa fatura. Você pode utilizar a opção Download ou enviar por email. '
                    'Caso deseje de fato gerar um novo boleto, você pode usar a opção Atualizar Boleto, ele fará a baixa'
                    ' no banco Inter (Cancelamento) e emitirá um novo boleto!')

            if self.state == 'draft':
                raise exceptions.UserError(
                    'O status da fatura ainda é provisório. Valide a fatura para que então você '
                    'consiga gerar o boleto. Impossível gerar boletos para fatura com status Provisório!')

            data = self.dados_boleto()
            for item in data:
                # Se a fatura ainda não tiver boleto (Nosso numero), então gera.
                if not self.pdf_boletos_id:

                    if self.date_due < datetime.now().date():
                        raise exceptions.UserError(
                            'A data de vencimento da fatura é menor que a data de emissão do boleto'
                            ' (Data atual). Impossível gerar um boleto com a data de vencimento menor'
                            ' que a data de emissão!')

                    # Inclusão do boleto e envio para a API do Inter
                    # os dados do emitente e do pagador estão no dicionarios no inicio do codigo
                    resposta = self.api.boleto_inclui(item._emissao_data())

                    # Salva o nosso numero do boleto na respectiva fatura
                    self.nossonumero = resposta['nossoNumero']

                    # Seta a flag para true, indicando que foi emitido boleto dessa fatura
                    self.boleto_emitido = True

                    # Consultar a API do Inter para localizar e baixar o PDF do boleto gerado
                    # A pesquisa é feita pelo nossonumero e o boleto é codificado em base64
                    boleto_base64 = self.api.boleto_pdf(self.nossonumero)

                    self.pdf_boletos_id = self.env['ir.attachment'].create(
                        {
                            'name': (
                                    "Boleto %s" % self.display_name.replace('/', '-')),
                            'datas': boleto_base64,
                            'datas_fname': ("boleto_%s.pdf" %
                                            self.display_name.replace('/', '-')),
                            'type': 'binary'
                        }
                    )

    def action_consulta_boleto(self):
        """A implementar para ser usado na futura view do painel do boleto"""
        pass

    def action_baixa_boleto(self):
        """
        Essa função realiza operações de Baixa de boleto na API do banco inter. Ela disponibiliza uma janela com todas
        as opções de baixa. Ai escolher o codigo da baixa e clicar em salvar, a API do inter é chamada e enviado a
        requisição.
        """

        journal = self.env['account.journal'].search([('code', '=', 'Inter')])
        with ArquivoCertificado(journal, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=journal.bank_account_id.acc_number + journal.bank_account_id.acc_number_dig
            )

            if not self.pdf_boletos_id:
                raise exceptions.UserError(
                    'Ainda não existe um boleto gerado dessa fatura. Impossível imprimir/baixar pdf '
                    'de um boleto inexistente!')

            if abs((self.date_invoice - datetime.now().date()).days) <= 2:
                raise exceptions.UserError(
                    'O boleto em questão tem menos de dois dias que foi emitido. É provavel que '
                    'ainda não esteja registrado. Aguarde pelo menos dois dias para realizar a baixa')

            # resposta = self.api.boleto_baixa(self.nossonumero, self.boleto_codigo_baixa)

            view_id = self.env.ref('sismais_account_payment_inter_boleto.baixa_boleto_invoice_form').id
            context = self._context.copy()
            return {
                'name': 'Baixa Boleto Inter',
                'view_type': 'form',
                'view_mode': 'tree',
                'views': [(view_id, 'form')],
                'res_model': 'account.invoice',
                'view_id': view_id,
                'type': 'ir.actions.act_window',
                'res_id': self.id,
                'target': 'new',
                'context': context,
            }

    def action_pdf_boleto(self):

        if not self.pdf_boletos_id:
            raise exceptions.UserError(
                'Ainda não existe um boleto gerado dessa fatura. Impossível imprimir/baixar pdf '
                'de um boleto inexistente!')

        boleto_id = self.pdf_boletos_id
        base_url = self.env['ir.config_parameter'].get_param(
            'web.base.url')
        download_url = '/web/content/%s/%s?download=True' % (
            str(boleto_id.id), boleto_id.name)

        return {
            "type": "ir.actions.act_url",
            "url": str(base_url) + str(download_url),
            "target": "new",
        }

    def baixa_recorrente_boleto(self):
        """
        O Odoo possui um proprio sistema de Cron, usarei ele para acionar essa função diariamente.
        O cron pode ser feito na propria interface do Odoo ou definida por XML, nesse caso, usuario um XML,
        Assim que o modulo for instalado, o cron será criado.
        """

        journal = self.env['account.journal'].search([('code', '=', 'Inter')])
        with ArquivoCertificado(journal, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=journal.bank_account_id.acc_number + journal.bank_account_id.acc_number_dig
            )

            #  Data inicial usada na consulta de boleto no no formato string com hifen no lugar de ponto (AAAA-MM-DD)
            #  Uma data bem antiga para que qualquer boleto não pago, independente do tempo, venha no filtro.
            dt_inicial = '1990-01-01'

            #  Data final usada na consulta de boleto no no formato string com hifen no lugar de ponto (AAAA-MM-DD)
            dt_final = str(datetime.now().date()).replace('.', '-')

            resposta = self.api.boleto_consulta('PAGOS', dt_inicial, dt_final)
            for i in range(len(resposta['content'])):
                invoice = self.env['account.invoice'].search([('nossonumero', '=', resposta['content'][i]['nossoNumero'])])
                # invoice = self.env['account.invoice'].search([('nossonumero', '=', '00663999953')]) # usada para teste
                if invoice and invoice.state == 'open':
                    Payment = self.env['account.payment'].with_context(default_invoice_ids=[(4, invoice.id, False)])
                    payment = Payment.create({
                        'payment_date': datetime.now(),
                        'payment_method_id': 1,
                        'payment_type': 'inbound',
                        'partner_type': 'customer',
                        'partner_id': invoice.partner_id.id,
                        'amount': invoice.amount_total,
                        'journal_id': journal.id,
                        'company_id': invoice.company_id,
                        'currency_id': 6,
                        'payment_difference_handling': 'reconcile'
                    })
                    payment.post()

    def action_atualiza_boleto(self):
        """
        Essa função faz a atualização de boleto. Basicamente ela cancela o boleto atual e gera um novo boleto atualizado.
        Provavelmente existe formas melhores de realizar essa operação, porém na API do Banco Inter existe apenas 4 endpoint
        (Emissão, consulta, baixa e PDF), então o que encontrei no momento foi cancelar com a baixa e emitir um novo boleto.

        Essa operação pode ser cobrada por boletos gerados.
        """
        journal = self.env['account.journal'].search([('code', '=', 'Inter')])
        with ArquivoCertificado(journal, 'w') as (key, cert):
            self.api = ApiInter(
                cert=(cert, key),
                conta_corrente=journal.bank_account_id.acc_number + journal.bank_account_id.acc_number_dig
            )

            if not self.pdf_boletos_id:
                raise exceptions.UserError(
                    'Ainda não existe um boleto gerado dessa fatura. Impossível atualizar um '
                    'boleto inexistente!')

            if self.date < datetime.now().date() + timedelta(days=2):
                raise exceptions.UserError(
                    'O boleto em questão tem menos de dois dias que foi emitido. É provavel que '
                    'ainda não esteja registrado. Aguarde pelo menos dois dias para atualizá-lo')

            # Realiza a baixa do boleto atual
            resposta_baixa = self.api.boleto_baixa(self.nossonumero, 'SUBISTITUICAO')

            # Depois que realizo a baixo lá no Inter, eu apago a justificativa do banco de dados, porque la vai ta o nosso numero
            # do novo boleto, e ele nao foi baixado. Sei que não é a melhor forma, mas futuramente podemos criar uma tabela
            # somente para armazenar os boletos, inclusive para consultas, podemos fazer isso quando for fazer o painel de boleto.
            self.boleto_codigo_baixa = None

            # Na função de de geração de boleto, só é gerado, se não tiver pdf_boletos_id. Esse tratamento existe para
            # a pessoa não gerar duas vezes. POrém, só nosso caso de atualização, precisaremos, então seto como Null
            # e abaixo quando a função for chamada, será gerado normalmente. Eu poderia excluir o registro do boleot,
            # mas futuramente, pode ser que queremos exibi-los em consultas no peinel do boleto.
            self.pdf_boletos_id = None

            # Realiza a emissão do novo boleto
            self.action_gerar_boleto()
Example #17
0
class TestBancoApiInter(unittest.TestCase):
    def setUp(self):
        certificado_cert = os.environ.get('certificado_inter_cert')
        certificado_key = os.environ.get('certificado_inter_key')

        self.api = ApiInter(cert=(certificado_cert, certificado_key),
                            conta_corrente='14054310')
        self.dados = []

        myself = User(
            name='KMEE INFORMATICA LTDA',
            identifier='23130935000198',
            bank=UserBank(bankId="341",
                          branchCode="1234",
                          accountNumber="33333",
                          accountVerifier="4",
                          bankName="BANCO ITAU SA"),
        )
        now = datetime.now()
        for i in range(3):
            payer = User(name="Sacado Teste",
                         identifier="26103754097",
                         email="*****@*****.**",
                         personType="FISICA",
                         phone="35988763663",
                         address=UserAddress(
                             streetLine1="Rua dos TESTES",
                             district="CENTRO",
                             city="SAO PAULO",
                             stateCode="SP",
                             zipCode="31327130",
                             streetNumber="15",
                         ))
            slip = BoletoInter(sender=myself,
                               amount_in_cents="100.00",
                               payer=payer,
                               issue_date=now,
                               due_date=now,
                               identifier="456" + str(i),
                               instructions=[
                                   'TESTE 1',
                                   'TESTE 2',
                                   'TESTE 3',
                                   'TESTE 4',
                               ])
            self.dados.append(slip)

    def test_data(self):
        for item in self.dados:
            self.assertTrue(item._emissao_data())

    def test_boleto_api(self):
        for item in self.dados:
            resposta = self.api.boleto_inclui(item._emissao_data())
            item.nosso_numero = resposta['nossoNumero']
            item.seu_numero = resposta['seuNumero']
            item.linha_digitavel = resposta['linhaDigitavel']
            item.barcode = resposta['codigoBarras']

            self.assertListEqual(
                list(resposta.keys()),
                ['seuNumero', 'nossoNumero', 'codigoBarras', 'linhaDigitavel'],
                'Erro ao registrar boleto')

        resposta = self.api.boleto_consulta(data_inicial='2020-01-01',
                                            data_final='2020-12-01',
                                            ordenar_por='SEUNUMERO')
        self.assertTrue(resposta, 'Falha ao consultar boletos')

        for item in self.dados:
            resposta = self.api.boleto_pdf(nosso_numero=item.nosso_numero)
            self.assertTrue(resposta, 'Falha ao imprimir boleto')

        for item in self.dados:
            resposta = self.api.boleto_baixa(
                nosso_numero=item.nosso_numero,
                codigo_baixa='SUBISTITUICAO',
            )
            self.assertTrue(resposta, 'Falha ao Baixar boletos')