Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions l10n_br_account_payment_brcobranca/constants/br_cobranca.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,20 @@ def get_brcobranca_api_url(env):
)

return brcobranca_api_url


def handle_brcobranca_response(res):
if res.ok:
return True

error_msg = res.text
if "<html" in error_msg.lower():
error_msg = _(
"The BRCobranca service returned an HTML error page instead of the "
"expected response. This usually indicates a server-side crash, "
"a timeout, or a proxy configuration issue.\n\n"
"Status Code: %s",
res.status_code,
)

raise UserError(_("BRCobranca service error:\n%s") % error_msg)
20 changes: 10 additions & 10 deletions l10n_br_account_payment_brcobranca/models/account_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import base64
import io
import json
import logging
import tempfile

import requests

from odoo import _, models
from odoo.exceptions import UserError

from ..constants.br_cobranca import TIMEOUT, get_brcobranca_api_url
from ..constants.br_cobranca import (
TIMEOUT,
get_brcobranca_api_url,
handle_brcobranca_response,
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -59,10 +63,8 @@ def generate_boleto_pdf(self):

def _get_brcobranca_boleto(self, boletos):
content = json.dumps(boletos)
f = open(tempfile.mktemp(), "w")
f.write(content)
f.close()
files = {"data": open(f.name, "rb")}
data_file = io.BytesIO(content.encode("utf-8"))
files = {"data": ("boleto.json", data_file)}

brcobranca_api_url = get_brcobranca_api_url(self.env)
brcobranca_service_url = brcobranca_api_url + "/api/boleto/multi"
Expand All @@ -78,10 +80,8 @@ def _get_brcobranca_boleto(self, boletos):
timeout=TIMEOUT,
)

if str(res.status_code)[0] == "2":
pdf_string = res.content
else:
raise UserError(res.text.encode("utf-8"))
handle_brcobranca_response(res)
pdf_string = res.content

return pdf_string

Expand Down
38 changes: 16 additions & 22 deletions l10n_br_account_payment_brcobranca/models/account_move_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ class AccountMoveLine(models.Model):
# a ser usado sem a necessidade de criar um novo
cnab_returned_ref = fields.Char(string="CNAB Returned Reference", copy=False)

def _get_brcobranca_address(self, partner):
address_parts = [
f"{partner.street_name or ''} {partner.street_number or ''}".strip(),
partner.district,
partner.city_id.name,
partner.state_id.code,
]
address = ", ".join(filter(None, address_parts))
if partner.zip:
address += f" CEP:{partner.zip}"
return address[:100] # Standard limit for many banks

# see the list of brcobranca boleto fields:
# https://github.com/kivanio/brcobranca/blob/master/lib/
# brcobranca/boleto/base.rb
Expand All @@ -49,17 +61,9 @@ def send_payment(self):
"bank": bank_name_brcobranca[0],
"valor": str("%.2f" % move_line.debit),
"cedente": move_line.company_id.partner_id.legal_name,
"cedente_endereco": (move_line.company_id.partner_id.street_name or "")
+ " "
+ (move_line.company_id.partner_id.street_number or "")
+ ", "
+ (move_line.company_id.partner_id.district or "")
+ ", "
+ (move_line.company_id.partner_id.city_id.name or "")
+ " - "
+ (move_line.company_id.partner_id.state_id.code or "")
+ " "
+ ("CEP:" + move_line.company_id.partner_id.zip or ""),
"cedente_endereco": self._get_brcobranca_address(
move_line.company_id.partner_id
),
"documento_cedente": move_line.company_id.cnpj_cpf,
"sacado": move_line.partner_id.legal_name,
"sacado_documento": move_line.partner_id.cnpj_cpf,
Expand All @@ -79,17 +83,7 @@ def send_payment(self):
),
"moeda": DICT_BRCOBRANCA_CURRENCY["R$"],
"aceite": cnab_config.boleto_accept,
"sacado_endereco": (move_line.partner_id.street_name or "")
+ " "
+ (move_line.partner_id.street_number or "")
+ ", "
+ (move_line.partner_id.district or "")
+ ", "
+ (move_line.partner_id.city_id.name or "")
+ " - "
+ (move_line.partner_id.state_id.code or "")
+ " "
+ ("CEP:" + move_line.partner_id.zip or ""),
"sacado_endereco": self._get_brcobranca_address(move_line.partner_id),
"data_processamento": move_line.move_id.invoice_date.strftime(
"%Y/%m/%d"
),
Expand Down
20 changes: 14 additions & 6 deletions l10n_br_account_payment_brcobranca/models/account_payment_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
# @author Luis Felipe Mileo <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import io
import json
import logging
import tempfile

import requests
from erpbrasil.base import misc
Expand All @@ -19,6 +19,7 @@
TIMEOUT,
get_brcobranca_api_url,
get_brcobranca_bank,
handle_brcobranca_response,
)

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -171,10 +172,8 @@ def generate_payment_file(self):

def _get_brcobranca_remessa(self, bank_brcobranca, remessa_values, cnab_type):
content = json.dumps(remessa_values)
f = open(tempfile.mktemp(), "w")
f.write(content)
f.close()
files = {"data": open(f.name, "rb")}
data_file = io.BytesIO(content.encode("utf-8"))
files = {"data": ("remessa.json", data_file)}

brcobranca_api_url = get_brcobranca_api_url(self.env)
# EX.: "http://boleto_cnab_api:9292/api/remessa"
Expand All @@ -194,6 +193,8 @@ def _get_brcobranca_remessa(self, bank_brcobranca, remessa_values, cnab_type):
timeout=TIMEOUT,
)

handle_brcobranca_response(res)

if cnab_type == "240" and "R01" in res.text[242:254]:
# Todos os header de lote cnab 240 tem conteúdo: R01,
# verificar observações G025 e G028 do manual cnab 240 febraban.
Expand All @@ -206,7 +207,14 @@ def _get_brcobranca_remessa(self, bank_brcobranca, remessa_values, cnab_type):
# https://github.com/kivanio/brcobranca/tree/master/spec/fixtures/remessa
remessa = res.content
else:
raise ValidationError(res.text)
raise ValidationError(
_(
"The BRCobranca service returned a success status, but the "
"content of the generated CNAB file is invalid or unexpected.\n\n"
"Response content: %s",
res.text[:500],
)
)

return remessa

Expand Down
16 changes: 10 additions & 6 deletions l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@

import requests

from odoo.exceptions import UserError

from odoo.addons.account_move_base_import.parser.file_parser import FileParser

from ..constants.br_cobranca import TIMEOUT, get_brcobranca_api_url
from ..constants.br_cobranca import (
TIMEOUT,
get_brcobranca_api_url,
handle_brcobranca_response,
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -94,8 +96,7 @@ def _get_brcobranca_retorno(self, files):
timeout=TIMEOUT,
)

if res.status_code != 201:
raise UserError(res.text)
handle_brcobranca_response(res)

string_result = res.json()
data = json.loads(string_result)
Expand Down Expand Up @@ -239,7 +240,10 @@ def process_return_file(self, data):
{
"occurrences": descricao_ocorrencia,
"occurrence_date": data_ocorrencia,
"str_motiv_a": " * - BOLETO NÃO ENCONTRADO.",
"str_motiv_a": (
f" * - BOLETO NÃO ENCONTRADO para o "
f"Nosso Número {linha_cnab['nosso_numero']}."
),
"own_number": linha_cnab["nosso_numero"],
"your_number": linha_cnab["documento_numero"],
"title_value": valor_titulo,
Expand Down