From 93f6c9e59e9a3b7dbc029f7b9bbe3ea456919637 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 17 Jun 2020 12:55:24 -0300 Subject: [PATCH 001/308] [12.0][MIG][REF] Join modules Boleto and CNAB. --- l10n_br_account_payment_brcobranca/README.rst | 79 ++++ .../__init__.py | 6 + .../__manifest__.py | 29 ++ .../data/cnab_data.xml | 12 + .../models/__init__.py | 10 + .../models/account_invoice.py | 87 +++++ .../models/account_move_line.py | 191 ++++++++++ .../models/account_payment_mode.py | 15 + .../models/account_payment_order.py | 200 ++++++++++ .../models/l10n_br_cnab.py | 352 ++++++++++++++++++ .../tests/invoice_create.yml | 213 +++++++++++ .../tests/test_remessa.REM | 6 + .../views/account_invoice_view.xml | 15 + .../views/account_payment_mode_view.xml | 16 + .../views/l10n_br_cnab_retorno_view.xml | 39 ++ 15 files changed, 1270 insertions(+) create mode 100644 l10n_br_account_payment_brcobranca/README.rst create mode 100644 l10n_br_account_payment_brcobranca/__init__.py create mode 100644 l10n_br_account_payment_brcobranca/__manifest__.py create mode 100644 l10n_br_account_payment_brcobranca/data/cnab_data.xml create mode 100644 l10n_br_account_payment_brcobranca/models/__init__.py create mode 100644 l10n_br_account_payment_brcobranca/models/account_invoice.py create mode 100644 l10n_br_account_payment_brcobranca/models/account_move_line.py create mode 100644 l10n_br_account_payment_brcobranca/models/account_payment_mode.py create mode 100644 l10n_br_account_payment_brcobranca/models/account_payment_order.py create mode 100644 l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py create mode 100644 l10n_br_account_payment_brcobranca/tests/invoice_create.yml create mode 100644 l10n_br_account_payment_brcobranca/tests/test_remessa.REM create mode 100644 l10n_br_account_payment_brcobranca/views/account_invoice_view.xml create mode 100644 l10n_br_account_payment_brcobranca/views/account_payment_mode_view.xml create mode 100644 l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml diff --git a/l10n_br_account_payment_brcobranca/README.rst b/l10n_br_account_payment_brcobranca/README.rst new file mode 100644 index 000000000000..aa277ec9984a --- /dev/null +++ b/l10n_br_account_payment_brcobranca/README.rst @@ -0,0 +1,79 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +============================================= +Odoo Brasil Account Payment Boleto BRCobranca +============================================= + +This module implement brazilian bank splips('Boletos Bancarios') by using + BRCobranca(https://github.com/kivanio/brcobranca). + + +Configuration +============= + +To configure this module, you need to: + +#. Run BRCobranca as micro-service (https://github.com/akretion/boleto_cnab_api). + +Usage +===== + +To use this module, you need to: + +#. Go to ... + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/{repo_id}/{branch} + +.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt +.. branch is "8.0" for example + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed feedback `here `_. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Raphaël Valyi (www.akretion.com.br) +* Magno Costa (www.akretion.com.br) + +Do not contact contributors directly about support or help with technical issues. + +Funders +------- + +The development of this module has been financially supported by: + +* Akretion + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/l10n_br_account_payment_brcobranca/__init__.py b/l10n_br_account_payment_brcobranca/__init__.py new file mode 100644 index 000000000000..6b195001e923 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion +# @author Raphaël Valyi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/l10n_br_account_payment_brcobranca/__manifest__.py b/l10n_br_account_payment_brcobranca/__manifest__.py new file mode 100644 index 000000000000..8682a08eb558 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/__manifest__.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion +# @author Raphaël Valyi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'L10n Br Account Payment Brcobranca', + 'description': """ + Gera Boletos, CNAB de Remessa e Retorno usando + a Gem brcobranca do Boletosimples""", + 'version': '12.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'Akretion', + 'website': 'www.akretion.com', + 'depends': [ + 'l10n_br_account_payment_cobranca', + ], + 'data': [ + 'views/account_invoice_view.xml', + 'views/account_payment_mode_view.xml', + 'views/l10n_br_cnab_retorno_view.xml', + 'data/cnab_data.xml', + ], + 'demo': [ + ], + 'test': [ + 'tests/invoice_create.yml' + ] +} diff --git a/l10n_br_account_payment_brcobranca/data/cnab_data.xml b/l10n_br_account_payment_brcobranca/data/cnab_data.xml new file mode 100644 index 000000000000..8a331242dee1 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/data/cnab_data.xml @@ -0,0 +1,12 @@ + + + + + Sequencia CNAB + Sequencia CNAB + + + + + + \ No newline at end of file diff --git a/l10n_br_account_payment_brcobranca/models/__init__.py b/l10n_br_account_payment_brcobranca/models/__init__.py new file mode 100644 index 000000000000..207e6c4b797f --- /dev/null +++ b/l10n_br_account_payment_brcobranca/models/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion +# @author Raphaël Valyi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import account_move_line +from . import account_invoice +from . import account_payment_mode +from . import account_payment_order +from . import l10n_br_cnab diff --git a/l10n_br_account_payment_brcobranca/models/account_invoice.py b/l10n_br_account_payment_brcobranca/models/account_invoice.py new file mode 100644 index 000000000000..0657f7178143 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/models/account_invoice.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Akretion +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from __future__ import with_statement + +import base64 +import requests +import json +import tempfile + +from odoo import models, api, fields, _ +from odoo.exceptions import Warning as UserError + + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + + file_boleto_pdf_id = fields.Many2one( + comodel_name="ir.attachment", + string="Boleto PDF", + ondelete="restrict", + copy=False) + + def gera_boleto_pdf(self): + file_pdf = self.file_boleto_pdf_id + self.file_boleto_pdf_id = False + file_pdf.unlink() + + receivable_ids = self.mapped('move_line_receivable_ids') + + boleto_list = receivable_ids.send_payment() + print('BOLETO LIST', boleto_list) + if not boleto_list: + raise UserError( + 'Error !', ('Não é possível gerar os boletos\n' + 'Certifique-se que a fatura esteja confirmada e o ' + 'forma de pagamento seja duplicatas')) + + boletos = [b.boleto_cnab_api_data for b in boleto_list] + + content = json.dumps(boletos) + f = open(tempfile.mktemp(), 'w') + f.write(content) + f.close() + files = {'data': open(f.name, 'rb')} + + # TODO - Name of service should be a parameter ? + # For docky users check the name of service + # defined in dev.docker-compose.yml + res = requests.post("http://brcobranca:9292/api/boleto/multi", + data={'type': 'pdf'}, files=files) + if str(res.status_code)[0] == '2': + pdf_string = res.content + else: + raise UserError(res.text.encode('utf-8')) + + # TODO - Name File + file_name = 'boleto-teste.pdf' + + self.file_pdf_id = self.env['ir.attachment'].create( + { + "name": file_name, + "datas_fname": file_name, + "res_model": self._name, + "res_id": self.id, + "datas": base64.b64encode(pdf_string), + "mimetype": "application/pdf", + "type": "binary", + } + ) + + def _target_new_tab(self, attachment_id): + if attachment_id: + return { + 'type' : 'ir.actions.act_url', + 'url': '/web/content/{id}/{nome}'.format( + id=attachment_id.id, + nome=attachment_id.name), + 'target': 'new', + } + + def view_boleto_pdf(self): + if not self.file_boleto_pdf_id: + self.gera_boleto_pdf() + return self._target_new_tab(self.file_boleto_pdf_id) diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py new file mode 100644 index 000000000000..b6db8c9a348d --- /dev/null +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion +# @author Raphaël Valyi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +from datetime import datetime + +from odoo import models, api, _ +from odoo.exceptions import Warning as UserError + +_logger = logging.getLogger(__name__) + + +class BoletoWrapper(object): + def __init__(self, boleto_cnab_api_data): + # wrap the object + # self._wrapped_obj = obj + self.boleto_cnab_api_data = boleto_cnab_api_data + + def __getattr__(self, attr): + # see if this object has attr + # NOTE do not use hasattr, it goes into + # infinite recurrsion + if attr in self.__dict__: + # this object has it + return getattr(self, attr) + # proxy to the wrapped object + return getattr(self._wrapped_obj, attr) + + +dict_brcobranca_bank = { + '001': 'banco_brasil', + '041': 'banrisul', + '237': 'bradesco', + '104': 'caixa', + '399': 'hsbc', + '341': 'itau', + '033': 'santander', + '748': 'sicredi', + '004': 'banco_nordeste', + '021': 'banestes', + '756': 'sicoob', +} + +dict_brcobranca_currency = { + 'R$': '9', +} + + +class AccountMoveLine(models.Model): + _inherit = 'account.move.line' + # see the list of brcobranca boleto fields: + # https://github.com/kivanio/brcobranca/blob/master/lib/ + # brcobranca/boleto/base.rb + # and test a here: + # https://github.com/kivanio/brcobranca/blob/master/spec/ + # brcobranca/boleto/itau_spec.rb + + @api.multi + def send_payment(self): + + # super(AccountMoveLine, self).send_payment() + wrapped_boleto_list = [] + + for move_line in self: + if move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.code_bc in \ + dict_brcobranca_bank: + bank_name_brcobranca = dict_brcobranca_bank[ + move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.code_bc], + else: + raise UserError( + _('The Bank %s is not implemented in BRCobranca.') % + move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.name) + + precision = self.env['decimal.precision'] + precision_account = precision.precision_get('Account') + + instrucao_juros = "" + instrucao_juros_tmp = "APÓS VENCIMENTO COBRAR PERCENTUAL" + if move_line.payment_mode_id.instrucao_boleto_perc_mora: + instrucao_juros_tmp = \ + move_line.payment_mode_id.instrucao_boleto_perc_mora + if move_line.payment_mode_id.boleto_perc_mora: + valor_juros = round( + move_line.debit * + ((move_line.payment_mode_id.boleto_perc_mora / 100) + / 30), precision_account) + instrucao_juros = ( + instrucao_juros_tmp.encode('UTF-8') + + " DE %s %% AO MÊS ( R$ %s AO DIA )" + % (('%.2f' % + move_line.payment_mode_id.boleto_perc_mora + ).replace('.', ','), + ('%.2f' % valor_juros).replace('.', ','))) + + instrucao_multa = '' + instrucao_multa_tmp = "APÓS VENCIMENTO COBRAR MULTA" + if move_line.payment_mode_id.instrucao_boleto_perc_multa: + instrucao_multa_tmp = \ + move_line.payment_mode_id.instrucao_boleto_perc_multa + if move_line.payment_mode_id.boleto_perc_mora: + valor_multa = round(move_line.debit * ( + (move_line.payment_mode_id.boleto_perc_multa / 100) + ), precision_account) + instrucao_multa = ( + instrucao_multa_tmp.encode('UTF-8') + + " DE %s %% ( R$ %s )" % + (('%.2f' % move_line.payment_mode_id.boleto_perc_multa + ).replace('.', ','), + ('%.2f' % valor_multa).replace('.', ','))) + + instrucao_desconto_vencimento = '' + instrucao_desconto_vencimento_tmp =\ + 'CONCEDER ABATIMENTO PERCENTUAL DE' + if move_line.payment_term_id.instrucao_discount_perc: + instrucao_desconto_vencimento_tmp =\ + move_line.payment_term_id.instrucao_discount_perc + if move_line.payment_term_id.discount_perc: + valor_desconto = round( + move_line.debit * ( + move_line.payment_term_id.discount_perc / 100), + precision_account) + instrucao_desconto_vencimento = ( + instrucao_desconto_vencimento_tmp.encode('UTF-8') + ' %s %% ' + 'ATÉ O VENCIMENTO EM %s ( R$ %s )' + % (('%.2f' % move_line.payment_term_id.discount_perc + ).replace('.', ','), + datetime.strptime( + move_line.date_maturity, + '%Y-%m-%d').strftime('%d/%m/%Y'), + ('%.2f' % valor_desconto).replace('.', ',') + )) + + boleto_cnab_api_data = { + '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 + ', ' + + move_line.company_id.partner_id.street_number + ' - ' + + move_line.company_id.partner_id.district + ' - ' + + move_line.company_id.partner_id.city_id.name + + ' - ' + 'CEP:' + move_line.company_id.partner_id.zip + + ' - ' + move_line.company_id.partner_id.state_id.code, + 'documento_cedente': move_line.company_id.cnpj_cpf, + 'sacado': move_line.partner_id.legal_name, + 'sacado_documento': move_line.partner_id.cnpj_cpf, + 'agencia': + move_line.payment_mode_id.fixed_journal_id.bank_account_id.bra_number, + 'conta_corrente': + move_line.payment_mode_id.fixed_journal_id.bank_account_id.acc_number, + 'convenio': move_line.payment_mode_id.boleto_convenio, + 'carteira': str(move_line.payment_mode_id.boleto_carteira), + 'nosso_numero': int(''.join( + i for i in move_line.nosso_numero if i.isdigit())), + 'documento_numero': move_line.name, + 'data_vencimento': + move_line.date_maturity.strftime('%Y/%m/%d'), + 'data_documento': + move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), + 'especie': move_line.payment_mode_id.boleto_especie, + 'moeda': dict_brcobranca_currency['R$'], + 'aceite': move_line.payment_mode_id.boleto_aceite, + 'sacado_endereco': + move_line.partner_id.street + ', ' + + move_line.partner_id.street_number + ' ' + + move_line.partner_id.city_id.name + ' - ' + + move_line.partner_id.state_id.name, + 'data_processamento': + move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), + 'instrucao1': move_line.payment_mode_id.instrucoes or '', + 'instrucao3': instrucao_juros, + 'instrucao4': instrucao_multa, + 'instrucao5': instrucao_desconto_vencimento, + } + + if move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.bic in ('021', '004'): + boleto_cnab_api_data.update({ + 'digito_conta_corrente': + move_line.payment_mode_id.bank_id.acc_number_dig + }) + + # TODO - Create or use a field to have byte_idt information + if move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.bic == '748': + boleto_cnab_api_data.update({ + 'byte_idt': '2', + }) + + wrapped_boleto_list.append(BoletoWrapper(boleto_cnab_api_data)) + return wrapped_boleto_list diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_mode.py b/l10n_br_account_payment_brcobranca/models/account_payment_mode.py new file mode 100644 index 000000000000..bf0ae1c94eec --- /dev/null +++ b/l10n_br_account_payment_brcobranca/models/account_payment_mode.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion +# @author Renato Lima +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +class PaymentMode(models.Model): + _inherit = 'account.payment.mode' + + cnab_sequence_id = fields.Many2one( + comodel_name='ir.sequence', + string=u'Sequencia do CNAB') + diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py new file mode 100644 index 000000000000..c5b2ebd3f21e --- /dev/null +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Akretion +# @author Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import time +import logging + +from odoo import models, api, fields, _ + +_logger = logging.getLogger(__name__) + +try: + from erpbrasil.base import misc +except ImportError: + _logger.error("Biblioteca erpbrasil.base não instalada") + +import logging + +import requests +import json +import tempfile +from odoo.exceptions import Warning as UserError + + +_logger = logging.getLogger(__name__) +try: + from cnab240.errors import (Cnab240Error) +except ImportError as err: + _logger.debug = err + +dict_brcobranca_bank = { + '001': 'banco_brasil', + '041': 'banrisul', + '237': 'bradesco', + '104': 'caixa', + '399': 'hsbc', + '341': 'itau', + '033': 'santander', + '748': 'sicredi', + '004': 'banco_nordeste', + '021': 'banestes', + '756': 'sicoob', +} + +dict_brcobranca_cnab_type = { + '240': 'cnab240', + '400': 'cnab400', +} + + +class PaymentOrder(models.Model): + _inherit = "account.payment.order" + + @api.multi + def generate_payment_file(self): + """Returns (payment file as string, filename)""" + self.ensure_one() + + # see remessa fields here: + # https://github.com/kivanio/brcobranca/blob/master/lib/brcobranca/remessa/base.rb + # https://github.com/kivanio/brcobranca/tree/master/lib/brcobranca/remessa/cnab240 + # https://github.com/kivanio/brcobranca/tree/master/lib/brcobranca/remessa/cnab400 + # and a test here: + # https://github.com/kivanio/brcobranca/blob/master/spec/brcobranca/remessa/cnab400/itau_spec.rb + + if self.payment_mode_id.fixed_journal_id.\ + bank_account_id.bank_id.code_bc in \ + dict_brcobranca_bank: + bank_name_brcobranca = \ + dict_brcobranca_bank[ + self.payment_mode_id.fixed_journal_id + .bank_account_id.bank_id.code_bc], + else: + raise UserError( + _('The Bank %s is not implemented in BRCobranca.') + % self.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.name) + + if (bank_name_brcobranca[0] not in ('bradesco', 'itau') + or self.payment_mode_id.payment_method_id.code != '400'): + raise UserError( + _('The Bank %s and CNAB %s are not implemented.') + % (self.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.name, + self.payment_mode_id.payment_method_id.code)) + + pagamentos = [] + for line in self.payment_line_ids: + linhas_pagamentos = { + 'valor': line.amount_currency, + 'data_vencimento': line.move_line_id.date_maturity.strftime('%Y/%m/%d'), + 'nosso_numero': line.move_line_id.nosso_numero, + 'documento_sacado': misc.punctuation_rm(line.partner_id.cnpj_cpf), + 'nome_sacado': + line.partner_id.legal_name.strip()[:40], + 'numero': str(line.move_line_id.name)[:10], + 'endereco_sacado': str( + line.partner_id.street + ', ' + str( + line.partner_id.street_number))[:40], + 'bairro_sacado': + line.partner_id.district.strip(), + 'cep_sacado': misc.punctuation_rm(line.partner_id.zip), + 'cidade_sacado': + line.partner_id.city_id.name, + 'uf_sacado': line.partner_id.state_id.code, + 'identificacao_ocorrencia': self.codigo_instrucao_movimento + } + if line.move_line_id.payment_mode_id.boleto_perc_mora: + linhas_pagamentos['codigo_multa'] = '2' + linhas_pagamentos['percentual_multa'] = \ + line.move_line_id.payment_mode_id.boleto_perc_mora + precision = self.env['decimal.precision'] + precision_account = precision.precision_get('Account') + if line.move_line_id.payment_mode_id.cnab_percent_interest: + linhas_pagamentos['valor_mora'] = round( + line.move_line_id.debit * + ((line.move_line_id.payment_mode_id.cnab_percent_interest / 100) + / 30), precision_account) + if line.move_line_id.payment_term_id.discount_perc: + linhas_pagamentos['data_desconto'] =\ + line.move_line_id.date_maturity.strftime('%Y/%m/%d') + linhas_pagamentos['valor_desconto'] = round( + line.move_line_id.debit * ( + line.move_line_id.payment_term_id.discount_perc / 100), + precision_account) + + pagamentos.append(linhas_pagamentos) + + remessa_values = { + 'carteira': str(self.payment_mode_id.boleto_carteira), + 'agencia': int(self.payment_mode_id.fixed_journal_id.bank_account_id.bra_number), + # 'digito_agencia': order.mode.bank_id.bra_number_dig, + 'conta_corrente': int(misc.punctuation_rm(self.payment_mode_id.fixed_journal_id.bank_account_id.acc_number)), + 'digito_conta': self.payment_mode_id.fixed_journal_id.bank_account_id.acc_number_dig[0], + 'empresa_mae': + self.payment_mode_id.fixed_journal_id.bank_account_id.partner_id.legal_name[:30], + 'documento_cedente': misc.punctuation_rm( + self.payment_mode_id.fixed_journal_id.bank_account_id.partner_id.cnpj_cpf), + 'pagamentos': pagamentos, + 'sequencial_remessa': self.payment_mode_id.cnab_sequence_id.next_by_id(), + } + + if (bank_name_brcobranca[0] == 'bradesco' + and self.payment_mode_id.payment_method_id.code == '400'): + remessa_values[ + 'codigo_empresa'] = int(self.payment_mode_id.codigo_convenio) + + content = json.dumps(remessa_values) + f = open(tempfile.mktemp(), 'w') + f.write(content) + f.close() + files = {'data': open(f.name, 'rb')} + + # TODO - Name of service should be a parameter ? + # For docky users check the name of service + # defined in dev.docker-compose.yml + + res = requests.post( + "http://brcobranca:9292/api/remessa", + data={ + 'type': dict_brcobranca_cnab_type[ + self.payment_mode_id.payment_method_id.code], + 'bank': bank_name_brcobranca[0], + }, files=files) + # print "AAAAAAAA", res.status_code, str(res.status_code)[0] + #print('RES.CONTENT', res.content[0]) + + # TODO - Check If will be necessary keep code + # Test made in CNAB 400 Bradesco + #if res.content[0] == '0': + # remessa = res.content + #else: + # raise UserError(res.text) + + remessa = res.content + + #self.state = 'done' + #self.cnab_file = base64.b64encode(remessa) + + # Criando instancia do CNAB a partir do código do banco + # cnab = Cnab.get_cnab( + # order.mode.bank_id.bank_bic, order.mode.type.code)() + + # remessa = cnab.remessa(order) + + file_name = '' + if self.payment_mode_id.payment_method_id.code == '240': + file_name = 'CB%s%s.REM' % ( + time.strftime('%d%m'), str(self.file_number)) + elif self.payment_mode_id.payment_method_id.code == '400': + file_name = 'CB%s%02d.REM' % ( + time.strftime('%d%m'), self.file_number or 1) + elif self.payment_mode_id.payment_method_id.code == '500': + file_name = 'PG%s%s.REM' % ( + time.strftime('%d%m'), str(self.file_number)) + # self.state = 'done' + # self.cnab_file = base64.b64encode(remessa) + # self.cnab_file = base64.b64encode(remessa) + # self.cnab_filename = self.name + + return remessa, file_name diff --git a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py new file mode 100644 index 000000000000..49fe17952a64 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py @@ -0,0 +1,352 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion - Renato Lima +# Copyright 2017 Akretion - Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import base64 +import requests +import json +import datetime + +from odoo import models, fields, api, _ +from odoo.exceptions import Warning as UserError + +DICT_OCORRENCIAS_BRADESCO = { + '02': u'Entrada Confirmada (verificar motivo na posição 319 a 328)', + '03': u'Entrada Rejeitada ( verificar motivo na posição 319 a 328)', + '06': u'Liquidação normal (sem motivo)', + '09': u'Baixado Automat. via Arquivo (verificar motivo posição 319 a 328)', + '10': u'Baixado conforme instruções da Agência(' + u'verificar motivo pos.319 a 328)', + '11': u'Em Ser - Arquivo de Títulos pendentes (sem motivo)', + '12': u'Abatimento Concedido (sem motivo)', + '13': u'Abatimento Cancelado (sem motivo)', + '14': u'Vencimento Alterado (sem motivo)', + '15': u'Liquidação em Cartório (sem motivo)', + '16': u'Título Pago em Cheque – Vinculado', + '17': u'Liquidação após baixa ou Título não registrado (sem motivo)', + '18': u'Acerto de Depositária (sem motivo)', + '19': u'Confirmação Receb. Inst. de Protesto ' + u'(verificar motivo pos.295 a 295)', + '20': u'Confirmação Recebimento Instrução Sustação de' + u' Protesto (sem motivo)', + '21': u'Acerto do Controle do Participante (sem motivo)', + '22': u'Título Com Pagamento Cancelado', + '23': u'Entrada do Título em Cartório (sem motivo)', + '24': u'Entrada rejeitada por CEP Irregular' + u' (verificar motivo pos.319 a 328)', + '25': u'Confirmação Receb.Inst.de Protesto Falimentar' + u' (verificar pos.295 a 295)', + '27': u'Baixa Rejeitada (verificar motivo posição 319 a 328)', + '28': u'Débito de tarifas/custas (verificar motivo na posição 319 a 328)', + '29': u'Ocorrências do Pagador (NOVO)', + '30': u'Alteração de Outros Dados Rejeitados ' + u'(verificar motivo pos.319 a 328)', + '32': u'Instrução Rejeitada (verificar motivo posição 319 a 328)', + '33': u'Confirmação Pedido Alteração Outros Dados (sem motivo)', + '34': u'Retirado de Cartório e Manutenção Carteira (sem motivo)', + '35': u'Desagendamento do débito automático ' + u'(verificar motivos pos. 319 a 328)', + '40': u'Estorno de pagamento (NOVO)', + '55': u'Sustado judicial (NOVO)', + '68': u'Acerto dos dados do rateio de Crédito (verificar motivo posição de' + u' status do registro tipo 3)', + '69': u'Cancelamento dos dados do rateio (verificar motivo posição de' + u' status do registro tipo 3)', + '073': u'Confirmação Receb. Pedido de Negativação (NOVO)', + '074': u'Confir Pedido de Excl de Negat (com ou sem baixa) (NOVO)', + '00': u'Nota: Para as ocorrências sem motivos, as posições serão' + u' informadas com Zeros.', +} + +DICT_OCORRENCIAS_ITAU = { + '02': u'ENTRADA CONFIRMADA COM POSSIBILIDADE DE MENSAGEM' + u' (NOTA 20 – TABELA 10)', + '03': u'ENTRADA REJEITADA (NOTA 20 – TABELA 1)', + '04': u'ALTERAÇÃO DE DADOS – NOVA ENTRADA OU ALTERAÇÃO/EXCLUSÃO' + u' DE DADOS ACATADA', + '05': u'ALTERAÇÃO DE DADOS – BAIXA', + '06': u'LIQUIDAÇÃO NORMAL', + '07': u'LIQUIDAÇÃO PARCIAL – COBRANÇA INTELIGENTE (B2B)', + '08': u'LIQUIDAÇÃO EM CARTÓRIO', + '09': u'BAIXA SIMPLES', + '10': u'BAIXA POR TER SIDO LIQUIDADO', + '11': u'EM SER (SÓ NO RETORNO MENSAL)', + '12': u'ABATIMENTO CONCEDIDO', + '13': u'ABATIMENTO CANCELADO', + '14': u'VENCIMENTO ALTERADO', + '15': u'BAIXAS REJEITADAS (NOTA 20 – TABELA 4)', + '16': u'INSTRUÇÕES REJEITADAS (NOTA 20 – TABELA 3)', + '17': u'ALTERAÇÃO/EXCLUSÃO DE DADOS REJEITADOS (NOTA 20 – TABELA 2)', + '18': u'COBRANÇA CONTRATUAL – INSTRUÇÕES/ALTERAÇÕES' + u' REJEITADAS/PENDENTES (NOTA 20 – TABELA 5)', + '19': u'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE PROTESTO', + '20': u'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE SUSTAÇÃO' + u' DE PROTESTO /TARIFA', + '21': u'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE NÃO PROTESTAR', + '23': u'TÍTULO ENVIADO A CARTÓRIO/TARIFA', + '24': u'INSTRUÇÃO DE PROTESTO REJEITADA / SUSTADA / PENDENTE' + u' (NOTA 20 – TABELA 7)', + '25': u'ALEGAÇÕES DO PAGADOR (NOTA 20 – TABELA 6)', + '26': u'TARIFA DE AVISO DE COBRANÇA', + '27': u'TARIFA DE EXTRATO POSIÇÃO (B40X)', + '28': u'TARIFA DE RELAÇÃO DAS LIQUIDAÇÕES', + '29': u'TARIFA DE MANUTENÇÃO DE TÍTULOS VENCIDOS', + '30': u'DÉBITO MENSAL DE TARIFAS (PARA ENTRADAS E BAIXAS)', + '32': u'BAIXA POR TER SIDO PROTESTADO', + '33': u'CUSTAS DE PROTESTO', + '34': u'CUSTAS DE SUSTAÇÃO', + '35': u'CUSTAS DE CARTÓRIO DISTRIBUIDOR', + '36': u'CUSTAS DE EDITAL', + '37': u'TARIFA DE EMISSÃO DE BOLETO/TARIFA DE ENVIO DE DUPLICATA', + '38': u'TARIFA DE INSTRUÇÃO', + '39': u'TARIFA DE OCORRÊNCIAS', + '40': u'TARIFA MENSAL DE EMISSÃO DE BOLETO/TARIFA MENSAL' + u' DE ENVIO DE DUPLICATA', + '41': u'DÉBITO MENSAL DE TARIFAS – EXTRATO DE POSIÇÃO (B4EP/B4OX)', + '42': u'DÉBITO MENSAL DE TARIFAS – OUTRAS INSTRUÇÕES', + '43': u'DÉBITO MENSAL DE TARIFAS – MANUTENÇÃO DE TÍTULOS VENCIDOS', + '44': u'DÉBITO MENSAL DE TARIFAS – OUTRAS OCORRÊNCIAS', + '45': u'DÉBITO MENSAL DE TARIFAS – PROTESTO', + '46': u'DÉBITO MENSAL DE TARIFAS – SUSTAÇÃO DE PROTESTO', + '47': u'BAIXA COM TRANSFERÊNCIA PARA DESCONTO', + '48': u'CUSTAS DE SUSTAÇÃO JUDICIAL', + '51': u'TARIFA MENSAL REF A ENTRADAS BANCOS CORRESPONDENTES NA CARTEIRA', + '52': u'TARIFA MENSAL BAIXAS NA CARTEIRA', + '53': u'TARIFA MENSAL BAIXAS EM BANCOS CORRESPONDENTES NA CARTEIRA', + '54': u'TARIFA MENSAL DE LIQUIDAÇÕES NA CARTEIRA', + '55': u'TARIFA MENSAL DE LIQUIDAÇÕES EM BANCOS' + u' CORRESPONDENTES NA CARTEIRA', + '56': u'CUSTAS DE IRREGULARIDADE', + '57': u'INSTRUÇÃO CANCELADA (NOTA 20 – TABELA 8)', + '59': u'BAIXA POR CRÉDITO EM C/C ATRAVÉS DO SISPAG', + '60': u'ENTRADA REJEITADA CARNÊ (NOTA 20 – TABELA 1)', + '61': u'TARIFA EMISSÃO AVISO DE MOVIMENTAÇÃO DE TÍTULOS (2154)', + '62': u'DÉBITO MENSAL DE TARIFA – AVISO DE MOVIMENTAÇÃO DE TÍTULOS (2154)', + '63': u'TÍTULO SUSTADO JUDICIALMENTE', + '64': u'ENTRADA CONFIRMADA COM RATEIO DE CRÉDITO', + '65': u'PAGAMENTO COM CHEQUE – AGUARDANDO COMPENSAÇÃO', + '69': u'CHEQUE DEVOLVIDO (NOTA 20 – TABELA 9)', + '71': u'ENTRADA REGISTRADA, AGUARDANDO AVALIAÇÃO', + '72': u'BAIXA POR CRÉDITO EM C/C ATRAVÉS DO SISPAG' + u' SEM TÍTULO CORRESPONDENTE', + '73': u'CONFIRMAÇÃO DE ENTRADA NA COBRANÇA SIMPLES –' + u' ENTRADA NÃO ACEITA NA COBRANÇA CONTRATUAL', + '74': u'INSTRUÇÃO DE NEGATIVAÇÃO EXPRESSA REJEITADA (NOTA 20 – TABELA 11)', + '75': u'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE ENTRADA' + u' EM NEGATIVAÇÃO EXPRESSA', + '76': u'CHEQUE COMPENSADO', + '77': u'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE EXCLUSÃO DE' + u' ENTRADA EM NEGATIVAÇÃO EXPRESSA', + '78': u'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE CANCELAMENTO DE' + u' NEGATIVAÇÃO EXPRESSA', + '79': u'NEGATIVAÇÃO EXPRESSA INFORMACIONAL (NOTA 20 – TABELA 12)', + '80': u'CONFIRMAÇÃO DE ENTRADA EM NEGATIVAÇÃO EXPRESSA – TARIFA', + '82': u'CONFIRMAÇÃO DO CANCELAMENTO DE NEGATIVAÇÃO EXPRESSA – TARIFA', + '83': u'CONFIRMAÇÃO DE EXCLUSÃO DE ENTRADA EM NEGATIVAÇÃO' + u' EXPRESSA POR LIQUIDAÇÃO – TARIFA', + '85': u'TARIFA POR BOLETO (ATÉ 03 ENVIOS) COBRANÇA ATIVA ELETRÔNICA', + '86': u'TARIFA EMAIL COBRANÇA ATIVA ELETRÔNICA', + '87': u'TARIFA SMS COBRANÇA ATIVA ELETRÔNICA', + '88': u'TARIFA MENSAL POR BOLETO (ATÉ 03 ENVIOS)' + u' COBRANÇA ATIVA ELETRÔNICA', + '89': u'TARIFA MENSAL EMAIL COBRANÇA ATIVA ELETRÔNICA', + '90': u'TARIFA MENSAL SMS COBRANÇA ATIVA ELETRÔNICA', + '91': u'TARIFA MENSAL DE EXCLUSÃO DE ENTRADA DE NEGATIVAÇÃO EXPRESSA', + '92': u'TARIFA MENSAL DE CANCELAMENTO DE NEGATIVAÇÃO EXPRESSA', + '93': u'TARIFA MENSAL DE EXCLUSÃO DE NEGATIVAÇÃO EXPRESSA POR LIQUIDAÇÃO', +} + + +class L10nBrHrCnab(models.Model): + _inherit = "l10n_br.cnab" + + account_journal = fields.Many2one( + 'account.journal', 'Journal used in Bank Statement', + readonly=True, + states={'draft': [('readonly', False)]}, + help='Journal used in create of Bank Statement.' + ) + cnab_type = fields.Selection( + [('cnab400', u'CNAB 400')], 'CNAB Type File', + default='cnab400', + readonly=True, + states={'draft': [('readonly', False)]} + ) + bank = fields.Selection( + [('bradesco', u'Bradesco'), ('itau', u'Itaú')], + string='Bank', states={'draft': [('readonly', False)]} + ) + + @api.multi + def processar_arquivo_retorno(self): + + files = {'data': base64.b64decode(self.arquivo_retorno)} + res = requests.post( + "http://boleto_cnab_api:9292/api/retorno", + data={ + 'type': self.cnab_type, + 'bank': self.bank, + }, files=files) + + if res.status_code != 201: + raise UserError(res.text) + + string_result = res.json() + data = json.loads(string_result) + + self.processar_arquivo_retorno_cnab400(data) + + @api.multi + def processar_arquivo_retorno_cnab400(self, data): + + lote_id = self.env['l10n.br.cnab.lote'].create({'cnab_id': self.id}) + + quantidade_registros = 0 + total_valores = 0 + balance_end_real = 0.0 + line_statement_vals = [] + + for dict_line in data: + if int(dict_line['codigo_registro']) != 1: + # Bradesco + # Existe o codigo de registro 9 que eh um totalizador + # porem os campos estao colocados em outras posicoes + # que nao estao mapeadas no BRCobranca + # Itau + # 9 - Registro Trailer do Arquivo + # 4 e 5 - Registro de Detalhe (Opcional) + continue + + quantidade_registros += 1 + account_move_line = self.env['account.move.line'].search( + [('boleto_own_number', '=', dict_line['nosso_numero'][:11])] + ) + payment_line = self.env['payment.line'].search( + [('move_line_id', '=', account_move_line.id)] + ) + + valor_titulo = float( + str(dict_line['valor_titulo'][0:11] + '.' + + dict_line['valor_titulo'][11:])) + + valor_recebido = 0.0 + if dict_line['valor_recebido']: + valor_recebido = float( + str(dict_line['valor_recebido'][0:11] + '.' + + dict_line['valor_recebido'][11:])) + total_valores += valor_titulo + + if (dict_line['data_ocorrencia'] == '000000' or + not dict_line['data_ocorrencia']): + data_ocorrencia = dict_line['data_de_ocorrencia'] + else: + data_ocorrencia = datetime.datetime.strptime( + str(dict_line['data_ocorrencia']), "%d%m%y").date() + + # Cada Banco pode possuir um Codigo de Ocorrencia distinto + if self.bank == 'bradesco': + descricao_ocorrencia = DICT_OCORRENCIAS_BRADESCO[ + dict_line['codigo_ocorrencia']].encode('utf-8') + if self.bank == 'itau': + descricao_ocorrencia = DICT_OCORRENCIAS_ITAU[ + dict_line['codigo_ocorrencia']].encode('utf-8') + + if not account_move_line: + vals_evento = { + 'lote_id': lote_id.id, + 'ocorrencias': descricao_ocorrencia, + 'data_ocorrencia': data_ocorrencia, + 'str_motiv_a': + u' * - BOLETO NÃO ENCONTRADO DENTRO DO PROGRAMA', + 'nosso_numero': dict_line['nosso_numero'], + 'seu_numero': dict_line['documento_numero'], + 'valor_titulo': valor_titulo, + } + self.env['l10n_br.cnab.evento'].create(vals_evento) + continue + + if (dict_line['data_credito'] == '000000' or + not dict_line['data_credito']): + data_credito = dict_line['data_credito'] + else: + data_credito = datetime.datetime.strptime( + str(dict_line['data_credito']), "%d%m%y").date() + + if (str(dict_line['codigo_ocorrencia']) in ('06', '17') + and self.bank == 'bradesco') or ( + str(dict_line['codigo_ocorrencia']) in ('06', '10') and + self.bank == 'itau'): + vals_evento = { + 'lote_id': lote_id.id, + 'data_ocorrencia': data_ocorrencia, + 'data_real_pagamento': data_credito.strftime("%Y-%m-%d"), + # 'segmento': evento.servico_segmento, + # 'favorecido_nome': + # obj_account_move_line.company_id.partner_id.name, + 'favorecido_conta_bancaria': + account_move_line.payment_mode_id.bank_id.id, + 'nosso_numero': dict_line['nosso_numero'], + 'seu_numero': dict_line['documento_numero'] or + account_move_line.name, + # 'tipo_moeda': evento.credito_moeda_tipo, + 'valor_titulo': valor_titulo, + 'valor_pagamento': valor_recebido, + 'ocorrencias': descricao_ocorrencia, + 'bank_payment_line_id': + payment_line.bank_line_id.id or False, + } + + # Monta o dicionario que sera usado + # para criar o Extrato Bancario + balance_end_real += valor_recebido + line_statement_vals.append({ + 'name': account_move_line.name or '?', + 'amount': valor_recebido, + 'partner_id': account_move_line.partner_id.id, + 'ref': account_move_line.ref, + 'date': account_move_line.date, + 'amount_currency': valor_recebido, + 'currency_id': account_move_line.currency_id.id, + }) + + else: + vals_evento = { + 'lote_id': lote_id.id, + 'ocorrencias': descricao_ocorrencia, + 'data_ocorrencia': data_ocorrencia, + 'nosso_numero': dict_line['nosso_numero'], + 'seu_numero': account_move_line.name, + 'valor_titulo': valor_titulo, + } + + self.env['l10n_br.cnab.evento'].create(vals_evento) + + lote_id.total_valores = total_valores + lote_id.qtd_registros = quantidade_registros + self.num_lotes = 1 + self.num_eventos = quantidade_registros + + # Criacao de um Extrato Bancario para ser conciliado + # pelo usuario permitindo assim o tratamento de valores + # a mais ou a menos pelo operador + if line_statement_vals: + vals_bank_statement = { + 'journal_id': self.account_journal.id, + 'balance_end_real': balance_end_real, + } + statement = self.env[ + 'account.bank.statement'].create(vals_bank_statement) + statement_line_obj = self.env['account.bank.statement.line'] + for line in line_statement_vals: + line['statement_id'] = statement.id + statement_line_obj.create(line) + + return self.write({'state': 'done'}) + + +class L10nBrHrCnabEvento(models.Model): + _inherit = "l10n_br.cnab.evento" + + data_ocorrencia = fields.Date(string=u"Data da Ocorrência") + valor_titulo = fields.Float(string=u"Valor do Título") diff --git a/l10n_br_account_payment_brcobranca/tests/invoice_create.yml b/l10n_br_account_payment_brcobranca/tests/invoice_create.yml new file mode 100644 index 000000000000..4adffd95071a --- /dev/null +++ b/l10n_br_account_payment_brcobranca/tests/invoice_create.yml @@ -0,0 +1,213 @@ +- + I prepare tunneling extra args in the context for account.invoice +- + !python {model: account.invoice}: | + context.update({'parent_fiscal_category_id': ref('l10n_br_account_product.fc_78df616ab31e95ee46c6a519a2ce9e12'), 'type_tax_use': 'sale', 'type': 'out_invoice', 'fiscal_type': 'product', 'fiscal_document_code': '55', 'form_view_ref': 'l10n_br_account_product.l10n_br_account_product_nfe_line_form'}) +- + Create a sale customer invoice from SP to SP fiscal contributor +- + !record {model: account.invoice, id: account_invoice_customer_sp_sp, view: l10n_br_account_product.l10n_br_account_product_nfe_form}: + partner_id: l10n_br_base.res_partner_cliente1_sp + fiscal_category_id: l10n_br_account_product.fc_78df616ab31e95ee46c6a519a2ce9e12 + reference_type: none + payment_mode_id: l10n_br_account_banking_payment_cnab.payment_mode_cobranca_bradesco240 + name: 'NFe Invoice Test' + date_due: 2017-03-05 + invoice_line: + - product_id: product.product_product_18 + fiscal_category_id: l10n_br_account_product.fc_78df616ab31e95ee46c6a519a2ce9e12 + quantity: 1.0 + price_unit: 1000.00 +- + I check if found fisal position and CFOP, document type and document serie +- + !assert {model: account.invoice, id: account_invoice_customer_sp_sp}: + - fiscal_position != False + - fiscal_document_id.id == company_id.product_invoice_id.id + - document_serie_id.fiscal_document_id.id == fiscal_document_id.id + - invoice_line[0].fiscal_position.id == ref('l10n_br_account_product.fp_78df616ab31e95ee46c6a519a2ce9e12_internal_demo') + - invoice_line[0].cfop_id.code == '5101' +- + I check that Initially customer invoice is in the "Draft" state +- + !assert {model: account.invoice, id: account_invoice_customer_sp_sp}: + - state == 'draft' +- + I create invoice by clicking on Create button +- + !workflow {model: account.invoice, action: invoice_validate, ref: account_invoice_customer_sp_sp} +- + I check that the invoice state is "sefaz_export" +- + !assert {model: account.invoice, id: account_invoice_customer_sp_sp}: + - state == 'sefaz_export' +- + I set the state of invoice to "open" state +- + !workflow {model: account.invoice, action: invoice_open, ref: account_invoice_customer_sp_sp} +- + I check that the invoice state is "open" +- + !assert {model: account.invoice, id: account_invoice_customer_sp_sp}: + - state == 'open' +- + I check if the invoice has a related move that is receivable +- + !assert {model: account.invoice, id: account_invoice_customer_sp_sp}: + - move_id.line_id[0].date_maturity != False + - move_id.line_id[0].account_id.type == 'receivable' + #- + # I update the main company bank bradesco account digit + #- +# !record {model: res.partner.bank, id: l10n_br_account_payment_mode.main_company_bank_bradesco}: +# bra_acc_dig: '0' +- + I create the payment mode for pagamento +- + !record {model: payment.mode, id: payment_mode_pagamento}: + name: u'Pagamento' + bank_id: l10n_br_account_payment_mode.main_company_bank_bradesco + active: True + payment_order_type: 'payment' + sale_ok: True + journal: account.bank_journal + company_id: base.main_company + type: account_banking_payment_export.manual_bank_tranfer + purchase_ok: False, + type_sale_payment: '99' +- + I create the cnab internal sequence +- + !record {model: ir.sequence, id: cnab_internal_sequence}: + name: u'Sequência interna de cobrança' + number_next: 1 + number_next_actual: 1 +- + I create the cnab sufix internal sequence +- + !record {model: ir.sequence, id: cnab_sufix_internal_sequence}: + name: u'Sequência interna do sufixo da cobrança' + number_next: 1 + number_next_actual: 1 +- + I create the cnab sufix sequence +- + !record {model: l10n_br_cnab_file_sufix.sequence, id: cnab_sufix_sequence}: + code: 'CobSuf' + name: u'Sequência do sufixo da Cobrança' + internal_sequence_id: cnab_sufix_internal_sequence + parent_payment_mode_suf: payment_mode_pagamento +- + I create the cobrança (payment.order) +- + !record {model: payment.order, id: cobranca}: + user_id: 1 + mode: l10n_br_account_banking_payment_cnab.payment_mode_cobranca_bradesco240 + date_prefered: 'due' + # serie_id: cnab_sequence + # serie_sufixo_arquivo: cnab_sufix_sequence +#- +# I confirm the cobrança (payment order) +#- +# !workflow {model: payment.order, action: open, ref: cobranca} +#- +# I check that cobranca(payment order) is now "Confirmed". +#- +# !assert {model: payment.order, id: cobranca, severity: error, string: Payment Order should be 'Confirmed'.}: +# - state == 'open' +- + I adapt the context for payment wizard +- + !python {model: account.invoice}: | + context.update({'active_model': 'payment.order', 'active_id': ref('cobranca')}) +- + !record {model: payment.order.create, id: wizard_cobranca}: + duedate: 2017-03-05 +- + I search for the invoice entries to make the payment. +- + !python {model: payment.order.create}: | + self.search_entries(cr, uid, [ref("wizard_cobranca")], { + "active_model": "payment.order", "active_ids": [ref("cobranca")], + "active_id": ref("cobranca"), }) +- + I create payment lines entries. +- + !python {model: payment.order.create}: | + invoice = self.pool.get('account.invoice').browse(cr, uid, ref("account_invoice_customer_sp_sp")) + move_line = invoice.move_id.line_id[0] + self.write(cr, uid, [ref("wizard_cobranca")], {'entries': [(6,0,[move_line.id])]}) + self.create_payment(cr, uid, [ref("wizard_cobranca")], { + "active_model": "payment.order", "active_ids": [ref("cobranca")], + "active_id": ref("cobranca")}) +- + I confirm the cobrança (payment order) +- + !workflow {model: payment.order, action: open, ref: cobranca} +- + I check that payment line is created with proper data. +- + !python {model: payment.order}: | + invoice = self.pool.get('account.invoice').browse(cr, uid, ref("account_invoice_customer_sp_sp")) + payment = self.browse(cr, uid, ref("cobranca")) + payment_line = payment.line_ids[0] + # rename the payment line because we can't test if the generated file get dinamic information (payment line get the by ir.sequence) + payment_line.name = '001' + payment_line.move_line_id.move_id.name = 1 + + assert payment_line.move_line_id, "move line is not created in payment line." + assert invoice.move_id.name == payment_line.ml_inv_ref.number, "invoice reference number is not same created." + assert invoice.partner_id == payment_line.partner_id, "Partner is not correct." + assert invoice.date_due == payment_line.ml_maturity_date, "Due date is not correct." + print "AMOUNT", invoice.amount_total, payment_line.amount + assert invoice.amount_total == - payment_line.amount, "Payment amount is not correct." + assert payment_line.move_line_id.amount_to_pay > 0, "Move line paid" + assert len(self.pool("account.move.line").search(cr, uid, + [('amount_to_pay', '>', 0), ('id', '=', payment_line.move_line_id.id)])) == 0, \ + "No payment order found for this move line" +- + I generate the .REM file and compare it with a test file +- + !python {model: payment.order, id: cobranca}: | + import base64 + import time + assert self.mode.type.ir_model_id.model == 'payment.cnab', "Modelo de wizard errado, deveria ser payment.cnab porem o selecionado foi: %s" %self.mode.type.ir_model_id.model + wizard = self.env['payment.cnab'].create({}) + wizard.with_context(active_ids=[self.env.ref("l10n_br_account_payment_cnab_brcobranca.cobranca").id]).export() + generated_file = base64.b64decode(str(wizard.cnab_file)) + + # fixing dates to have the same date as the test_file + def change(string, interval, what): + start = interval[0] + end = interval[1] + length = end - start + if len(what) + + + + l10n_br_account.invoice.form + account.invoice + + + + + + + \ No newline at end of file diff --git a/l10n_br_account_payment_brcobranca/views/account_payment_mode_view.xml b/l10n_br_account_payment_brcobranca/views/account_payment_mode_view.xml new file mode 100644 index 000000000000..27f46cf27b50 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/views/account_payment_mode_view.xml @@ -0,0 +1,16 @@ + + + + + payment.mode.cnab.form.inherit + account.payment.mode + + + + + + + + + + diff --git a/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml b/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml new file mode 100644 index 000000000000..1d1a1c970209 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml @@ -0,0 +1,39 @@ + + + + + brcobranca.cnab.evento.tree + l10n_br.cnab.evento + + + + + + + + + + + + + brcobranca.cnab.retorno.form.view + l10n_br.cnab + + + + + + + + + + + + + + + From 2fc7d5def9851da86f30d5c2d74e13408d468fa2 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 17 Jun 2020 16:50:16 -0300 Subject: [PATCH 002/308] [REF] Clean code and allow tests with Unicred bank. --- .../models/account_invoice.py | 1 - .../models/account_move_line.py | 35 ++++++++++--------- .../models/account_payment_order.py | 28 +++++++-------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_invoice.py b/l10n_br_account_payment_brcobranca/models/account_invoice.py index 0657f7178143..910053e34ca3 100644 --- a/l10n_br_account_payment_brcobranca/models/account_invoice.py +++ b/l10n_br_account_payment_brcobranca/models/account_invoice.py @@ -31,7 +31,6 @@ def gera_boleto_pdf(self): receivable_ids = self.mapped('move_line_receivable_ids') boleto_list = receivable_ids.send_payment() - print('BOLETO LIST', boleto_list) if not boleto_list: raise UserError( 'Error !', ('Não é possível gerar os boletos\n' diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index b6db8c9a348d..a2b0902e92a2 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -28,6 +28,8 @@ def __getattr__(self, attr): # proxy to the wrapped object return getattr(self._wrapped_obj, attr) +# TODO Sicredi e Unicred possuem o mesmo codigo 748, +# isso estaria certo ? Como diferenciar dict_brcobranca_bank = { '001': 'banco_brasil', @@ -37,7 +39,7 @@ def __getattr__(self, attr): '399': 'hsbc', '341': 'itau', '033': 'santander', - '748': 'sicredi', + '748': 'unicred', '004': 'banco_nordeste', '021': 'banestes', '756': 'sicoob', @@ -64,14 +66,15 @@ def send_payment(self): wrapped_boleto_list = [] for move_line in self: - if move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.code_bc in \ - dict_brcobranca_bank: + bank_account = \ + move_line.payment_mode_id.fixed_journal_id.bank_account_id + if bank_account.bank_id.code_bc in dict_brcobranca_bank: bank_name_brcobranca = dict_brcobranca_bank[ - move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.code_bc], + bank_account.bank_id.code_bc], else: raise UserError( _('The Bank %s is not implemented in BRCobranca.') % - move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.name) + bank_account.bank_id.name) precision = self.env['decimal.precision'] precision_account = precision.precision_get('Account') @@ -87,7 +90,7 @@ def send_payment(self): ((move_line.payment_mode_id.boleto_perc_mora / 100) / 30), precision_account) instrucao_juros = ( - instrucao_juros_tmp.encode('UTF-8') + + instrucao_juros_tmp + " DE %s %% AO MÊS ( R$ %s AO DIA )" % (('%.2f' % move_line.payment_mode_id.boleto_perc_mora @@ -104,7 +107,7 @@ def send_payment(self): (move_line.payment_mode_id.boleto_perc_multa / 100) ), precision_account) instrucao_multa = ( - instrucao_multa_tmp.encode('UTF-8') + + instrucao_multa_tmp + " DE %s %% ( R$ %s )" % (('%.2f' % move_line.payment_mode_id.boleto_perc_multa ).replace('.', ','), @@ -122,7 +125,7 @@ def send_payment(self): move_line.payment_term_id.discount_perc / 100), precision_account) instrucao_desconto_vencimento = ( - instrucao_desconto_vencimento_tmp.encode('UTF-8') + ' %s %% ' + instrucao_desconto_vencimento_tmp + ' %s %% ' 'ATÉ O VENCIMENTO EM %s ( R$ %s )' % (('%.2f' % move_line.payment_term_id.discount_perc ).replace('.', ','), @@ -146,10 +149,8 @@ def send_payment(self): 'documento_cedente': move_line.company_id.cnpj_cpf, 'sacado': move_line.partner_id.legal_name, 'sacado_documento': move_line.partner_id.cnpj_cpf, - 'agencia': - move_line.payment_mode_id.fixed_journal_id.bank_account_id.bra_number, - 'conta_corrente': - move_line.payment_mode_id.fixed_journal_id.bank_account_id.acc_number, + 'agencia': bank_account.bra_number, + 'conta_corrente': bank_account.acc_number, 'convenio': move_line.payment_mode_id.boleto_convenio, 'carteira': str(move_line.payment_mode_id.boleto_carteira), 'nosso_numero': int(''.join( @@ -175,16 +176,18 @@ def send_payment(self): 'instrucao5': instrucao_desconto_vencimento, } - if move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.bic in ('021', '004'): + if bank_account.bank_id.code_bc in ('021', '004'): boleto_cnab_api_data.update({ 'digito_conta_corrente': - move_line.payment_mode_id.bank_id.acc_number_dig + move_line.payment_mode_id.bank_id.acc_number_dig, }) - # TODO - Create or use a field to have byte_idt information - if move_line.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.bic == '748': + # TODO - Create or use a field to have + # byte_idt and posto information + if bank_account.bank_id.code_bc == '748': boleto_cnab_api_data.update({ 'byte_idt': '2', + 'posto': '01', }) wrapped_boleto_list.append(BoletoWrapper(boleto_cnab_api_data)) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index c5b2ebd3f21e..df1c93d8769b 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -37,7 +37,7 @@ '399': 'hsbc', '341': 'itau', '033': 'santander', - '748': 'sicredi', + '748': 'unicred', '004': 'banco_nordeste', '021': 'banestes', '756': 'sicoob', @@ -63,24 +63,23 @@ def generate_payment_file(self): # https://github.com/kivanio/brcobranca/tree/master/lib/brcobranca/remessa/cnab400 # and a test here: # https://github.com/kivanio/brcobranca/blob/master/spec/brcobranca/remessa/cnab400/itau_spec.rb + bank_account = \ + self.payment_mode_id.fixed_journal_id.bank_account_id - if self.payment_mode_id.fixed_journal_id.\ - bank_account_id.bank_id.code_bc in \ + if bank_account.bank_id.code_bc in \ dict_brcobranca_bank: bank_name_brcobranca = \ - dict_brcobranca_bank[ - self.payment_mode_id.fixed_journal_id - .bank_account_id.bank_id.code_bc], + dict_brcobranca_bank[bank_account.bank_id.code_bc], else: raise UserError( _('The Bank %s is not implemented in BRCobranca.') - % self.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.name) + % bank_account.bank_id.name) - if (bank_name_brcobranca[0] not in ('bradesco', 'itau') + if (bank_name_brcobranca[0] not in ('bradesco', 'itau', 'unicred') or self.payment_mode_id.payment_method_id.code != '400'): raise UserError( _('The Bank %s and CNAB %s are not implemented.') - % (self.payment_mode_id.fixed_journal_id.bank_account_id.bank_id.name, + % (bank_account.bank_id.name, self.payment_mode_id.payment_method_id.code)) pagamentos = [] @@ -127,14 +126,13 @@ def generate_payment_file(self): remessa_values = { 'carteira': str(self.payment_mode_id.boleto_carteira), - 'agencia': int(self.payment_mode_id.fixed_journal_id.bank_account_id.bra_number), + 'agencia': int(bank_account.bra_number), # 'digito_agencia': order.mode.bank_id.bra_number_dig, - 'conta_corrente': int(misc.punctuation_rm(self.payment_mode_id.fixed_journal_id.bank_account_id.acc_number)), - 'digito_conta': self.payment_mode_id.fixed_journal_id.bank_account_id.acc_number_dig[0], - 'empresa_mae': - self.payment_mode_id.fixed_journal_id.bank_account_id.partner_id.legal_name[:30], + 'conta_corrente': int(misc.punctuation_rm(bank_account.acc_number)), + 'digito_conta': bank_account.acc_number_dig[0], + 'empresa_mae': bank_account.partner_id.legal_name[:30], 'documento_cedente': misc.punctuation_rm( - self.payment_mode_id.fixed_journal_id.bank_account_id.partner_id.cnpj_cpf), + bank_account.partner_id.cnpj_cpf), 'pagamentos': pagamentos, 'sequencial_remessa': self.payment_mode_id.cnab_sequence_id.next_by_id(), } From cf6b133ec0f81bd30e5386b1cf88a146811e9cdc Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 18 Jun 2020 17:01:42 -0300 Subject: [PATCH 003/308] [REF] Move field CNAB Sequence to module l10n_br_account_payment_cobranca. --- .../__manifest__.py | 1 - .../models/__init__.py | 1 - .../models/account_payment_mode.py | 15 --------------- .../views/account_payment_mode_view.xml | 16 ---------------- 4 files changed, 33 deletions(-) delete mode 100644 l10n_br_account_payment_brcobranca/models/account_payment_mode.py delete mode 100644 l10n_br_account_payment_brcobranca/views/account_payment_mode_view.xml diff --git a/l10n_br_account_payment_brcobranca/__manifest__.py b/l10n_br_account_payment_brcobranca/__manifest__.py index 8682a08eb558..8adde4a29973 100644 --- a/l10n_br_account_payment_brcobranca/__manifest__.py +++ b/l10n_br_account_payment_brcobranca/__manifest__.py @@ -17,7 +17,6 @@ ], 'data': [ 'views/account_invoice_view.xml', - 'views/account_payment_mode_view.xml', 'views/l10n_br_cnab_retorno_view.xml', 'data/cnab_data.xml', ], diff --git a/l10n_br_account_payment_brcobranca/models/__init__.py b/l10n_br_account_payment_brcobranca/models/__init__.py index 207e6c4b797f..92e4243015e5 100644 --- a/l10n_br_account_payment_brcobranca/models/__init__.py +++ b/l10n_br_account_payment_brcobranca/models/__init__.py @@ -5,6 +5,5 @@ from . import account_move_line from . import account_invoice -from . import account_payment_mode from . import account_payment_order from . import l10n_br_cnab diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_mode.py b/l10n_br_account_payment_brcobranca/models/account_payment_mode.py deleted file mode 100644 index bf0ae1c94eec..000000000000 --- a/l10n_br_account_payment_brcobranca/models/account_payment_mode.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2017 Akretion -# @author Renato Lima -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import models, fields - - -class PaymentMode(models.Model): - _inherit = 'account.payment.mode' - - cnab_sequence_id = fields.Many2one( - comodel_name='ir.sequence', - string=u'Sequencia do CNAB') - diff --git a/l10n_br_account_payment_brcobranca/views/account_payment_mode_view.xml b/l10n_br_account_payment_brcobranca/views/account_payment_mode_view.xml deleted file mode 100644 index 27f46cf27b50..000000000000 --- a/l10n_br_account_payment_brcobranca/views/account_payment_mode_view.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - payment.mode.cnab.form.inherit - account.payment.mode - - - - - - - - - - From 1dcf57ebbe8432ce7abd322a4474571dbd632790 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Fri, 19 Jun 2020 15:39:38 -0300 Subject: [PATCH 004/308] [FIX] Implented fields used for Sicredi/Unicred, Sicoob Banks. --- .../models/account_move_line.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index a2b0902e92a2..322fa20e1c7c 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -182,12 +182,11 @@ def send_payment(self): move_line.payment_mode_id.bank_id.acc_number_dig, }) - # TODO - Create or use a field to have - # byte_idt and posto information + # Fields used in Sicredi/Unicred and Sicoob Banks if bank_account.bank_id.code_bc == '748': boleto_cnab_api_data.update({ - 'byte_idt': '2', - 'posto': '01', + 'byte_idt': move_line.payment_mode_id.boleto_byte_idt, + 'posto': move_line.payment_mode_id.boleto_posto, }) wrapped_boleto_list.append(BoletoWrapper(boleto_cnab_api_data)) From c728a1672b7481c93eb5572fd5aa5abf09f3d2f3 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 24 Jun 2020 12:44:17 -0300 Subject: [PATCH 005/308] [IMP] Remessa CNAB 400 for Sicredi/Unicred, Sicoob Banks. --- .../models/account_payment_order.py | 35 +++++++++++-------- .../models/l10n_br_cnab.py | 6 ++-- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index df1c93d8769b..ade68eedb9a0 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -142,6 +142,13 @@ def generate_payment_file(self): remessa_values[ 'codigo_empresa'] = int(self.payment_mode_id.codigo_convenio) + # Field used in Sicredi/Unicred and Sicoob Banks + if bank_account.bank_id.code_bc == '748': + # TODO - Verificar no manual de onde vem o campo e tirar o hardcode + remessa_values.update({ + 'codigo_transmissao': '01234567890123456789', + }) + content = json.dumps(remessa_values) f = open(tempfile.mktemp(), 'w') f.write(content) @@ -151,7 +158,6 @@ def generate_payment_file(self): # TODO - Name of service should be a parameter ? # For docky users check the name of service # defined in dev.docker-compose.yml - res = requests.post( "http://brcobranca:9292/api/remessa", data={ @@ -159,20 +165,20 @@ def generate_payment_file(self): self.payment_mode_id.payment_method_id.code], 'bank': bank_name_brcobranca[0], }, files=files) - # print "AAAAAAAA", res.status_code, str(res.status_code)[0] - #print('RES.CONTENT', res.content[0]) - - # TODO - Check If will be necessary keep code - # Test made in CNAB 400 Bradesco - #if res.content[0] == '0': - # remessa = res.content - #else: - # raise UserError(res.text) - - remessa = res.content + # print("AAAAAAAA", res.status_code, str(res.status_code)[0]) + # print('RES.CONTENT', res.content[0], type(res.content[0])) + + # TODO - Verificar a melhor forma de validação res.status_code ou + # res.content[0], na versão 8 foi feito esse if para o banco bradesco + # if bank_name_brcobranca[0] == 'bradesco' and res.content[0] == '0': + # remessa = res.content + if res.status_code == 201: + remessa = res.content + else: + raise UserError(res.text) - #self.state = 'done' - #self.cnab_file = base64.b64encode(remessa) + # self.state = 'done' + # self.cnab_file = base64.b64encode(remessa) # Criando instancia do CNAB a partir do código do banco # cnab = Cnab.get_cnab( @@ -180,7 +186,6 @@ def generate_payment_file(self): # remessa = cnab.remessa(order) - file_name = '' if self.payment_mode_id.payment_method_id.code == '240': file_name = 'CB%s%s.REM' % ( time.strftime('%d%m'), str(self.file_number)) diff --git a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py index 49fe17952a64..940285937e3b 100644 --- a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py +++ b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py @@ -174,7 +174,9 @@ class L10nBrHrCnab(models.Model): states={'draft': [('readonly', False)]} ) bank = fields.Selection( - [('bradesco', u'Bradesco'), ('itau', u'Itaú')], + [('bradesco', 'Bradesco'), + ('itau', 'Itaú'), + ('unicred', 'Unicred')], string='Bank', states={'draft': [('readonly', False)]} ) @@ -183,7 +185,7 @@ def processar_arquivo_retorno(self): files = {'data': base64.b64decode(self.arquivo_retorno)} res = requests.post( - "http://boleto_cnab_api:9292/api/retorno", + "http://brcobranca:9292/api/retorno", data={ 'type': self.cnab_type, 'bank': self.bank, From 8193de83da23d5e2bd70cd4357cdc9b1f733d3b4 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 25 Jun 2020 18:20:33 -0300 Subject: [PATCH 006/308] [FIX] Avoid error with address fields empty. --- .../models/account_move_line.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index 322fa20e1c7c..3a79f837df31 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -134,18 +134,17 @@ def send_payment(self): '%Y-%m-%d').strftime('%d/%m/%Y'), ('%.2f' % valor_desconto).replace('.', ',') )) - boleto_cnab_api_data = { '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 + ', ' + - move_line.company_id.partner_id.street_number + ' - ' + - move_line.company_id.partner_id.district + ' - ' + - move_line.company_id.partner_id.city_id.name - + ' - ' + 'CEP:' + move_line.company_id.partner_id.zip - + ' - ' + move_line.company_id.partner_id.state_id.code, + move_line.company_id.partner_id.street 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 '' + + ' - ' + 'CEP:' + move_line.company_id.partner_id.zip or '' + + ' - ' + move_line.company_id.partner_id.state_id.code or '', 'documento_cedente': move_line.company_id.cnpj_cpf, 'sacado': move_line.partner_id.legal_name, 'sacado_documento': move_line.partner_id.cnpj_cpf, @@ -164,10 +163,10 @@ def send_payment(self): 'moeda': dict_brcobranca_currency['R$'], 'aceite': move_line.payment_mode_id.boleto_aceite, 'sacado_endereco': - move_line.partner_id.street + ', ' + - move_line.partner_id.street_number + ' ' + - move_line.partner_id.city_id.name + ' - ' + - move_line.partner_id.state_id.name, + move_line.partner_id.street or '' + ', ' + + move_line.partner_id.street_number or '' + ' ' + + move_line.partner_id.city_id.name or '' + ' - ' + + move_line.partner_id.state_id.name or '', 'data_processamento': move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), 'instrucao1': move_line.payment_mode_id.instrucoes or '', From 14d3ee1e0c706b88dd9f64e6bc120b032026a7ed Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Fri, 26 Jun 2020 16:51:28 -0300 Subject: [PATCH 007/308] [FIX] Included the right code for Unicred bank. --- .../models/account_move_line.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index 3a79f837df31..5493cb2b6838 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -28,8 +28,6 @@ def __getattr__(self, attr): # proxy to the wrapped object return getattr(self._wrapped_obj, attr) -# TODO Sicredi e Unicred possuem o mesmo codigo 748, -# isso estaria certo ? Como diferenciar dict_brcobranca_bank = { '001': 'banco_brasil', @@ -39,10 +37,11 @@ def __getattr__(self, attr): '399': 'hsbc', '341': 'itau', '033': 'santander', - '748': 'unicred', + '748': 'sicred', '004': 'banco_nordeste', '021': 'banestes', '756': 'sicoob', + '136': 'unicred', } dict_brcobranca_currency = { From e244e6a2e50036369e7ce85196090c5e4670d891 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Sun, 28 Jun 2020 18:43:18 -0300 Subject: [PATCH 008/308] =?UTF-8?q?[WIP]=20Configura=C3=A7=C3=A3o=20do=20a?= =?UTF-8?q?rquivo=20de=20Remessa=20CNAB400=20para=20o=20banco=20UNICRED.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/account_payment_order.py | 89 +++++++++++++++++-- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index ade68eedb9a0..afb47652ccc4 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -37,10 +37,11 @@ '399': 'hsbc', '341': 'itau', '033': 'santander', - '748': 'unicred', + '748': 'sicred', '004': 'banco_nordeste', '021': 'banestes', '756': 'sicoob', + '136': 'unicred', } dict_brcobranca_cnab_type = { @@ -103,10 +104,54 @@ def generate_payment_file(self): 'uf_sacado': line.partner_id.state_id.code, 'identificacao_ocorrencia': self.codigo_instrucao_movimento } + if bank_name_brcobranca[0] == 'unicred': + # TODO - Verificar se é uma tabela unica por banco ou há padrão + # Identificação da Ocorrência: + # 01 - Remessa* + # 02 - Pedido de Baixa + # 04 - Concessão de Abatimento* + # 05 - Cancelamento de Abatimento + # 06 - Alteração de vencimento + # 08 - Alteração de Seu Número + # 09 - Protestar* + # 11 - Sustar Protesto e Manter em Carteira + # 25 - Sustar Protesto e Baixar Título + # 26 – Protesto automático + # 31 - Alteração de outros da + linhas_pagamentos['identificacao_ocorrencia'] = '01' + if line.move_line_id.payment_mode_id.boleto_perc_mora: - linhas_pagamentos['codigo_multa'] = '2' - linhas_pagamentos['percentual_multa'] = \ - line.move_line_id.payment_mode_id.boleto_perc_mora + if bank_name_brcobranca[0] == 'bradesco': + linhas_pagamentos['codigo_multa'] = '2' + linhas_pagamentos['percentual_multa'] = \ + line.move_line_id.payment_mode_id.boleto_perc_mora + if bank_name_brcobranca[0] == 'unicred': + # TODO + # Código adotado pela FEBRABAN para identificação + # do tipo de pagamento de multa. + # Domínio: + # ‘1’ = Valor Fixo (R$) + # ‘2’ = Taxa (%) + # ‘3’ = Isento + linhas_pagamentos['codigo_multa'] = '2' + linhas_pagamentos['percentual_multa'] = \ + line.move_line_id.payment_mode_id.boleto_perc_mora + # TODO + # Código para Protesto* + # Código adotado pela FEBRABAN para identificar o tipo de prazo a ser considerado para o + # protesto. + # Domínio: + # 1 = Protestar Dias Corridos + # 2 = Protestar Dias Úteis + # 3 = Não Protestar + # OBS.: a) Na remessa de títulos - Identificação da Ocorrência igual a ‘01’ - Remessa: + # Qualquer caracter diferente de ‘1’, ‘2’ ou ‘3’ será considerado igual a ‘3’. + # 158 a 158 + # b) Na instrução de Protesto Automático - Identificação da Ocorrência ‘26’ - + # Protesto automático: + # Qualquer caracter diferente de ‘1’, ‘2’ ou ‘3’, a instrução será rejeitada. + linhas_pagamentos['codigo_protesto'] = 3 + precision = self.env['decimal.precision'] precision_account = precision.precision_get('Account') if line.move_line_id.payment_mode_id.cnab_percent_interest: @@ -114,6 +159,21 @@ def generate_payment_file(self): line.move_line_id.debit * ((line.move_line_id.payment_mode_id.cnab_percent_interest / 100) / 30), precision_account) + if bank_name_brcobranca[0] == 'unicred': + # TODO + # Código adotado pela FEBRABAN para identificação do tipo de + # pagamento de mora de juros. + # Domínio: + # ‘1’ = Valor Diário (R$) + # ‘2’ = Taxa Mensal (%) + # ‘3’= Valor Mensal (R$) * + # ‘4’ = Taxa diária (%) + # ‘5’ = Isento + # *OBSERVAÇÃO: + # ‘3’ - Valor Mensal (R$): a CIP não acata valor mensal, + # segundo manual. Cógido mantido + # para Correspondentes que ainda utilizam. + linhas_pagamentos['tipo_mora'] = '1' if line.move_line_id.payment_term_id.discount_perc: linhas_pagamentos['data_desconto'] =\ line.move_line_id.date_maturity.strftime('%Y/%m/%d') @@ -121,6 +181,13 @@ def generate_payment_file(self): line.move_line_id.debit * ( line.move_line_id.payment_term_id.discount_perc / 100), precision_account) + if bank_name_brcobranca[0] == 'unicred': + # TODO + # Código adotado pela FEBRABAN para identificação do desconto. + # Domínio: + # 0 = Isento + # 1 = Valor Fixo + linhas_pagamentos['cod_desconto'] = 1 pagamentos.append(linhas_pagamentos) @@ -148,6 +215,11 @@ def generate_payment_file(self): remessa_values.update({ 'codigo_transmissao': '01234567890123456789', }) + # Field used in Unicred Bank + if bank_account.bank_id.code_bc == '136': + remessa_values.update({ + 'codigo_beneficiario': int(self.payment_mode_id.codigo_convenio), + }) content = json.dumps(remessa_values) f = open(tempfile.mktemp(), 'w') @@ -168,11 +240,10 @@ def generate_payment_file(self): # print("AAAAAAAA", res.status_code, str(res.status_code)[0]) # print('RES.CONTENT', res.content[0], type(res.content[0])) - # TODO - Verificar a melhor forma de validação res.status_code ou - # res.content[0], na versão 8 foi feito esse if para o banco bradesco - # if bank_name_brcobranca[0] == 'bradesco' and res.content[0] == '0': - # remessa = res.content - if res.status_code == 201: + # TODO - res.content[0] parece variar de acordo com banco, existe padrão ? + if bank_name_brcobranca[0] == 'bradesco' and res.content[0] == '0': + remessa = res.content + elif bank_name_brcobranca[0] == 'unicred' and res.content[0] == 48: remessa = res.content else: raise UserError(res.text) From aea5aafd49359578071fa86c4fb0ce016334c07a Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 2 Jul 2020 16:46:24 -0300 Subject: [PATCH 009/308] Created parameter to inform Boleto CNAB API address. --- .../__manifest__.py | 2 + .../data/res_config_settings_data.xml | 13 +++++++ .../models/__init__.py | 1 + .../models/account_invoice.py | 22 ++++++++--- .../models/account_payment_order.py | 19 ++++++++-- .../models/l10n_br_cnab.py | 13 ++++++- .../models/res_config_settings.py | 38 +++++++++++++++++++ .../views/res_config_settings_view.xml | 28 ++++++++++++++ 8 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml create mode 100644 l10n_br_account_payment_brcobranca/models/res_config_settings.py create mode 100644 l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml diff --git a/l10n_br_account_payment_brcobranca/__manifest__.py b/l10n_br_account_payment_brcobranca/__manifest__.py index 8adde4a29973..ce440eb2891d 100644 --- a/l10n_br_account_payment_brcobranca/__manifest__.py +++ b/l10n_br_account_payment_brcobranca/__manifest__.py @@ -18,7 +18,9 @@ 'data': [ 'views/account_invoice_view.xml', 'views/l10n_br_cnab_retorno_view.xml', + 'views/res_config_settings_view.xml', 'data/cnab_data.xml', + 'data/res_config_settings_data.xml', ], 'demo': [ ], diff --git a/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml b/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml new file mode 100644 index 000000000000..1c93af57ec2a --- /dev/null +++ b/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml @@ -0,0 +1,13 @@ + + + + + boleto_cnab_api + + + + + + + \ No newline at end of file diff --git a/l10n_br_account_payment_brcobranca/models/__init__.py b/l10n_br_account_payment_brcobranca/models/__init__.py index 92e4243015e5..dcbea94ecb4a 100644 --- a/l10n_br_account_payment_brcobranca/models/__init__.py +++ b/l10n_br_account_payment_brcobranca/models/__init__.py @@ -7,3 +7,4 @@ from . import account_invoice from . import account_payment_order from . import l10n_br_cnab +from . import res_config_settings diff --git a/l10n_br_account_payment_brcobranca/models/account_invoice.py b/l10n_br_account_payment_brcobranca/models/account_invoice.py index 910053e34ca3..8d0e60b76579 100644 --- a/l10n_br_account_payment_brcobranca/models/account_invoice.py +++ b/l10n_br_account_payment_brcobranca/models/account_invoice.py @@ -45,15 +45,25 @@ def gera_boleto_pdf(self): f.close() files = {'data': open(f.name, 'rb')} - # TODO - Name of service should be a parameter ? - # For docky users check the name of service - # defined in dev.docker-compose.yml - res = requests.post("http://brcobranca:9292/api/boleto/multi", - data={'type': 'pdf'}, files=files) + api_address = self.env[ + "ir.config_parameter"].sudo().get_param( + "l10n_br_account_payment_brcobranca.boleto_cnab_api") + + if not api_address: + raise UserError( + ('Não é possível gerar os boletos.\n' + 'Informe o Endereço IP ou Nome do' + ' Boleto CNAB API.')) + + api_service_address = \ + 'http://' + api_address + ':9292/api/boleto/multi' + res = requests.post( + api_service_address, data={'type': 'pdf'}, files=files) + if str(res.status_code)[0] == '2': pdf_string = res.content else: - raise UserError(res.text.encode('utf-8')) + raise UserError(res.text.encode('utf-8')) # TODO - Name File file_name = 'boleto-teste.pdf' diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index afb47652ccc4..3d7c69606aa7 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -64,6 +64,7 @@ def generate_payment_file(self): # https://github.com/kivanio/brcobranca/tree/master/lib/brcobranca/remessa/cnab400 # and a test here: # https://github.com/kivanio/brcobranca/blob/master/spec/brcobranca/remessa/cnab400/itau_spec.rb + bank_account = \ self.payment_mode_id.fixed_journal_id.bank_account_id @@ -227,11 +228,21 @@ def generate_payment_file(self): f.close() files = {'data': open(f.name, 'rb')} - # TODO - Name of service should be a parameter ? - # For docky users check the name of service - # defined in dev.docker-compose.yml + api_address = self.env[ + "ir.config_parameter"].sudo().get_param( + "l10n_br_account_payment_brcobranca.boleto_cnab_api") + + if not api_address: + raise UserError( + ('Não é possível gerar os remessa\n' + 'Informe o Endereço IP ou Nome do' + 'Boleto CNAB API.')) + + # EX.: "http://boleto_cnab_api:9292/api/remessa" + api_service_address = \ + 'http://' + api_address + ':9292/api/remessa' res = requests.post( - "http://brcobranca:9292/api/remessa", + api_service_address, data={ 'type': dict_brcobranca_cnab_type[ self.payment_mode_id.payment_method_id.code], diff --git a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py index 940285937e3b..5a11d39f60ad 100644 --- a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py +++ b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py @@ -184,8 +184,19 @@ class L10nBrHrCnab(models.Model): def processar_arquivo_retorno(self): files = {'data': base64.b64decode(self.arquivo_retorno)} + api_address = self.env[ + "ir.config_parameter"].sudo().get_param( + "l10n_br_account_payment_brcobranca.boleto_cnab_api") + if not api_address: + raise UserError( + ('Não é possível gerar o retorno.\n' + 'Informe o Endereço IP ou Nome do' + ' Boleto CNAB API.')) + # Ex.: "http://boleto_cnab_api:9292/api/retorno" + api_service_address = \ + 'http://' + api_address + ':9292/api/retorno' res = requests.post( - "http://brcobranca:9292/api/retorno", + api_service_address, data={ 'type': self.cnab_type, 'bank': self.bank, diff --git a/l10n_br_account_payment_brcobranca/models/res_config_settings.py b/l10n_br_account_payment_brcobranca/models/res_config_settings.py new file mode 100644 index 000000000000..4b7c4fe08cc0 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/models/res_config_settings.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Akretion - Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields, api + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + # TODO - default don't work, solve with data file + boleto_cnab_api = fields.Char( + string='Endereço do Boleto CNAB API / BRCobranca', + help='Endereço Eletrônico IP ou Nome do Boleto CNAB API.', + default='boleto_cnab_api' + ) + + @api.model + def get_values(self): + res = super(ResConfigSettings, self).get_values() + res.update( + boleto_cnab_api=self.env[ + 'ir.config_parameter'].sudo().get_param( + 'l10n_br_account_payment_brcobranca.boleto_cnab_api') + ) + return res + + @api.multi + def set_values(self): + super(ResConfigSettings, self).set_values() + param = self.env['ir.config_parameter'].sudo() + + boleto_cnab_api =\ + self.boleto_cnab_api or 'boleto_cnab_api' + + param.set_param( + 'l10n_br_account_payment_brcobranca.boleto_cnab_api', + boleto_cnab_api) diff --git a/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml b/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml new file mode 100644 index 000000000000..96925cd37d5e --- /dev/null +++ b/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml @@ -0,0 +1,28 @@ + + + + + res.config.settings.view.form + res.config.settings + + + +
+
+
+
+
+ + + + + From 2e86c2b82a147064648ea0105fbc9a9b81147caa Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 2 Jul 2020 16:50:22 -0300 Subject: [PATCH 010/308] [FIX] Data was moved to l10n_br_account_payment_cobranca. --- l10n_br_account_payment_brcobranca/__manifest__.py | 1 - .../data/cnab_data.xml | 12 ------------ 2 files changed, 13 deletions(-) delete mode 100644 l10n_br_account_payment_brcobranca/data/cnab_data.xml diff --git a/l10n_br_account_payment_brcobranca/__manifest__.py b/l10n_br_account_payment_brcobranca/__manifest__.py index ce440eb2891d..9ecd3f228bf8 100644 --- a/l10n_br_account_payment_brcobranca/__manifest__.py +++ b/l10n_br_account_payment_brcobranca/__manifest__.py @@ -19,7 +19,6 @@ 'views/account_invoice_view.xml', 'views/l10n_br_cnab_retorno_view.xml', 'views/res_config_settings_view.xml', - 'data/cnab_data.xml', 'data/res_config_settings_data.xml', ], 'demo': [ diff --git a/l10n_br_account_payment_brcobranca/data/cnab_data.xml b/l10n_br_account_payment_brcobranca/data/cnab_data.xml deleted file mode 100644 index 8a331242dee1..000000000000 --- a/l10n_br_account_payment_brcobranca/data/cnab_data.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Sequencia CNAB - Sequencia CNAB - - - - - - \ No newline at end of file From 32f9232f958b51e601f86c8df8a64711933ca4ed Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 2 Jul 2020 16:57:14 -0300 Subject: [PATCH 011/308] [FIX] Configure Boleto file name. --- l10n_br_account_payment_brcobranca/models/account_invoice.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_invoice.py b/l10n_br_account_payment_brcobranca/models/account_invoice.py index 8d0e60b76579..5a49dea66003 100644 --- a/l10n_br_account_payment_brcobranca/models/account_invoice.py +++ b/l10n_br_account_payment_brcobranca/models/account_invoice.py @@ -65,8 +65,7 @@ def gera_boleto_pdf(self): else: raise UserError(res.text.encode('utf-8')) - # TODO - Name File - file_name = 'boleto-teste.pdf' + file_name = 'boleto-' + self.number + '.pdf' self.file_pdf_id = self.env['ir.attachment'].create( { From 9e0dbc6d2afd25ceff3cfecd01ac50408437f508 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Mon, 6 Jul 2020 09:24:49 -0300 Subject: [PATCH 012/308] [REF] Remessa Unicred values. --- .../models/account_payment_order.py | 105 +++++++++--------- 1 file changed, 50 insertions(+), 55 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 3d7c69606aa7..795dbd019aef 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -118,63 +118,64 @@ def generate_payment_file(self): # 11 - Sustar Protesto e Manter em Carteira # 25 - Sustar Protesto e Baixar Título # 26 – Protesto automático - # 31 - Alteração de outros da + # 31 - Alteração de outros dados (Alteração de dados do pagador) + # 40 - Alteração de Carteira linhas_pagamentos['identificacao_ocorrencia'] = '01' - - if line.move_line_id.payment_mode_id.boleto_perc_mora: - if bank_name_brcobranca[0] == 'bradesco': - linhas_pagamentos['codigo_multa'] = '2' - linhas_pagamentos['percentual_multa'] = \ - line.move_line_id.payment_mode_id.boleto_perc_mora - if bank_name_brcobranca[0] == 'unicred': - # TODO - # Código adotado pela FEBRABAN para identificação - # do tipo de pagamento de multa. - # Domínio: - # ‘1’ = Valor Fixo (R$) - # ‘2’ = Taxa (%) - # ‘3’ = Isento - linhas_pagamentos['codigo_multa'] = '2' + linhas_pagamentos['codigo_protesto'] = \ + line.move_line_id.payment_mode_id.boleto_cod_protesto or '3' + linhas_pagamentos['dias_protesto'] = \ + line.move_line_id.payment_mode_id.boleto_dias_protesto + + # Código adotado pela FEBRABAN para identificação + # do tipo de pagamento de multa. + # Domínio: + # ‘1’ = Valor Fixo (R$) + # ‘2’ = Taxa (%) + # ‘3’ = Isento + # Isento de Multa caso não exista percentual + linhas_pagamentos['codigo_multa'] = '3' + + # Código adotado pela FEBRABAN para identificação do tipo de + # pagamento de mora de juros. + # Domínio: + # ‘1’ = Valor Diário (R$) + # ‘2’ = Taxa Mensal (%) + # ‘3’= Valor Mensal (R$) * + # ‘4’ = Taxa diária (%) + # ‘5’ = Isento + # *OBSERVAÇÃO: + # ‘3’ - Valor Mensal (R$): a CIP não acata valor mensal, + # segundo manual. Cógido mantido + # para Correspondentes que ainda utilizam. + # Isento de Mora caso não exista percentual + linhas_pagamentos['tipo_mora'] = '5' + + # TODO + # Código adotado pela FEBRABAN para identificação do desconto. + # Domínio: + # 0 = Isento + # 1 = Valor Fixo + linhas_pagamentos['cod_desconto'] = '0' + + if line.move_line_id.payment_mode_id.boleto_perc_multa: + if bank_name_brcobranca[0] in ('bradesco', 'unicred'): + linhas_pagamentos['codigo_multa'] = \ + line.move_line_id.payment_mode_id.boleto_cod_multa linhas_pagamentos['percentual_multa'] = \ - line.move_line_id.payment_mode_id.boleto_perc_mora - # TODO - # Código para Protesto* - # Código adotado pela FEBRABAN para identificar o tipo de prazo a ser considerado para o - # protesto. - # Domínio: - # 1 = Protestar Dias Corridos - # 2 = Protestar Dias Úteis - # 3 = Não Protestar - # OBS.: a) Na remessa de títulos - Identificação da Ocorrência igual a ‘01’ - Remessa: - # Qualquer caracter diferente de ‘1’, ‘2’ ou ‘3’ será considerado igual a ‘3’. - # 158 a 158 - # b) Na instrução de Protesto Automático - Identificação da Ocorrência ‘26’ - - # Protesto automático: - # Qualquer caracter diferente de ‘1’, ‘2’ ou ‘3’, a instrução será rejeitada. - linhas_pagamentos['codigo_protesto'] = 3 + line.move_line_id.payment_mode_id.boleto_perc_multa precision = self.env['decimal.precision'] precision_account = precision.precision_get('Account') - if line.move_line_id.payment_mode_id.cnab_percent_interest: + if line.move_line_id.payment_mode_id.boleto_perc_mora: linhas_pagamentos['valor_mora'] = round( line.move_line_id.debit * - ((line.move_line_id.payment_mode_id.cnab_percent_interest / 100) + ((line.move_line_id.payment_mode_id.boleto_perc_mora / 100) / 30), precision_account) if bank_name_brcobranca[0] == 'unicred': - # TODO - # Código adotado pela FEBRABAN para identificação do tipo de - # pagamento de mora de juros. - # Domínio: - # ‘1’ = Valor Diário (R$) - # ‘2’ = Taxa Mensal (%) - # ‘3’= Valor Mensal (R$) * - # ‘4’ = Taxa diária (%) - # ‘5’ = Isento - # *OBSERVAÇÃO: - # ‘3’ - Valor Mensal (R$): a CIP não acata valor mensal, - # segundo manual. Cógido mantido - # para Correspondentes que ainda utilizam. - linhas_pagamentos['tipo_mora'] = '1' + linhas_pagamentos['tipo_mora'] =\ + line.move_line_id.payment_mode_id.boleto_cod_mora + + # TODO - Definir como sera implementado a configuração do Desconto if line.move_line_id.payment_term_id.discount_perc: linhas_pagamentos['data_desconto'] =\ line.move_line_id.date_maturity.strftime('%Y/%m/%d') @@ -183,11 +184,6 @@ def generate_payment_file(self): line.move_line_id.payment_term_id.discount_perc / 100), precision_account) if bank_name_brcobranca[0] == 'unicred': - # TODO - # Código adotado pela FEBRABAN para identificação do desconto. - # Domínio: - # 0 = Isento - # 1 = Valor Fixo linhas_pagamentos['cod_desconto'] = 1 pagamentos.append(linhas_pagamentos) @@ -212,9 +208,8 @@ def generate_payment_file(self): # Field used in Sicredi/Unicred and Sicoob Banks if bank_account.bank_id.code_bc == '748': - # TODO - Verificar no manual de onde vem o campo e tirar o hardcode remessa_values.update({ - 'codigo_transmissao': '01234567890123456789', + 'codigo_transmissao': int(self.payment_mode_id.codigo_convenio), }) # Field used in Unicred Bank if bank_account.bank_id.code_bc == '136': From 5f28721a25737775557e313c7177df6174ae81be Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Mon, 6 Jul 2020 09:25:16 -0300 Subject: [PATCH 013/308] [REF] Retorno Unicred values. --- .../models/l10n_br_cnab.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py index 5a11d39f60ad..fc6d066c0484 100644 --- a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py +++ b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py @@ -157,6 +157,21 @@ '93': u'TARIFA MENSAL DE EXCLUSÃO DE NEGATIVAÇÃO EXPRESSA POR LIQUIDAÇÃO', } +DICT_OCORRENCIAS_UNICRED = { + '01': '01 - Pago (Título protestado pago em cartório)', + '02': '02 - Instrução Confirmada*', + '03': '03 - Instrução Rejeitada*', + '04': '04 - Sustado Judicial (Título protestado sustado judicialmente)', + '06': '06 - Liquidação Normal *', + '07': '07 - Liquidação em Condicional (Título liquidado em cartório com' + ' cheque do próprio devedor)', + '08': '08 - Sustado Definitivo (Título protestado sustado judicialmente)', + '09': '09 - Liquidação de Título Descontado', + '10': '10 - Protesto solicitado', + '11': '11 - Protesto Em cartório', + '12': '12 - Sustação solicitada' +} + class L10nBrHrCnab(models.Model): _inherit = "l10n_br.cnab" @@ -264,6 +279,9 @@ def processar_arquivo_retorno_cnab400(self, data): if self.bank == 'itau': descricao_ocorrencia = DICT_OCORRENCIAS_ITAU[ dict_line['codigo_ocorrencia']].encode('utf-8') + if self.bank == 'unicred': + descricao_ocorrencia = DICT_OCORRENCIAS_UNICRED[ + dict_line['codigo_ocorrencia']].encode('utf-8') if not account_move_line: vals_evento = { From 80dee6e95654c582057b541f4d95362d1b8a43f0 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Mon, 6 Jul 2020 09:47:30 -0300 Subject: [PATCH 014/308] [FIX] Remessa Unicred values. --- .../models/account_payment_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 795dbd019aef..de0a7809cb86 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -124,7 +124,7 @@ def generate_payment_file(self): linhas_pagamentos['codigo_protesto'] = \ line.move_line_id.payment_mode_id.boleto_cod_protesto or '3' linhas_pagamentos['dias_protesto'] = \ - line.move_line_id.payment_mode_id.boleto_dias_protesto + line.move_line_id.payment_mode_id.boleto_dias_protesto or '0' # Código adotado pela FEBRABAN para identificação # do tipo de pagamento de multa. From ea5ea7da6d94128034cdd6ece4b78f7efc9d74a7 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Mon, 6 Jul 2020 10:16:02 -0300 Subject: [PATCH 015/308] [REF] Boleto print button just appear in Open Invoice state. --- .../views/account_invoice_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_br_account_payment_brcobranca/views/account_invoice_view.xml b/l10n_br_account_payment_brcobranca/views/account_invoice_view.xml index 382dc41c6ba3..d1e55a017ce9 100644 --- a/l10n_br_account_payment_brcobranca/views/account_invoice_view.xml +++ b/l10n_br_account_payment_brcobranca/views/account_invoice_view.xml @@ -7,7 +7,7 @@ From fdf3d478dfaa9cc4283a81f359bda743d9302514 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Mon, 6 Jul 2020 15:38:11 -0300 Subject: [PATCH 016/308] =?UTF-8?q?[FIX]=20Instru=C3=A7=C3=B5es=20Mora,Mul?= =?UTF-8?q?ta=20e=20Desconto=20dos=20Boletos.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/account_move_line.py | 124 ++++++++++-------- 1 file changed, 68 insertions(+), 56 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index 5493cb2b6838..a73fb1e377f8 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -78,12 +78,52 @@ def send_payment(self): precision = self.env['decimal.precision'] precision_account = precision.precision_get('Account') - instrucao_juros = "" - instrucao_juros_tmp = "APÓS VENCIMENTO COBRAR PERCENTUAL" - if move_line.payment_mode_id.instrucao_boleto_perc_mora: - instrucao_juros_tmp = \ - move_line.payment_mode_id.instrucao_boleto_perc_mora + boleto_cnab_api_data = { + '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 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 '' + + ' - ' + 'CEP:' + move_line.company_id.partner_id.zip or '' + + ' - ' + move_line.company_id.partner_id.state_id.code or '', + 'documento_cedente': move_line.company_id.cnpj_cpf, + 'sacado': move_line.partner_id.legal_name, + 'sacado_documento': move_line.partner_id.cnpj_cpf, + 'agencia': bank_account.bra_number, + 'conta_corrente': bank_account.acc_number, + 'convenio': move_line.payment_mode_id.boleto_convenio, + 'carteira': str(move_line.payment_mode_id.boleto_carteira), + 'nosso_numero': int(''.join( + i for i in move_line.nosso_numero if i.isdigit())), + 'documento_numero': move_line.name, + 'data_vencimento': + move_line.date_maturity.strftime('%Y/%m/%d'), + 'data_documento': + move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), + 'especie': move_line.payment_mode_id.boleto_especie, + 'moeda': dict_brcobranca_currency['R$'], + 'aceite': move_line.payment_mode_id.boleto_aceite, + 'sacado_endereco': + move_line.partner_id.street or '' + ', ' + + move_line.partner_id.street_number or '' + ' ' + + move_line.partner_id.city_id.name or '' + ' - ' + + move_line.partner_id.state_id.name or '', + 'data_processamento': + move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), + 'instrucao1': move_line.payment_mode_id.instrucoes or '', + } + + # Instrução de Juros if move_line.payment_mode_id.boleto_perc_mora: + instrucao_juros = "" + instrucao_juros_tmp = "APÓS VENCIMENTO COBRAR PERCENTUAL" + if move_line.payment_mode_id.instrucao_boleto_perc_mora: + instrucao_juros_tmp = \ + move_line.payment_mode_id.instrucao_boleto_perc_mora + valor_juros = round( move_line.debit * ((move_line.payment_mode_id.boleto_perc_mora / 100) @@ -95,13 +135,18 @@ def send_payment(self): move_line.payment_mode_id.boleto_perc_mora ).replace('.', ','), ('%.2f' % valor_juros).replace('.', ','))) + boleto_cnab_api_data.update({ + 'instrucao3': instrucao_juros, + }) - instrucao_multa = '' - instrucao_multa_tmp = "APÓS VENCIMENTO COBRAR MULTA" - if move_line.payment_mode_id.instrucao_boleto_perc_multa: - instrucao_multa_tmp = \ - move_line.payment_mode_id.instrucao_boleto_perc_multa + # Instrução Multa if move_line.payment_mode_id.boleto_perc_mora: + instrucao_multa = '' + instrucao_multa_tmp = "APÓS VENCIMENTO COBRAR MULTA" + if move_line.payment_mode_id.instrucao_boleto_perc_multa: + instrucao_multa_tmp = \ + move_line.payment_mode_id.instrucao_boleto_perc_multa + valor_multa = round(move_line.debit * ( (move_line.payment_mode_id.boleto_perc_multa / 100) ), precision_account) @@ -111,14 +156,18 @@ def send_payment(self): (('%.2f' % move_line.payment_mode_id.boleto_perc_multa ).replace('.', ','), ('%.2f' % valor_multa).replace('.', ','))) + boleto_cnab_api_data.update({ + 'instrucao4': instrucao_multa, + }) - instrucao_desconto_vencimento = '' - instrucao_desconto_vencimento_tmp =\ - 'CONCEDER ABATIMENTO PERCENTUAL DE' - if move_line.payment_term_id.instrucao_discount_perc: - instrucao_desconto_vencimento_tmp =\ - move_line.payment_term_id.instrucao_discount_perc + # Instrução Desconto if move_line.payment_term_id.discount_perc: + instrucao_desconto_vencimento = '' + instrucao_desconto_vencimento_tmp = \ + 'CONCEDER ABATIMENTO PERCENTUAL DE' + if move_line.payment_term_id.instrucao_discount_perc: + instrucao_desconto_vencimento_tmp = \ + move_line.payment_term_id.instrucao_discount_per valor_desconto = round( move_line.debit * ( move_line.payment_term_id.discount_perc / 100), @@ -133,46 +182,9 @@ def send_payment(self): '%Y-%m-%d').strftime('%d/%m/%Y'), ('%.2f' % valor_desconto).replace('.', ',') )) - boleto_cnab_api_data = { - '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 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 '' - + ' - ' + 'CEP:' + move_line.company_id.partner_id.zip or '' - + ' - ' + move_line.company_id.partner_id.state_id.code or '', - 'documento_cedente': move_line.company_id.cnpj_cpf, - 'sacado': move_line.partner_id.legal_name, - 'sacado_documento': move_line.partner_id.cnpj_cpf, - 'agencia': bank_account.bra_number, - 'conta_corrente': bank_account.acc_number, - 'convenio': move_line.payment_mode_id.boleto_convenio, - 'carteira': str(move_line.payment_mode_id.boleto_carteira), - 'nosso_numero': int(''.join( - i for i in move_line.nosso_numero if i.isdigit())), - 'documento_numero': move_line.name, - 'data_vencimento': - move_line.date_maturity.strftime('%Y/%m/%d'), - 'data_documento': - move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), - 'especie': move_line.payment_mode_id.boleto_especie, - 'moeda': dict_brcobranca_currency['R$'], - 'aceite': move_line.payment_mode_id.boleto_aceite, - 'sacado_endereco': - move_line.partner_id.street or '' + ', ' + - move_line.partner_id.street_number or '' + ' ' + - move_line.partner_id.city_id.name or '' + ' - ' + - move_line.partner_id.state_id.name or '', - 'data_processamento': - move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), - 'instrucao1': move_line.payment_mode_id.instrucoes or '', - 'instrucao3': instrucao_juros, - 'instrucao4': instrucao_multa, - 'instrucao5': instrucao_desconto_vencimento, - } + boleto_cnab_api_data.update({ + 'instrucao4': instrucao_desconto_vencimento, + }) if bank_account.bank_id.code_bc in ('021', '004'): boleto_cnab_api_data.update({ From 1ed2e27c128e87b8adbe6b7de2de26050779c4a5 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Mon, 6 Jul 2020 16:20:17 -0300 Subject: [PATCH 017/308] [FIX] Field to inform Numero do documento. --- l10n_br_account_payment_brcobranca/models/account_move_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index a73fb1e377f8..ceff66aae277 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -98,7 +98,7 @@ def send_payment(self): 'carteira': str(move_line.payment_mode_id.boleto_carteira), 'nosso_numero': int(''.join( i for i in move_line.nosso_numero if i.isdigit())), - 'documento_numero': move_line.name, + 'documento_numero': move_line.numero_documento, 'data_vencimento': move_line.date_maturity.strftime('%Y/%m/%d'), 'data_documento': From e05491a230372f6e4e36d6841c3966f4d4cdcace Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Mon, 6 Jul 2020 16:34:52 -0300 Subject: [PATCH 018/308] [FIX] Raise receive just one argument. --- .../models/account_invoice.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_invoice.py b/l10n_br_account_payment_brcobranca/models/account_invoice.py index 5a49dea66003..7b4555221452 100644 --- a/l10n_br_account_payment_brcobranca/models/account_invoice.py +++ b/l10n_br_account_payment_brcobranca/models/account_invoice.py @@ -33,9 +33,9 @@ def gera_boleto_pdf(self): boleto_list = receivable_ids.send_payment() if not boleto_list: raise UserError( - 'Error !', ('Não é possível gerar os boletos\n' - 'Certifique-se que a fatura esteja confirmada e o ' - 'forma de pagamento seja duplicatas')) + ('Não é possível gerar os boletos\n' + 'Certifique-se que a fatura esteja confirmada e o ' + 'forma de pagamento seja duplicatas')) boletos = [b.boleto_cnab_api_data for b in boleto_list] From adb81475daa8dbf2b116c5879757c88cab33ff88 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 8 Jul 2020 11:17:14 -0300 Subject: [PATCH 019/308] [REF] Boleto file name. --- l10n_br_account_payment_brcobranca/models/account_invoice.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_invoice.py b/l10n_br_account_payment_brcobranca/models/account_invoice.py index 7b4555221452..3259728ea791 100644 --- a/l10n_br_account_payment_brcobranca/models/account_invoice.py +++ b/l10n_br_account_payment_brcobranca/models/account_invoice.py @@ -65,7 +65,8 @@ def gera_boleto_pdf(self): else: raise UserError(res.text.encode('utf-8')) - file_name = 'boleto-' + self.number + '.pdf' + inv_number = self.get_invoice_fiscal_number().split("/")[-1].zfill(8) + file_name = 'boleto_nf-' + inv_number + '.pdf' self.file_pdf_id = self.env['ir.attachment'].create( { @@ -82,7 +83,7 @@ def gera_boleto_pdf(self): def _target_new_tab(self, attachment_id): if attachment_id: return { - 'type' : 'ir.actions.act_url', + 'type': 'ir.actions.act_url', 'url': '/web/content/{id}/{nome}'.format( id=attachment_id.id, nome=attachment_id.name), From 628b575829d680da79712e1fcc723034d27577fe Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 8 Jul 2020 11:18:47 -0300 Subject: [PATCH 020/308] =?UTF-8?q?[REF]=20Instru=C3=A7=C3=B5es=20Mora,Mul?= =?UTF-8?q?ta=20e=20Desconto=20dos=20Boletos.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/account_move_line.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index ceff66aae277..32583e9e2377 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -117,8 +117,7 @@ def send_payment(self): } # Instrução de Juros - if move_line.payment_mode_id.boleto_perc_mora: - instrucao_juros = "" + if move_line.payment_mode_id.boleto_perc_mora > 0.0: instrucao_juros_tmp = "APÓS VENCIMENTO COBRAR PERCENTUAL" if move_line.payment_mode_id.instrucao_boleto_perc_mora: instrucao_juros_tmp = \ @@ -140,8 +139,7 @@ def send_payment(self): }) # Instrução Multa - if move_line.payment_mode_id.boleto_perc_mora: - instrucao_multa = '' + if move_line.payment_mode_id.boleto_perc_multa > 0.0: instrucao_multa_tmp = "APÓS VENCIMENTO COBRAR MULTA" if move_line.payment_mode_id.instrucao_boleto_perc_multa: instrucao_multa_tmp = \ @@ -161,7 +159,7 @@ def send_payment(self): }) # Instrução Desconto - if move_line.payment_term_id.discount_perc: + if move_line.payment_term_id.discount_perc > 0.0: instrucao_desconto_vencimento = '' instrucao_desconto_vencimento_tmp = \ 'CONCEDER ABATIMENTO PERCENTUAL DE' From b7e30400e007520eb9d855a68c02598c0f6ed107 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 8 Jul 2020 11:20:09 -0300 Subject: [PATCH 021/308] [FIX] Field Numero do Documento. --- .../models/account_payment_order.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index de0a7809cb86..ecc7de9a201d 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -93,7 +93,7 @@ def generate_payment_file(self): 'documento_sacado': misc.punctuation_rm(line.partner_id.cnpj_cpf), 'nome_sacado': line.partner_id.legal_name.strip()[:40], - 'numero': str(line.move_line_id.name)[:10], + 'numero': str(line.numero_documento)[:10], 'endereco_sacado': str( line.partner_id.street + ', ' + str( line.partner_id.street_number))[:40], @@ -156,6 +156,8 @@ def generate_payment_file(self): # 0 = Isento # 1 = Valor Fixo linhas_pagamentos['cod_desconto'] = '0' + # 00000005/01 + linhas_pagamentos['numero'] = str(line.numero_documento)[1:11] if line.move_line_id.payment_mode_id.boleto_perc_multa: if bank_name_brcobranca[0] in ('bradesco', 'unicred'): @@ -263,6 +265,9 @@ def generate_payment_file(self): # remessa = cnab.remessa(order) + # TODO - Devido a configuração do TimeZone os arquivos + # que estão sendo criados dependendo do horário estão + # sendo salvos com a data do dia seguinte if self.payment_mode_id.payment_method_id.code == '240': file_name = 'CB%s%s.REM' % ( time.strftime('%d%m'), str(self.file_number)) From c4763b78d0c6cb580f0fb29a5a250016e26482e5 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 8 Jul 2020 11:50:19 -0300 Subject: [PATCH 022/308] [REF] Retorno CNAB400 Unicred bank. --- .../models/l10n_br_cnab.py | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py index fc6d066c0484..9b5f61ed6589 100644 --- a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py +++ b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py @@ -228,7 +228,7 @@ def processar_arquivo_retorno(self): @api.multi def processar_arquivo_retorno_cnab400(self, data): - lote_id = self.env['l10n.br.cnab.lote'].create({'cnab_id': self.id}) + lote_id = self.env['l10n_br.cnab.lote'].create({'cnab_id': self.id}) quantidade_registros = 0 total_valores = 0 @@ -246,11 +246,17 @@ def processar_arquivo_retorno_cnab400(self, data): # 4 e 5 - Registro de Detalhe (Opcional) continue - quantidade_registros += 1 - account_move_line = self.env['account.move.line'].search( - [('boleto_own_number', '=', dict_line['nosso_numero'][:11])] - ) - payment_line = self.env['payment.line'].search( + # Nosso Numero sem o Digito Verificador + if self.bank == 'bradesco': + account_move_line = self.env['account.move.line'].search( + [('nosso_numero', '=', dict_line['nosso_numero'][:11])] + ) + elif self.bank == 'unicred': + account_move_line = self.env['account.move.line'].search( + [('nosso_numero', '=', dict_line['nosso_numero'][6:16])] + ) + + payment_line = self.env['account.payment.line'].search( [('move_line_id', '=', account_move_line.id)] ) @@ -265,21 +271,22 @@ def processar_arquivo_retorno_cnab400(self, data): dict_line['valor_recebido'][11:])) total_valores += valor_titulo - if (dict_line['data_ocorrencia'] == '000000' or - not dict_line['data_ocorrencia']): - data_ocorrencia = dict_line['data_de_ocorrencia'] - else: - data_ocorrencia = datetime.datetime.strptime( - str(dict_line['data_ocorrencia']), "%d%m%y").date() - # Cada Banco pode possuir um Codigo de Ocorrencia distinto if self.bank == 'bradesco': + if (dict_line['data_ocorrencia'] == '000000' or + not dict_line['data_ocorrencia']): + data_ocorrencia = dict_line['data_de_ocorrencia'] + else: + data_ocorrencia = datetime.datetime.strptime( + str(dict_line['data_ocorrencia']), "%d%m%y").date() descricao_ocorrencia = DICT_OCORRENCIAS_BRADESCO[ dict_line['codigo_ocorrencia']].encode('utf-8') if self.bank == 'itau': + data_ocorrencia = datetime.date.today() descricao_ocorrencia = DICT_OCORRENCIAS_ITAU[ dict_line['codigo_ocorrencia']].encode('utf-8') if self.bank == 'unicred': + data_ocorrencia = datetime.date.today() descricao_ocorrencia = DICT_OCORRENCIAS_UNICRED[ dict_line['codigo_ocorrencia']].encode('utf-8') @@ -305,9 +312,10 @@ def processar_arquivo_retorno_cnab400(self, data): str(dict_line['data_credito']), "%d%m%y").date() if (str(dict_line['codigo_ocorrencia']) in ('06', '17') - and self.bank == 'bradesco') or ( + and self.bank in ('bradesco', 'unicred')) or ( str(dict_line['codigo_ocorrencia']) in ('06', '10') and self.bank == 'itau'): + vals_evento = { 'lote_id': lote_id.id, 'data_ocorrencia': data_ocorrencia, @@ -316,10 +324,11 @@ def processar_arquivo_retorno_cnab400(self, data): # 'favorecido_nome': # obj_account_move_line.company_id.partner_id.name, 'favorecido_conta_bancaria': - account_move_line.payment_mode_id.bank_id.id, + account_move_line.payment_mode_id. + fixed_journal_id.bank_account_id, 'nosso_numero': dict_line['nosso_numero'], 'seu_numero': dict_line['documento_numero'] or - account_move_line.name, + account_move_line.numero_documento, # 'tipo_moeda': evento.credito_moeda_tipo, 'valor_titulo': valor_titulo, 'valor_pagamento': valor_recebido, @@ -332,7 +341,7 @@ def processar_arquivo_retorno_cnab400(self, data): # para criar o Extrato Bancario balance_end_real += valor_recebido line_statement_vals.append({ - 'name': account_move_line.name or '?', + 'name': account_move_line.numero_documento or '?', 'amount': valor_recebido, 'partner_id': account_move_line.partner_id.id, 'ref': account_move_line.ref, @@ -347,7 +356,7 @@ def processar_arquivo_retorno_cnab400(self, data): 'ocorrencias': descricao_ocorrencia, 'data_ocorrencia': data_ocorrencia, 'nosso_numero': dict_line['nosso_numero'], - 'seu_numero': account_move_line.name, + 'seu_numero': account_move_line.numero_documento, 'valor_titulo': valor_titulo, } @@ -363,6 +372,8 @@ def processar_arquivo_retorno_cnab400(self, data): # a mais ou a menos pelo operador if line_statement_vals: vals_bank_statement = { + # TODO - name of Bank Statement + # 'name': 'BNK/%s/0001' % time.strftime('%Y'), 'journal_id': self.account_journal.id, 'balance_end_real': balance_end_real, } From dbe749b9e6ad177d95dbd2eaf6123530dee3e1fd Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 8 Jul 2020 11:51:42 -0300 Subject: [PATCH 023/308] [IMP] Demo data, Retorno CNAB400 Unicred. --- l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET diff --git a/l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET b/l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET new file mode 100644 index 000000000000..edab763e9240 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET @@ -0,0 +1,4 @@ +02RETORNO01COBRANCA 42056000156620925442CENTRAL DAS COOPERATIVAS DE CR001BANCO DO BRASIL0708131314759 000001 +10211034414000158123430000037190000007223405000000000000005010 01 01900000000000000000001806060720 060720000000007000013612343 0607200000000 00000000000000000000000000000000007000000000000000000000005/01 00000000000000000000001 000002 +10211034414000158123430000037190000007223405000000000000005029 01 01900000000000000000001806060720 060720000000003000013612343 0607200000000 00000000000000000000000000000000003000000000000000000000005/02 00000000000000000000001 000003 +9201001 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000016 From 9317a601ec173bdff1f37294489fd28c555b93b4 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 9 Jul 2020 18:10:06 -0300 Subject: [PATCH 024/308] =?UTF-8?q?[FIX]=20Instru=C3=A7=C3=B5es=20Boleto.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/account_move_line.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index 32583e9e2377..12539271a217 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -160,12 +160,11 @@ def send_payment(self): # Instrução Desconto if move_line.payment_term_id.discount_perc > 0.0: - instrucao_desconto_vencimento = '' instrucao_desconto_vencimento_tmp = \ 'CONCEDER ABATIMENTO PERCENTUAL DE' if move_line.payment_term_id.instrucao_discount_perc: instrucao_desconto_vencimento_tmp = \ - move_line.payment_term_id.instrucao_discount_per + move_line.payment_term_id.instrucao_discount_perc valor_desconto = round( move_line.debit * ( move_line.payment_term_id.discount_perc / 100), @@ -175,13 +174,11 @@ def send_payment(self): 'ATÉ O VENCIMENTO EM %s ( R$ %s )' % (('%.2f' % move_line.payment_term_id.discount_perc ).replace('.', ','), - datetime.strptime( - move_line.date_maturity, - '%Y-%m-%d').strftime('%d/%m/%Y'), + move_line.date_maturity.strftime('%d/%m/%Y'), ('%.2f' % valor_desconto).replace('.', ',') )) boleto_cnab_api_data.update({ - 'instrucao4': instrucao_desconto_vencimento, + 'instrucao5': instrucao_desconto_vencimento, }) if bank_account.bank_id.code_bc in ('021', '004'): From 7d1c7a6fc1ab4a65fd0e9e5233101e4ffc6efb0e Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 9 Jul 2020 19:36:52 -0300 Subject: [PATCH 025/308] [REF] Value Mora in REMESSA. --- .../models/account_payment_order.py | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index ecc7de9a201d..ce129b500d13 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -105,6 +105,9 @@ def generate_payment_file(self): 'uf_sacado': line.partner_id.state_id.code, 'identificacao_ocorrencia': self.codigo_instrucao_movimento } + + payment_mode = line.move_line_id.payment_mode_id + if bank_name_brcobranca[0] == 'unicred': # TODO - Verificar se é uma tabela unica por banco ou há padrão # Identificação da Ocorrência: @@ -122,9 +125,9 @@ def generate_payment_file(self): # 40 - Alteração de Carteira linhas_pagamentos['identificacao_ocorrencia'] = '01' linhas_pagamentos['codigo_protesto'] = \ - line.move_line_id.payment_mode_id.boleto_cod_protesto or '3' + payment_mode.boleto_cod_protesto or '3' linhas_pagamentos['dias_protesto'] = \ - line.move_line_id.payment_mode_id.boleto_dias_protesto or '0' + payment_mode.boleto_dias_protesto or '0' # Código adotado pela FEBRABAN para identificação # do tipo de pagamento de multa. @@ -135,19 +138,7 @@ def generate_payment_file(self): # Isento de Multa caso não exista percentual linhas_pagamentos['codigo_multa'] = '3' - # Código adotado pela FEBRABAN para identificação do tipo de - # pagamento de mora de juros. - # Domínio: - # ‘1’ = Valor Diário (R$) - # ‘2’ = Taxa Mensal (%) - # ‘3’= Valor Mensal (R$) * - # ‘4’ = Taxa diária (%) - # ‘5’ = Isento - # *OBSERVAÇÃO: - # ‘3’ - Valor Mensal (R$): a CIP não acata valor mensal, - # segundo manual. Cógido mantido - # para Correspondentes que ainda utilizam. - # Isento de Mora caso não exista percentual + # Isento de Mora linhas_pagamentos['tipo_mora'] = '5' # TODO @@ -159,25 +150,41 @@ def generate_payment_file(self): # 00000005/01 linhas_pagamentos['numero'] = str(line.numero_documento)[1:11] - if line.move_line_id.payment_mode_id.boleto_perc_multa: + if payment_mode.boleto_perc_multa: if bank_name_brcobranca[0] in ('bradesco', 'unicred'): linhas_pagamentos['codigo_multa'] = \ - line.move_line_id.payment_mode_id.boleto_cod_multa + payment_mode.boleto_cod_multa linhas_pagamentos['percentual_multa'] = \ - line.move_line_id.payment_mode_id.boleto_perc_multa + payment_mode.boleto_perc_multa precision = self.env['decimal.precision'] precision_account = precision.precision_get('Account') - if line.move_line_id.payment_mode_id.boleto_perc_mora: - linhas_pagamentos['valor_mora'] = round( - line.move_line_id.debit * - ((line.move_line_id.payment_mode_id.boleto_perc_mora / 100) - / 30), precision_account) - if bank_name_brcobranca[0] == 'unicred': - linhas_pagamentos['tipo_mora'] =\ - line.move_line_id.payment_mode_id.boleto_cod_mora + if payment_mode.boleto_perc_mora: + linhas_pagamentos['tipo_mora'] = \ + payment_mode.boleto_cod_mora + # TODO - É padrão em todos os bancos ? + # Código adotado pela FEBRABAN para identificação do tipo de + # pagamento de mora de juros. + # Domínio: + # ‘1’ = Valor Diário (R$) + # ‘2’ = Taxa Mensal (%) + # ‘3’= Valor Mensal (R$) * + # ‘4’ = Taxa diária (%) + # ‘5’ = Isento + # *OBSERVAÇÃO: + # ‘3’ - Valor Mensal (R$): a CIP não acata valor mensal, + # segundo manual. Cógido mantido + # para Correspondentes que ainda utilizam. + # Isento de Mora caso não exista percentual + if payment_mode.boleto_cod_mora == '1': + linhas_pagamentos['valor_mora'] = round( + line.move_line_id.debit * + ((payment_mode.boleto_perc_mora / 100) + / 30), precision_account) + if payment_mode.boleto_cod_mora == '2': + linhas_pagamentos['valor_mora'] = \ + payment_mode.boleto_perc_mora - # TODO - Definir como sera implementado a configuração do Desconto if line.move_line_id.payment_term_id.discount_perc: linhas_pagamentos['data_desconto'] =\ line.move_line_id.date_maturity.strftime('%Y/%m/%d') @@ -186,7 +193,7 @@ def generate_payment_file(self): line.move_line_id.payment_term_id.discount_perc / 100), precision_account) if bank_name_brcobranca[0] == 'unicred': - linhas_pagamentos['cod_desconto'] = 1 + linhas_pagamentos['cod_desconto'] = '1' pagamentos.append(linhas_pagamentos) From c6d7a8a83ab04e4b6cd0e6eb0a9881e9deaf6fa5 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Tue, 14 Jul 2020 17:44:23 -0300 Subject: [PATCH 026/308] [FIX] Group CNAB access to Bank Statement. --- l10n_br_account_payment_brcobranca/__manifest__.py | 1 + .../security/ir.model.access.csv | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 l10n_br_account_payment_brcobranca/security/ir.model.access.csv diff --git a/l10n_br_account_payment_brcobranca/__manifest__.py b/l10n_br_account_payment_brcobranca/__manifest__.py index 9ecd3f228bf8..86cb4de07f38 100644 --- a/l10n_br_account_payment_brcobranca/__manifest__.py +++ b/l10n_br_account_payment_brcobranca/__manifest__.py @@ -20,6 +20,7 @@ 'views/l10n_br_cnab_retorno_view.xml', 'views/res_config_settings_view.xml', 'data/res_config_settings_data.xml', + 'security/ir.model.access.csv', ], 'demo': [ ], diff --git a/l10n_br_account_payment_brcobranca/security/ir.model.access.csv b/l10n_br_account_payment_brcobranca/security/ir.model.access.csv new file mode 100644 index 000000000000..644ffb4206d3 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/security/ir.model.access.csv @@ -0,0 +1,4 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +access_account_bank_statement,account.bank.statement,account.model_account_bank_statement,l10n_br_account_payment_cobranca.group_cnab_cobranca,1,1,1,1 +access_account_bank_statement_line,account.bank.statement.line,account.model_account_bank_statement_line,l10n_br_account_payment_cobranca.group_cnab_cobranca,1,1,1,1 +access_account_reconcile,access_account_reconcile,account.model_account_reconcile_model,l10n_br_account_payment_cobranca.group_cnab_cobranca,1,1,1,1 \ No newline at end of file From bafffcddea75ba1ab52ee7ffe7d391e6ff669349 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Tue, 14 Jul 2020 17:47:42 -0300 Subject: [PATCH 027/308] [FIX] Included Bank Statement in Remessa/Retorno menu. --- l10n_br_account_payment_brcobranca/__manifest__.py | 1 + .../views/account_bank_statement_view.xml | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 l10n_br_account_payment_brcobranca/views/account_bank_statement_view.xml diff --git a/l10n_br_account_payment_brcobranca/__manifest__.py b/l10n_br_account_payment_brcobranca/__manifest__.py index 86cb4de07f38..9f49307fe3b6 100644 --- a/l10n_br_account_payment_brcobranca/__manifest__.py +++ b/l10n_br_account_payment_brcobranca/__manifest__.py @@ -19,6 +19,7 @@ 'views/account_invoice_view.xml', 'views/l10n_br_cnab_retorno_view.xml', 'views/res_config_settings_view.xml', + 'views/account_bank_statement_view.xml', 'data/res_config_settings_data.xml', 'security/ir.model.access.csv', ], diff --git a/l10n_br_account_payment_brcobranca/views/account_bank_statement_view.xml b/l10n_br_account_payment_brcobranca/views/account_bank_statement_view.xml new file mode 100644 index 000000000000..18663176b136 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/views/account_bank_statement_view.xml @@ -0,0 +1,13 @@ + + + + + + + + From 7c5aea08a674b8c2de725eac43e35d24f8de4a86 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Tue, 14 Jul 2020 17:50:35 -0300 Subject: [PATCH 028/308] [WIP] Automatic bank statement reconcialiation by Remessa file. --- .../models/l10n_br_cnab.py | 149 ++++++++++++++++-- 1 file changed, 133 insertions(+), 16 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py index 9b5f61ed6589..2a011f16b133 100644 --- a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py +++ b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py @@ -234,6 +234,7 @@ def processar_arquivo_retorno_cnab400(self, data): total_valores = 0 balance_end_real = 0.0 line_statement_vals = [] + reconcile_statement_vals = [] for dict_line in data: if int(dict_line['codigo_registro']) != 1: @@ -264,13 +265,9 @@ def processar_arquivo_retorno_cnab400(self, data): str(dict_line['valor_titulo'][0:11] + '.' + dict_line['valor_titulo'][11:])) - valor_recebido = 0.0 - if dict_line['valor_recebido']: - valor_recebido = float( - str(dict_line['valor_recebido'][0:11] + '.' + - dict_line['valor_recebido'][11:])) total_valores += valor_titulo + data_ocorrencia = datetime.date.today() # Cada Banco pode possuir um Codigo de Ocorrencia distinto if self.bank == 'bradesco': if (dict_line['data_ocorrencia'] == '000000' or @@ -281,15 +278,16 @@ def processar_arquivo_retorno_cnab400(self, data): str(dict_line['data_ocorrencia']), "%d%m%y").date() descricao_ocorrencia = DICT_OCORRENCIAS_BRADESCO[ dict_line['codigo_ocorrencia']].encode('utf-8') + if self.bank == 'itau': - data_ocorrencia = datetime.date.today() descricao_ocorrencia = DICT_OCORRENCIAS_ITAU[ dict_line['codigo_ocorrencia']].encode('utf-8') + if self.bank == 'unicred': - data_ocorrencia = datetime.date.today() descricao_ocorrencia = DICT_OCORRENCIAS_UNICRED[ dict_line['codigo_ocorrencia']].encode('utf-8') + # Linha não encontrada if not account_move_line: vals_evento = { 'lote_id': lote_id.id, @@ -304,6 +302,12 @@ def processar_arquivo_retorno_cnab400(self, data): self.env['l10n_br.cnab.evento'].create(vals_evento) continue + valor_recebido = 0.0 + if dict_line['valor_recebido']: + valor_recebido = float( + str(dict_line['valor_recebido'][0:11] + '.' + + dict_line['valor_recebido'][11:])) + if (dict_line['data_credito'] == '000000' or not dict_line['data_credito']): data_credito = dict_line['data_credito'] @@ -312,9 +316,17 @@ def processar_arquivo_retorno_cnab400(self, data): str(dict_line['data_credito']), "%d%m%y").date() if (str(dict_line['codigo_ocorrencia']) in ('06', '17') - and self.bank in ('bradesco', 'unicred')) or ( + and self.bank == 'bradesco') or ( str(dict_line['codigo_ocorrencia']) in ('06', '10') and - self.bank == 'itau'): + self.bank == 'itau') or ( + str(dict_line['codigo_ocorrencia']) in + ('01', '06', '07', '09') and + self.bank == 'unicred'): + + reconcile_line_vals = { + 'numero_documento': account_move_line.numero_documento} + counterpart_line_vals = [] + new_aml_vals = [] vals_evento = { 'lote_id': lote_id.id, @@ -323,20 +335,100 @@ def processar_arquivo_retorno_cnab400(self, data): # 'segmento': evento.servico_segmento, # 'favorecido_nome': # obj_account_move_line.company_id.partner_id.name, - 'favorecido_conta_bancaria': + 'favorecido_conta_bancaria_id': account_move_line.payment_mode_id. - fixed_journal_id.bank_account_id, + fixed_journal_id.bank_account_id.id, 'nosso_numero': dict_line['nosso_numero'], - 'seu_numero': dict_line['documento_numero'] or + 'identificacao_titulo_empresa': + dict_line['documento_numero'] or account_move_line.numero_documento, # 'tipo_moeda': evento.credito_moeda_tipo, - 'valor_titulo': valor_titulo, + 'valor': valor_titulo, 'valor_pagamento': valor_recebido, 'ocorrencias': descricao_ocorrencia, 'bank_payment_line_id': payment_line.bank_line_id.id or False, + 'invoice_id': account_move_line.invoice_id.id, + 'data_vencimento': datetime.datetime.strptime( + str(dict_line['data_vencimento']), "%d%m%y").date(), } + # Valor Desconto + if dict_line.get('desconto'): + valor_desconto = float( + str(dict_line['desconto'][0:11] + '.' + + dict_line['desconto'][11:])) + vals_evento['valor_desconto'] = valor_desconto + + # Atualiza o Valor Recebido + valor_recebido -= valor_desconto + + new_aml_vals.append({ + 'name': 'Desconto (boleto)', + 'debit': valor_desconto, + 'credit': 0.0, + # TODO - Definir Conta Contabil de Desconto + 'account_id': account_move_line.payment_mode_id. + default_tax_account_id.id, + }) + + # Valor Juros Mora - valor de mora e multa pagos pelo sacado + if dict_line.get('juros_mora'): + valor_juros_mora = float( + str(dict_line['juros_mora'][0:11] + '.' + + dict_line['juros_mora'][11:])) + + vals_evento['juros_mora_multa'] = valor_juros_mora + + # Atualiza o Valor Recebido + valor_recebido += valor_juros_mora + + new_aml_vals.append({ + 'name': 'Valor Juros Mora (boleto)', + 'debit': 0.0, + 'credit': valor_juros_mora, + # TODO - Definir Conta Contábil de Juros Mora e Multa + 'account_id': account_move_line.payment_mode_id. + fixed_journal_id.default_credit_account_id.id, + }) + + # Valor Tarifa + if dict_line.get('valor_tarifa'): + valor_tarifa = float( + str(dict_line['valor_tarifa'][0:4] + '.' + + dict_line['valor_tarifa'][4:])) + vals_evento['tarifa_cobranca'] = valor_tarifa + + # Atualiza o Valor Recebido + valor_recebido -= valor_tarifa + + new_aml_vals.append({ + 'name': 'Tarifas bancárias (boleto)', + 'debit': valor_tarifa, + 'credit': 0.0, + 'account_id': account_move_line.payment_mode_id. + default_tax_account_id.id, + }) + + # Valor Abatimento + if dict_line.get('valor_abatimento'): + valor_abatimento = float( + str(dict_line['valor_abatimento'][0:11] + '.' + + dict_line['valor_abatimento'][11:])) + vals_evento['valor_abatimento'] = valor_abatimento + + # Atualiza o valor recebido + valor_recebido -= valor_abatimento + + new_aml_vals.append({ + 'name': 'Abatimento (boleto)', + 'debit': valor_abatimento, + 'credit': 0.0, + # TODO - Definir Conta Contabil de Abatimento + 'account_id': account_move_line.payment_mode_id. + default_tax_account_id.id, + }) + # Monta o dicionario que sera usado # para criar o Extrato Bancario balance_end_real += valor_recebido @@ -350,6 +442,20 @@ def processar_arquivo_retorno_cnab400(self, data): 'currency_id': account_move_line.currency_id.id, }) + # Linha da Fatura a ser reconciliada + counterpart_line_vals.append({ + 'name': account_move_line.numero_documento, + 'debit': 0.0, + 'credit': valor_titulo, + 'move_line': account_move_line, + }) + + # Monta uma Lista com Dicionarios usados para + # reconciliar o extrato bancario + reconcile_line_vals['new_aml_vals'] = new_aml_vals + reconcile_line_vals['counterpart_aml_vals'] = counterpart_line_vals + reconcile_statement_vals.append(reconcile_line_vals) + else: vals_evento = { 'lote_id': lote_id.id, @@ -367,9 +473,9 @@ def processar_arquivo_retorno_cnab400(self, data): self.num_lotes = 1 self.num_eventos = quantidade_registros - # Criacao de um Extrato Bancario para ser conciliado - # pelo usuario permitindo assim o tratamento de valores - # a mais ou a menos pelo operador + # Criacao de um Extrato Bancario a ser conciliado logo abaixo, sendo + # necessário o usuario apenas clicar em Validar na tela do Extrato. + # TODO - automatizar validando o extrato ou deixar de usar o extrato ? if line_statement_vals: vals_bank_statement = { # TODO - name of Bank Statement @@ -384,6 +490,17 @@ def processar_arquivo_retorno_cnab400(self, data): line['statement_id'] = statement.id statement_line_obj.create(line) + # Reconciliação do Extrato e Fatura + for line in statement.line_ids: + for reconcile_line in reconcile_statement_vals: + if line.name == reconcile_line.get('numero_documento'): + line.process_reconciliation( + counterpart_aml_dicts=reconcile_line.get( + 'counterpart_aml_vals'), + new_aml_dicts=reconcile_line.get( + 'new_aml_vals') + ) + return self.write({'state': 'done'}) From 9a38e6beca75e3ca209dc61f552e60b503eb2f1e Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Tue, 14 Jul 2020 17:51:45 -0300 Subject: [PATCH 029/308] [IMP] Demo data, Remessa file with Mora, Multa, Desconto and Abatimento fields. --- l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET b/l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET index edab763e9240..88ad7be1460a 100644 --- a/l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET +++ b/l10n_br_account_payment_brcobranca/tests/CNAB400UNICRED.RET @@ -1,4 +1,4 @@ 02RETORNO01COBRANCA 42056000156620925442CENTRAL DAS COOPERATIVAS DE CR001BANCO DO BRASIL0708131314759 000001 -10211034414000158123430000037190000007223405000000000000005010 01 01900000000000000000001806060720 060720000000007000013612343 0607200000000 00000000000000000000000000000000007000000000000000000000005/01 00000000000000000000001 000002 -10211034414000158123430000037190000007223405000000000000005029 01 01900000000000000000001806060720 060720000000003000013612343 0607200000000 00000000000000000000000000000000003000000000000000000000005/02 00000000000000000000001 000003 +10211034414000158123430000037190000007223405000000000000005010 01 01900000000000000000001806060720 060720000000007000013612343 0607200000100 00000000002000000000000300000000007000000000000001500000005/01 00000000000000000000001 000002 +10211034414000158123430000037190000007223405000000000000005029 01 01900000000000000000001806060720 060720000000003000013612343 0607200000100 00000000002000000000000300000000003000000000000001500000005/02 00000000000000000000001 000003 9201001 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000000000000000000000000000000 000016 From 064b45ce3b8433e968989e712097a417aec3670a Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 15 Jul 2020 11:52:47 -0300 Subject: [PATCH 030/308] [REF] Clean code, Retorno CNAB. --- .../models/l10n_br_cnab.py | 182 +++++++++--------- 1 file changed, 94 insertions(+), 88 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py index 2a011f16b133..88473af58075 100644 --- a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py +++ b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py @@ -236,8 +236,8 @@ def processar_arquivo_retorno_cnab400(self, data): line_statement_vals = [] reconcile_statement_vals = [] - for dict_line in data: - if int(dict_line['codigo_registro']) != 1: + for linha_cnab in data: + if int(linha_cnab['codigo_registro']) != 1: # Bradesco # Existe o codigo de registro 9 que eh um totalizador # porem os campos estao colocados em outras posicoes @@ -250,11 +250,11 @@ def processar_arquivo_retorno_cnab400(self, data): # Nosso Numero sem o Digito Verificador if self.bank == 'bradesco': account_move_line = self.env['account.move.line'].search( - [('nosso_numero', '=', dict_line['nosso_numero'][:11])] + [('nosso_numero', '=', linha_cnab['nosso_numero'][:11])] ) elif self.bank == 'unicred': account_move_line = self.env['account.move.line'].search( - [('nosso_numero', '=', dict_line['nosso_numero'][6:16])] + [('nosso_numero', '=', linha_cnab['nosso_numero'][6:16])] ) payment_line = self.env['account.payment.line'].search( @@ -262,30 +262,31 @@ def processar_arquivo_retorno_cnab400(self, data): ) valor_titulo = float( - str(dict_line['valor_titulo'][0:11] + '.' + - dict_line['valor_titulo'][11:])) + str(linha_cnab['valor_titulo'][0:11] + '.' + + linha_cnab['valor_titulo'][11:])) total_valores += valor_titulo data_ocorrencia = datetime.date.today() + cod_ocorrencia = str(linha_cnab['codigo_ocorrencia']) # Cada Banco pode possuir um Codigo de Ocorrencia distinto if self.bank == 'bradesco': - if (dict_line['data_ocorrencia'] == '000000' or - not dict_line['data_ocorrencia']): - data_ocorrencia = dict_line['data_de_ocorrencia'] + if (linha_cnab['data_ocorrencia'] == '000000' or + not linha_cnab['data_ocorrencia']): + data_ocorrencia = linha_cnab['data_de_ocorrencia'] else: data_ocorrencia = datetime.datetime.strptime( - str(dict_line['data_ocorrencia']), "%d%m%y").date() + str(linha_cnab['data_ocorrencia']), "%d%m%y").date() descricao_ocorrencia = DICT_OCORRENCIAS_BRADESCO[ - dict_line['codigo_ocorrencia']].encode('utf-8') + cod_ocorrencia].encode('utf-8') if self.bank == 'itau': descricao_ocorrencia = DICT_OCORRENCIAS_ITAU[ - dict_line['codigo_ocorrencia']].encode('utf-8') + cod_ocorrencia].encode('utf-8') if self.bank == 'unicred': descricao_ocorrencia = DICT_OCORRENCIAS_UNICRED[ - dict_line['codigo_ocorrencia']].encode('utf-8') + cod_ocorrencia].encode('utf-8') # Linha não encontrada if not account_move_line: @@ -295,73 +296,44 @@ def processar_arquivo_retorno_cnab400(self, data): 'data_ocorrencia': data_ocorrencia, 'str_motiv_a': u' * - BOLETO NÃO ENCONTRADO DENTRO DO PROGRAMA', - 'nosso_numero': dict_line['nosso_numero'], - 'seu_numero': dict_line['documento_numero'], + 'nosso_numero': linha_cnab['nosso_numero'], + 'seu_numero': linha_cnab['documento_numero'], 'valor_titulo': valor_titulo, } self.env['l10n_br.cnab.evento'].create(vals_evento) continue valor_recebido = 0.0 - if dict_line['valor_recebido']: - valor_recebido = float( - str(dict_line['valor_recebido'][0:11] + '.' + - dict_line['valor_recebido'][11:])) - - if (dict_line['data_credito'] == '000000' or - not dict_line['data_credito']): - data_credito = dict_line['data_credito'] + if linha_cnab['valor_recebido']: + valor_recebido = self.cnab_str_to_float( + linha_cnab['valor_recebido']) + + if (linha_cnab['data_credito'] == '000000' or + not linha_cnab['data_credito']): + data_credito = linha_cnab['data_credito'] else: data_credito = datetime.datetime.strptime( - str(dict_line['data_credito']), "%d%m%y").date() + str(linha_cnab['data_credito']), "%d%m%y").date() - if (str(dict_line['codigo_ocorrencia']) in ('06', '17') - and self.bank == 'bradesco') or ( - str(dict_line['codigo_ocorrencia']) in ('06', '10') and - self.bank == 'itau') or ( - str(dict_line['codigo_ocorrencia']) in - ('01', '06', '07', '09') and - self.bank == 'unicred'): + # Codigos de Ocorrencia - Liquidação + if ((cod_ocorrencia in ('06', '17') and self.bank == 'bradesco') or + (cod_ocorrencia in ('06', '10') and self.bank == 'itau') or + (cod_ocorrencia in ('01', '06', '07', '09') and + self.bank == 'unicred')): reconcile_line_vals = { 'numero_documento': account_move_line.numero_documento} counterpart_line_vals = [] new_aml_vals = [] - vals_evento = { - 'lote_id': lote_id.id, - 'data_ocorrencia': data_ocorrencia, - 'data_real_pagamento': data_credito.strftime("%Y-%m-%d"), - # 'segmento': evento.servico_segmento, - # 'favorecido_nome': - # obj_account_move_line.company_id.partner_id.name, - 'favorecido_conta_bancaria_id': - account_move_line.payment_mode_id. - fixed_journal_id.bank_account_id.id, - 'nosso_numero': dict_line['nosso_numero'], - 'identificacao_titulo_empresa': - dict_line['documento_numero'] or - account_move_line.numero_documento, - # 'tipo_moeda': evento.credito_moeda_tipo, - 'valor': valor_titulo, - 'valor_pagamento': valor_recebido, - 'ocorrencias': descricao_ocorrencia, - 'bank_payment_line_id': - payment_line.bank_line_id.id or False, - 'invoice_id': account_move_line.invoice_id.id, - 'data_vencimento': datetime.datetime.strptime( - str(dict_line['data_vencimento']), "%d%m%y").date(), - } - # Valor Desconto - if dict_line.get('desconto'): - valor_desconto = float( - str(dict_line['desconto'][0:11] + '.' + - dict_line['desconto'][11:])) - vals_evento['valor_desconto'] = valor_desconto + if linha_cnab.get('desconto'): + valor_desconto = self.cnab_str_to_float( + linha_cnab['desconto']) - # Atualiza o Valor Recebido - valor_recebido -= valor_desconto + # TODO - Atualizar o Valor Recebido ou isso + # ja vem informado ? Confirmar no arquivo de retorno + # valor_recebido -= valor_desconto new_aml_vals.append({ 'name': 'Desconto (boleto)', @@ -369,20 +341,20 @@ def processar_arquivo_retorno_cnab400(self, data): 'credit': 0.0, # TODO - Definir Conta Contabil de Desconto 'account_id': account_move_line.payment_mode_id. - default_tax_account_id.id, + default_tax_account_id.id, }) # Valor Juros Mora - valor de mora e multa pagos pelo sacado - if dict_line.get('juros_mora'): - valor_juros_mora = float( - str(dict_line['juros_mora'][0:11] + '.' + - dict_line['juros_mora'][11:])) - - vals_evento['juros_mora_multa'] = valor_juros_mora + if linha_cnab.get('juros_mora'): + valor_juros_mora = self.cnab_str_to_float( + linha_cnab['juros_mora']) - # Atualiza o Valor Recebido - valor_recebido += valor_juros_mora + # TODO - Atualizar o Valor Recebido ou isso + # ja vem informado ? Confirmar no arquivo de retorno + # valor_recebido += valor_juros_mora + # TODO - Deveria ser tbm um counterpart ? E ser criado + # um outro lançamento para ser conciliado com esse new_aml_vals.append({ 'name': 'Valor Juros Mora (boleto)', 'debit': 0.0, @@ -393,32 +365,31 @@ def processar_arquivo_retorno_cnab400(self, data): }) # Valor Tarifa - if dict_line.get('valor_tarifa'): + if linha_cnab.get('valor_tarifa'): valor_tarifa = float( - str(dict_line['valor_tarifa'][0:4] + '.' + - dict_line['valor_tarifa'][4:])) - vals_evento['tarifa_cobranca'] = valor_tarifa + str(linha_cnab['valor_tarifa'][0:4] + '.' + + linha_cnab['valor_tarifa'][4:])) - # Atualiza o Valor Recebido - valor_recebido -= valor_tarifa + # TODO - Atualizar o Valor Recebido ou isso + # ja vem informado ? Confirmar no arquivo de retorno + # valor_recebido -= valor_tarifa new_aml_vals.append({ 'name': 'Tarifas bancárias (boleto)', 'debit': valor_tarifa, 'credit': 0.0, 'account_id': account_move_line.payment_mode_id. - default_tax_account_id.id, + default_tax_account_id.id, }) # Valor Abatimento - if dict_line.get('valor_abatimento'): - valor_abatimento = float( - str(dict_line['valor_abatimento'][0:11] + '.' + - dict_line['valor_abatimento'][11:])) - vals_evento['valor_abatimento'] = valor_abatimento + if linha_cnab.get('valor_abatimento'): + valor_abatimento = self.cnab_str_to_float( + linha_cnab['valor_abatimento']) - # Atualiza o valor recebido - valor_recebido -= valor_abatimento + # TODO - Atualizar o Valor Recebido ou isso + # ja vem informado ? Confirmar no arquivo de retorno + # valor_recebido -= valor_abatimento new_aml_vals.append({ 'name': 'Abatimento (boleto)', @@ -429,6 +400,35 @@ def processar_arquivo_retorno_cnab400(self, data): default_tax_account_id.id, }) + vals_evento = { + 'lote_id': lote_id.id, + 'data_ocorrencia': data_ocorrencia, + 'data_real_pagamento': data_credito.strftime("%Y-%m-%d"), + # 'segmento': evento.servico_segmento, + # 'favorecido_nome': + # obj_account_move_line.company_id.partner_id.name, + 'favorecido_conta_bancaria_id': + account_move_line.payment_mode_id. + fixed_journal_id.bank_account_id.id, + 'nosso_numero': linha_cnab['nosso_numero'], + 'identificacao_titulo_empresa': + linha_cnab['documento_numero'] or + account_move_line.numero_documento, + # 'tipo_moeda': evento.credito_moeda_tipo, + 'valor': valor_titulo, + 'valor_pagamento': valor_recebido, + 'ocorrencias': descricao_ocorrencia, + 'bank_payment_line_id': + payment_line.bank_line_id.id or False, + 'invoice_id': account_move_line.invoice_id.id, + 'data_vencimento': datetime.datetime.strptime( + str(linha_cnab['data_vencimento']), "%d%m%y").date(), + 'valor_desconto': valor_desconto, + 'juros_mora_multa': valor_juros_mora, + 'valor_abatimento': valor_abatimento, + 'tarifa_cobranca': valor_tarifa, + } + # Monta o dicionario que sera usado # para criar o Extrato Bancario balance_end_real += valor_recebido @@ -446,7 +446,7 @@ def processar_arquivo_retorno_cnab400(self, data): counterpart_line_vals.append({ 'name': account_move_line.numero_documento, 'debit': 0.0, - 'credit': valor_titulo, + 'credit': valor_recebido, 'move_line': account_move_line, }) @@ -461,7 +461,7 @@ def processar_arquivo_retorno_cnab400(self, data): 'lote_id': lote_id.id, 'ocorrencias': descricao_ocorrencia, 'data_ocorrencia': data_ocorrencia, - 'nosso_numero': dict_line['nosso_numero'], + 'nosso_numero': linha_cnab['nosso_numero'], 'seu_numero': account_move_line.numero_documento, 'valor_titulo': valor_titulo, } @@ -503,6 +503,12 @@ def processar_arquivo_retorno_cnab400(self, data): return self.write({'state': 'done'}) + def cnab_str_to_float(self, value): + if len(value) == 13: + value_float = float( + str(value[0:11] + '.' + value[11:])) + return value_float + class L10nBrHrCnabEvento(models.Model): _inherit = "l10n_br.cnab.evento" From ddf9a7419c755589d5c6a685d9258cf80d918a41 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 16 Jul 2020 11:48:50 -0300 Subject: [PATCH 031/308] [REF] Removed unnecessary code and view. --- .../models/l10n_br_cnab.py | 11 ++--------- .../views/l10n_br_cnab_retorno_view.xml | 14 -------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py index 88473af58075..d14f2f7a6366 100644 --- a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py +++ b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py @@ -298,7 +298,7 @@ def processar_arquivo_retorno_cnab400(self, data): u' * - BOLETO NÃO ENCONTRADO DENTRO DO PROGRAMA', 'nosso_numero': linha_cnab['nosso_numero'], 'seu_numero': linha_cnab['documento_numero'], - 'valor_titulo': valor_titulo, + 'valor': valor_titulo, } self.env['l10n_br.cnab.evento'].create(vals_evento) continue @@ -463,7 +463,7 @@ def processar_arquivo_retorno_cnab400(self, data): 'data_ocorrencia': data_ocorrencia, 'nosso_numero': linha_cnab['nosso_numero'], 'seu_numero': account_move_line.numero_documento, - 'valor_titulo': valor_titulo, + 'valor': valor_titulo, } self.env['l10n_br.cnab.evento'].create(vals_evento) @@ -508,10 +508,3 @@ def cnab_str_to_float(self, value): value_float = float( str(value[0:11] + '.' + value[11:])) return value_float - - -class L10nBrHrCnabEvento(models.Model): - _inherit = "l10n_br.cnab.evento" - - data_ocorrencia = fields.Date(string=u"Data da Ocorrência") - valor_titulo = fields.Float(string=u"Valor do Título") diff --git a/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml b/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml index 1d1a1c970209..63623a3030ce 100644 --- a/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml +++ b/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml @@ -1,20 +1,6 @@ - - brcobranca.cnab.evento.tree - l10n_br.cnab.evento - - - - - - - - - - - brcobranca.cnab.retorno.form.view l10n_br.cnab From a0150be60fcef8c24652c003173e0bb6ad67e130 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 30 Jul 2020 15:53:15 -0300 Subject: [PATCH 032/308] [FIX] Removed unnecessary code for python3. --- l10n_br_account_payment_brcobranca/__init__.py | 1 - l10n_br_account_payment_brcobranca/models/__init__.py | 1 - l10n_br_account_payment_brcobranca/models/account_invoice.py | 1 - l10n_br_account_payment_brcobranca/models/account_move_line.py | 1 - .../models/account_payment_order.py | 1 - l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py | 1 - l10n_br_account_payment_brcobranca/models/res_config_settings.py | 1 - 7 files changed, 7 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/__init__.py b/l10n_br_account_payment_brcobranca/__init__.py index 6b195001e923..eb08a105c2b8 100644 --- a/l10n_br_account_payment_brcobranca/__init__.py +++ b/l10n_br_account_payment_brcobranca/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion # @author Raphaël Valyi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/l10n_br_account_payment_brcobranca/models/__init__.py b/l10n_br_account_payment_brcobranca/models/__init__.py index dcbea94ecb4a..a8a8dddb6918 100644 --- a/l10n_br_account_payment_brcobranca/models/__init__.py +++ b/l10n_br_account_payment_brcobranca/models/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion # @author Raphaël Valyi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/l10n_br_account_payment_brcobranca/models/account_invoice.py b/l10n_br_account_payment_brcobranca/models/account_invoice.py index 3259728ea791..ddbf2d07f221 100644 --- a/l10n_br_account_payment_brcobranca/models/account_invoice.py +++ b/l10n_br_account_payment_brcobranca/models/account_invoice.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2020 Akretion # @author Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index 12539271a217..573297358015 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion # @author Raphaël Valyi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index ce129b500d13..ac3633e57319 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2020 Akretion # @author Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py index d14f2f7a6366..df8842902744 100644 --- a/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py +++ b/l10n_br_account_payment_brcobranca/models/l10n_br_cnab.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion - Renato Lima # Copyright 2017 Akretion - Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/l10n_br_account_payment_brcobranca/models/res_config_settings.py b/l10n_br_account_payment_brcobranca/models/res_config_settings.py index 4b7c4fe08cc0..79710ee36961 100644 --- a/l10n_br_account_payment_brcobranca/models/res_config_settings.py +++ b/l10n_br_account_payment_brcobranca/models/res_config_settings.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2020 Akretion - Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). From ccae5f6fba9978ba643532b0e7e7453a1e7520d1 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Mon, 17 Aug 2020 16:22:26 -0300 Subject: [PATCH 033/308] [FIX] Days and code of protest was missing in Remessa file. --- .../models/account_payment_order.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index ac3633e57319..4127c728943a 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -194,6 +194,14 @@ def generate_payment_file(self): if bank_name_brcobranca[0] == 'unicred': linhas_pagamentos['cod_desconto'] = '1' + # Protesto + if payment_mode.boleto_cod_protesto: + linhas_pagamentos['codigo_protesto'] = \ + payment_mode.boleto_cod_protesto + if payment_mode.boleto_dias_protesto: + linhas_pagamentos['dias_protesto'] = \ + payment_mode.boleto_dias_protesto + pagamentos.append(linhas_pagamentos) remessa_values = { From 7ae725dfdd32dbad78a4dcaecae6499ac9ecb7f9 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 20 Aug 2020 18:09:14 -0300 Subject: [PATCH 034/308] [REF] Update change made in dependency module. --- .../__manifest__.py | 2 +- .../models/account_move_line.py | 57 +++++++---------- .../models/account_payment_order.py | 62 +++++++++---------- .../views/l10n_br_cnab_retorno_view.xml | 4 +- 4 files changed, 54 insertions(+), 71 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/__manifest__.py b/l10n_br_account_payment_brcobranca/__manifest__.py index 9f49307fe3b6..089462f36111 100644 --- a/l10n_br_account_payment_brcobranca/__manifest__.py +++ b/l10n_br_account_payment_brcobranca/__manifest__.py @@ -13,7 +13,7 @@ 'author': 'Akretion', 'website': 'www.akretion.com', 'depends': [ - 'l10n_br_account_payment_cobranca', + 'l10n_br_account_payment_order', ], 'data': [ 'views/account_invoice_view.xml', diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index 573297358015..f54ad1c5de93 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -79,7 +79,7 @@ def send_payment(self): boleto_cnab_api_data = { 'bank': bank_name_brcobranca[0], - 'valor': str("%.2f" % move_line.debit), + 'valor': str('%.2f' % move_line.debit), 'cedente': move_line.company_id.partner_id.legal_name, 'cedente_endereco': move_line.company_id.partner_id.street or '' + ', ' + @@ -93,18 +93,18 @@ def send_payment(self): 'sacado_documento': move_line.partner_id.cnpj_cpf, 'agencia': bank_account.bra_number, 'conta_corrente': bank_account.acc_number, - 'convenio': move_line.payment_mode_id.boleto_convenio, - 'carteira': str(move_line.payment_mode_id.boleto_carteira), + 'convenio': move_line.payment_mode_id.boleto_convetion, + 'carteira': str(move_line.payment_mode_id.boleto_wallet), 'nosso_numero': int(''.join( - i for i in move_line.nosso_numero if i.isdigit())), - 'documento_numero': move_line.numero_documento, + i for i in move_line.own_number if i.isdigit())), + 'documento_numero': move_line.document_number, 'data_vencimento': move_line.date_maturity.strftime('%Y/%m/%d'), 'data_documento': move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), - 'especie': move_line.payment_mode_id.boleto_especie, + 'especie': move_line.payment_mode_id.boleto_species, 'moeda': dict_brcobranca_currency['R$'], - 'aceite': move_line.payment_mode_id.boleto_aceite, + 'aceite': move_line.payment_mode_id.boleto_accept, 'sacado_endereco': move_line.partner_id.street or '' + ', ' + move_line.partner_id.street_number or '' + ' ' + @@ -112,23 +112,18 @@ def send_payment(self): move_line.partner_id.state_id.name or '', 'data_processamento': move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), - 'instrucao1': move_line.payment_mode_id.instrucoes or '', + 'instrucao1': move_line.payment_mode_id.instructions or '', } # Instrução de Juros - if move_line.payment_mode_id.boleto_perc_mora > 0.0: - instrucao_juros_tmp = "APÓS VENCIMENTO COBRAR PERCENTUAL" - if move_line.payment_mode_id.instrucao_boleto_perc_mora: - instrucao_juros_tmp = \ - move_line.payment_mode_id.instrucao_boleto_perc_mora - + if move_line.payment_mode_id.boleto_interest_perc > 0.0: valor_juros = round( move_line.debit * ((move_line.payment_mode_id.boleto_perc_mora / 100) / 30), precision_account) instrucao_juros = ( - instrucao_juros_tmp + - " DE %s %% AO MÊS ( R$ %s AO DIA )" + 'APÓS VENCIMENTO COBRAR PERCENTUAL' + + ' DE %s %% AO MÊS ( R$ %s AO DIA )' % (('%.2f' % move_line.payment_mode_id.boleto_perc_mora ).replace('.', ','), @@ -138,19 +133,14 @@ def send_payment(self): }) # Instrução Multa - if move_line.payment_mode_id.boleto_perc_multa > 0.0: - instrucao_multa_tmp = "APÓS VENCIMENTO COBRAR MULTA" - if move_line.payment_mode_id.instrucao_boleto_perc_multa: - instrucao_multa_tmp = \ - move_line.payment_mode_id.instrucao_boleto_perc_multa - + if move_line.payment_mode_id.boleto_fee_perc > 0.0: valor_multa = round(move_line.debit * ( - (move_line.payment_mode_id.boleto_perc_multa / 100) + (move_line.payment_mode_id.boleto_fee_perc / 100) ), precision_account) instrucao_multa = ( - instrucao_multa_tmp + - " DE %s %% ( R$ %s )" % - (('%.2f' % move_line.payment_mode_id.boleto_perc_multa + 'APÓS VENCIMENTO COBRAR MULTA' + + ' DE %s %% ( R$ %s )' % + (('%.2f' % move_line.payment_mode_id.boleto_fee_perc ).replace('.', ','), ('%.2f' % valor_multa).replace('.', ','))) boleto_cnab_api_data.update({ @@ -158,18 +148,13 @@ def send_payment(self): }) # Instrução Desconto - if move_line.payment_term_id.discount_perc > 0.0: - instrucao_desconto_vencimento_tmp = \ - 'CONCEDER ABATIMENTO PERCENTUAL DE' - if move_line.payment_term_id.instrucao_discount_perc: - instrucao_desconto_vencimento_tmp = \ - move_line.payment_term_id.instrucao_discount_perc + if move_line.payment_mode_id.boleto_discount_perc > 0.0: valor_desconto = round( move_line.debit * ( - move_line.payment_term_id.discount_perc / 100), + move_line.payment_mode_id.boleto_discount_perc / 100), precision_account) instrucao_desconto_vencimento = ( - instrucao_desconto_vencimento_tmp + ' %s %% ' + 'CONCEDER ABATIMENTO PERCENTUAL DE' + ' %s %% ' 'ATÉ O VENCIMENTO EM %s ( R$ %s )' % (('%.2f' % move_line.payment_term_id.discount_perc ).replace('.', ','), @@ -186,8 +171,8 @@ def send_payment(self): move_line.payment_mode_id.bank_id.acc_number_dig, }) - # Fields used in Sicredi/Unicred and Sicoob Banks - if bank_account.bank_id.code_bc == '748': + # Fields used in Sicredi and Sicoob Banks + if bank_account.bank_id.code_bc in ('748', '756'): boleto_cnab_api_data.update({ 'byte_idt': move_line.payment_mode_id.boleto_byte_idt, 'posto': move_line.payment_mode_id.boleto_posto, diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 4127c728943a..00aedaa0eb82 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -88,11 +88,11 @@ def generate_payment_file(self): linhas_pagamentos = { 'valor': line.amount_currency, 'data_vencimento': line.move_line_id.date_maturity.strftime('%Y/%m/%d'), - 'nosso_numero': line.move_line_id.nosso_numero, + 'nosso_numero': line.move_line_id.own_number, 'documento_sacado': misc.punctuation_rm(line.partner_id.cnpj_cpf), 'nome_sacado': line.partner_id.legal_name.strip()[:40], - 'numero': str(line.numero_documento)[:10], + 'numero': str(line.document_number)[:10], 'endereco_sacado': str( line.partner_id.street + ', ' + str( line.partner_id.street_number))[:40], @@ -102,7 +102,7 @@ def generate_payment_file(self): 'cidade_sacado': line.partner_id.city_id.name, 'uf_sacado': line.partner_id.state_id.code, - 'identificacao_ocorrencia': self.codigo_instrucao_movimento + 'identificacao_ocorrencia': self.movement_instruction_code } payment_mode = line.move_line_id.payment_mode_id @@ -124,9 +124,9 @@ def generate_payment_file(self): # 40 - Alteração de Carteira linhas_pagamentos['identificacao_ocorrencia'] = '01' linhas_pagamentos['codigo_protesto'] = \ - payment_mode.boleto_cod_protesto or '3' + payment_mode.boleto_protest_code or '3' linhas_pagamentos['dias_protesto'] = \ - payment_mode.boleto_dias_protesto or '0' + payment_mode.boleto_days_protest or '0' # Código adotado pela FEBRABAN para identificação # do tipo de pagamento de multa. @@ -147,20 +147,19 @@ def generate_payment_file(self): # 1 = Valor Fixo linhas_pagamentos['cod_desconto'] = '0' # 00000005/01 - linhas_pagamentos['numero'] = str(line.numero_documento)[1:11] + linhas_pagamentos['numero'] = str(line.document_number)[1:11] - if payment_mode.boleto_perc_multa: - if bank_name_brcobranca[0] in ('bradesco', 'unicred'): - linhas_pagamentos['codigo_multa'] = \ - payment_mode.boleto_cod_multa - linhas_pagamentos['percentual_multa'] = \ - payment_mode.boleto_perc_multa + if payment_mode.boleto_fee_perc: + linhas_pagamentos['codigo_multa'] = \ + payment_mode.boleto_fee_code + linhas_pagamentos['percentual_multa'] = \ + payment_mode.boleto_fee_perc precision = self.env['decimal.precision'] precision_account = precision.precision_get('Account') - if payment_mode.boleto_perc_mora: + if payment_mode.boleto_interest_perc: linhas_pagamentos['tipo_mora'] = \ - payment_mode.boleto_cod_mora + payment_mode.boleto_interest_perc # TODO - É padrão em todos os bancos ? # Código adotado pela FEBRABAN para identificação do tipo de # pagamento de mora de juros. @@ -175,37 +174,37 @@ def generate_payment_file(self): # segundo manual. Cógido mantido # para Correspondentes que ainda utilizam. # Isento de Mora caso não exista percentual - if payment_mode.boleto_cod_mora == '1': + if payment_mode.boleto_interest_code == '1': linhas_pagamentos['valor_mora'] = round( line.move_line_id.debit * - ((payment_mode.boleto_perc_mora / 100) + ((payment_mode.boleto_interest_code / 100) / 30), precision_account) - if payment_mode.boleto_cod_mora == '2': + if payment_mode.boleto_interest_code == '2': linhas_pagamentos['valor_mora'] = \ - payment_mode.boleto_perc_mora + payment_mode.boleto_interest_code - if line.move_line_id.payment_term_id.discount_perc: + if payment_mode.boleto_discount_perc: linhas_pagamentos['data_desconto'] =\ line.move_line_id.date_maturity.strftime('%Y/%m/%d') linhas_pagamentos['valor_desconto'] = round( line.move_line_id.debit * ( - line.move_line_id.payment_term_id.discount_perc / 100), + payment_mode.boleto_discount_perc / 100), precision_account) if bank_name_brcobranca[0] == 'unicred': linhas_pagamentos['cod_desconto'] = '1' # Protesto - if payment_mode.boleto_cod_protesto: + if payment_mode.boleto_protest_code: linhas_pagamentos['codigo_protesto'] = \ - payment_mode.boleto_cod_protesto - if payment_mode.boleto_dias_protesto: + payment_mode.boleto_protest_code + if payment_mode.boleto_days_protest: linhas_pagamentos['dias_protesto'] = \ - payment_mode.boleto_dias_protesto + payment_mode.boleto_days_protest pagamentos.append(linhas_pagamentos) remessa_values = { - 'carteira': str(self.payment_mode_id.boleto_carteira), + 'carteira': str(self.payment_mode_id.boleto_wallet), 'agencia': int(bank_account.bra_number), # 'digito_agencia': order.mode.bank_id.bra_number_dig, 'conta_corrente': int(misc.punctuation_rm(bank_account.acc_number)), @@ -217,20 +216,19 @@ def generate_payment_file(self): 'sequencial_remessa': self.payment_mode_id.cnab_sequence_id.next_by_id(), } - if (bank_name_brcobranca[0] == 'bradesco' - and self.payment_mode_id.payment_method_id.code == '400'): + if bank_name_brcobranca[0] == 'bradesco': remessa_values[ - 'codigo_empresa'] = int(self.payment_mode_id.codigo_convenio) + 'codigo_empresa'] = int(self.payment_mode_id.code_convetion) - # Field used in Sicredi/Unicred and Sicoob Banks - if bank_account.bank_id.code_bc == '748': + # Field used in Sicredi and Sicoob Banks + if bank_account.bank_id.code_bc in ('748', '756'): remessa_values.update({ - 'codigo_transmissao': int(self.payment_mode_id.codigo_convenio), + 'codigo_transmissao': int(self.payment_mode_id.code_convetion), }) # Field used in Unicred Bank if bank_account.bank_id.code_bc == '136': remessa_values.update({ - 'codigo_beneficiario': int(self.payment_mode_id.codigo_convenio), + 'codigo_beneficiario': int(self.payment_mode_id.code_convetion), }) content = json.dumps(remessa_values) diff --git a/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml b/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml index 63623a3030ce..137261d7b2d2 100644 --- a/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml +++ b/l10n_br_account_payment_brcobranca/views/l10n_br_cnab_retorno_view.xml @@ -4,9 +4,9 @@ brcobranca.cnab.retorno.form.view l10n_br.cnab - + - + From 561294e055102cc456c628b318d7cfd51842f56a Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 20 Aug 2020 18:29:06 -0300 Subject: [PATCH 035/308] [REF][FIX] Update change made in dependency module, security access file. --- .../security/ir.model.access.csv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/security/ir.model.access.csv b/l10n_br_account_payment_brcobranca/security/ir.model.access.csv index 644ffb4206d3..fddc2d0c88ff 100644 --- a/l10n_br_account_payment_brcobranca/security/ir.model.access.csv +++ b/l10n_br_account_payment_brcobranca/security/ir.model.access.csv @@ -1,4 +1,4 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -access_account_bank_statement,account.bank.statement,account.model_account_bank_statement,l10n_br_account_payment_cobranca.group_cnab_cobranca,1,1,1,1 -access_account_bank_statement_line,account.bank.statement.line,account.model_account_bank_statement_line,l10n_br_account_payment_cobranca.group_cnab_cobranca,1,1,1,1 -access_account_reconcile,access_account_reconcile,account.model_account_reconcile_model,l10n_br_account_payment_cobranca.group_cnab_cobranca,1,1,1,1 \ No newline at end of file +access_account_bank_statement,account.bank.statement,account.model_account_bank_statement,l10n_br_account_payment_order.group_cnab_cobranca,1,1,1,1 +access_account_bank_statement_line,account.bank.statement.line,account.model_account_bank_statement_line,l10n_br_account_payment_order.group_cnab_cobranca,1,1,1,1 +access_account_reconcile,access_account_reconcile,account.model_account_reconcile_model,l10n_br_account_payment_order.group_cnab_cobranca,1,1,1,1 \ No newline at end of file From b15de255b34eaaef9ba551aba1e4b9ae19d84e49 Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Thu, 3 Sep 2020 15:01:06 -0300 Subject: [PATCH 036/308] [NEW] After confirm the export to bank set payment order to done. Signed-off-by: Luis Felipe Mileo --- .../models/account_payment_order.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 00aedaa0eb82..2f849644bfe5 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -268,15 +268,6 @@ def generate_payment_file(self): else: raise UserError(res.text) - # self.state = 'done' - # self.cnab_file = base64.b64encode(remessa) - - # Criando instancia do CNAB a partir do código do banco - # cnab = Cnab.get_cnab( - # order.mode.bank_id.bank_bic, order.mode.type.code)() - - # remessa = cnab.remessa(order) - # TODO - Devido a configuração do TimeZone os arquivos # que estão sendo criados dependendo do horário estão # sendo salvos com a data do dia seguinte @@ -289,9 +280,9 @@ def generate_payment_file(self): elif self.payment_mode_id.payment_method_id.code == '500': file_name = 'PG%s%s.REM' % ( time.strftime('%d%m'), str(self.file_number)) - # self.state = 'done' - # self.cnab_file = base64.b64encode(remessa) - # self.cnab_file = base64.b64encode(remessa) - # self.cnab_filename = self.name return remessa, file_name + + def generated2uploaded(self): + super().generated2uploaded() + self.action_done() From b1c485f755c17e536dcd1e39d5756ba997a66ef7 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 9 Sep 2020 11:47:05 -0300 Subject: [PATCH 037/308] [FIX] Date name file, get user time zone to avoid future date. --- .../models/account_payment_order.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 2f849644bfe5..e0dc2d3cbd5f 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -2,8 +2,8 @@ # @author Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import time import logging +from datetime import datetime from odoo import models, api, fields, _ @@ -268,18 +268,17 @@ def generate_payment_file(self): else: raise UserError(res.text) - # TODO - Devido a configuração do TimeZone os arquivos - # que estão sendo criados dependendo do horário estão - # sendo salvos com a data do dia seguinte + # Get user TIME ZONE to avoid generate file in 'future' + now_user_tz = fields.Datetime.context_timestamp(self, datetime.now()) if self.payment_mode_id.payment_method_id.code == '240': file_name = 'CB%s%s.REM' % ( - time.strftime('%d%m'), str(self.file_number)) + datetime.strftime(now_user_tz, '%d%m'), str(self.file_number)) elif self.payment_mode_id.payment_method_id.code == '400': file_name = 'CB%s%02d.REM' % ( - time.strftime('%d%m'), self.file_number or 1) + datetime.strftime(now_user_tz, '%d%m'), self.file_number or 1) elif self.payment_mode_id.payment_method_id.code == '500': file_name = 'PG%s%s.REM' % ( - time.strftime('%d%m'), str(self.file_number)) + datetime.strftime(now_user_tz, '%d%m'), str(self.file_number)) return remessa, file_name From 726b52f085caf2c3f8920190d8e5e49bdfc33d86 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 9 Sep 2020 14:48:26 -0300 Subject: [PATCH 038/308] [REF] Allow generate Remessa CNAB to banks implemented in BRCobranca. --- .../models/account_payment_order.py | 77 ++++++++++++++----- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index e0dc2d3cbd5f..016f9188fa94 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -67,6 +67,7 @@ def generate_payment_file(self): bank_account = \ self.payment_mode_id.fixed_journal_id.bank_account_id + # Lista de bancos não implentados no BRCobranca if bank_account.bank_id.code_bc in \ dict_brcobranca_bank: bank_name_brcobranca = \ @@ -76,12 +77,14 @@ def generate_payment_file(self): _('The Bank %s is not implemented in BRCobranca.') % bank_account.bank_id.name) - if (bank_name_brcobranca[0] not in ('bradesco', 'itau', 'unicred') - or self.payment_mode_id.payment_method_id.code != '400'): + # Informa se o CNAB especifico de um Banco não está implementado + # no BRCobranca, evitando a mensagem de erro mais extensa da lib + if (bank_name_brcobranca[0] == 'itau' + and self.payment_mode_id.payment_method_id.code == '240'): raise UserError( - _('The Bank %s and CNAB %s are not implemented.') - % (bank_account.bank_id.name, - self.payment_mode_id.payment_method_id.code)) + _('The CNAB %s for Bank %s are not implemented in BRCobranca.') + % (self.payment_mode_id.payment_method_id.code, + bank_account.bank_id.name,)) pagamentos = [] for line in self.payment_line_ids: @@ -204,31 +207,59 @@ def generate_payment_file(self): pagamentos.append(linhas_pagamentos) remessa_values = { - 'carteira': str(self.payment_mode_id.boleto_wallet), + 'carteira': str(payment_mode.boleto_wallet), 'agencia': int(bank_account.bra_number), - # 'digito_agencia': order.mode.bank_id.bra_number_dig, 'conta_corrente': int(misc.punctuation_rm(bank_account.acc_number)), 'digito_conta': bank_account.acc_number_dig[0], 'empresa_mae': bank_account.partner_id.legal_name[:30], 'documento_cedente': misc.punctuation_rm( bank_account.partner_id.cnpj_cpf), 'pagamentos': pagamentos, - 'sequencial_remessa': self.payment_mode_id.cnab_sequence_id.next_by_id(), + 'sequencial_remessa': payment_mode.cnab_sequence_id.next_by_id(), } + # Campos especificos de cada Banco if bank_name_brcobranca[0] == 'bradesco': remessa_values[ - 'codigo_empresa'] = int(self.payment_mode_id.code_convetion) + 'codigo_empresa'] = int(payment_mode.code_convetion) - # Field used in Sicredi and Sicoob Banks - if bank_account.bank_id.code_bc in ('748', '756'): + # Field used in Sicoob Banks + if bank_account.bank_id.code_bc == '756': remessa_values.update({ - 'codigo_transmissao': int(self.payment_mode_id.code_convetion), + 'codigo_transmissao': int(payment_mode.code_convetion), }) + + # Field used in Sicredi Banks + if bank_account.bank_id.code_bc == '748': + remessa_values.update({ + 'codigo_transmissao': int(payment_mode.code_convetion), + 'posto': payment_mode.boleto_post, + 'byte_idt': payment_mode.boleto_byte_idt, + }) + # Field used in Unicred Bank if bank_account.bank_id.code_bc == '136': + remessa_values[ + 'codigo_beneficiario'] = int(self.payment_mode_id.code_convetion) + + # Field used in Caixa Economica Federal + if bank_account.bank_id.code_bc == '104': remessa_values.update({ - 'codigo_beneficiario': int(self.payment_mode_id.code_convetion), + 'convenio': int(payment_mode.code_convetion), + 'digito_agencia': bank_account.bra_number_dig, + }) + + # Field used in Banco do Brasil + if bank_account.bank_id.code_bc == '001': + # TODO - BRCobranca retornando erro de agencia deve ter 4 digitos, + # mesmo o valor estando correto, é preciso verificar melhor + remessa_values.update({ + 'convenio': int(payment_mode.code_convetion), + 'variacao_carteira': payment_mode.boleto_variation, + # TODO - Mapear e se necessário criar os campos abaixo devido + # ao erro comentado acima não está sendo possível validar + 'tipo_cobranca': '04DSC', + 'convenio_lider': '7654321', }) content = json.dumps(remessa_values) @@ -238,8 +269,8 @@ def generate_payment_file(self): files = {'data': open(f.name, 'rb')} api_address = self.env[ - "ir.config_parameter"].sudo().get_param( - "l10n_br_account_payment_brcobranca.boleto_cnab_api") + 'ir.config_parameter'].sudo().get_param( + 'l10n_br_account_payment_brcobranca.boleto_cnab_api') if not api_address: raise UserError( @@ -257,13 +288,19 @@ def generate_payment_file(self): self.payment_mode_id.payment_method_id.code], 'bank': bank_name_brcobranca[0], }, files=files) - # print("AAAAAAAA", res.status_code, str(res.status_code)[0]) - # print('RES.CONTENT', res.content[0], type(res.content[0])) - # TODO - res.content[0] parece variar de acordo com banco, existe padrão ? - if bank_name_brcobranca[0] == 'bradesco' and res.content[0] == '0': + # TODO - res.content[0] parece variar de acordo com banco, + # existe padrão ? + # Provavelemnte está em 48 para CNAB 400 e 49 para o 240, é + # preciso testar e validar outros bancos e CNAB para confirmação + + # É preciso manter o codigo abaixo pois sem isso o programa + # irá gravar o LOG de erros do BRCobranca no arquivo gerado + # ao inves do conteudo do CNAB + if bank_name_brcobranca[0] == 'caixa' and res.content[0] == 49: remessa = res.content - elif bank_name_brcobranca[0] == 'unicred' and res.content[0] == 48: + elif bank_name_brcobranca[0] in ('unicred', 'bradesco', 'itau')\ + and res.content[0] == 48: remessa = res.content else: raise UserError(res.text) From 4fa01df1d40ca2cb67c9ba1e082b0f0b4378ee99 Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Thu, 10 Sep 2020 11:30:45 -0300 Subject: [PATCH 039/308] =?UTF-8?q?[REF]=20Verifica=C3=A7=C3=A3o=20do=20ar?= =?UTF-8?q?quivo=20de=20retorno=20cnab240=20e=20400?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Luis Felipe Mileo --- .../models/account_payment_order.py | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 016f9188fa94..d8ab1da5c1f8 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -3,7 +3,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging -from datetime import datetime from odoo import models, api, fields, _ @@ -64,6 +63,8 @@ def generate_payment_file(self): # and a test here: # https://github.com/kivanio/brcobranca/blob/master/spec/brcobranca/remessa/cnab400/itau_spec.rb + cnab_type = self.payment_mode_id.payment_method_code + bank_account = \ self.payment_mode_id.fixed_journal_id.bank_account_id @@ -80,7 +81,7 @@ def generate_payment_file(self): # Informa se o CNAB especifico de um Banco não está implementado # no BRCobranca, evitando a mensagem de erro mais extensa da lib if (bank_name_brcobranca[0] == 'itau' - and self.payment_mode_id.payment_method_id.code == '240'): + and cnab_type == '240'): raise UserError( _('The CNAB %s for Bank %s are not implemented in BRCobranca.') % (self.payment_mode_id.payment_method_id.code, @@ -285,37 +286,35 @@ def generate_payment_file(self): api_service_address, data={ 'type': dict_brcobranca_cnab_type[ - self.payment_mode_id.payment_method_id.code], + cnab_type], 'bank': bank_name_brcobranca[0], }, files=files) - # TODO - res.content[0] parece variar de acordo com banco, - # existe padrão ? - # Provavelemnte está em 48 para CNAB 400 e 49 para o 240, é - # preciso testar e validar outros bancos e CNAB para confirmação - - # É preciso manter o codigo abaixo pois sem isso o programa - # irá gravar o LOG de erros do BRCobranca no arquivo gerado - # ao inves do conteudo do CNAB - if bank_name_brcobranca[0] == 'caixa' and res.content[0] == 49: + 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. remessa = res.content - elif bank_name_brcobranca[0] in ('unicred', 'bradesco', 'itau')\ - and res.content[0] == 48: + elif cnab_type == '400' and res.text[:3] in ('01R', 'DCB'): + # A remessa 400 não tem um layout padronizado, + # entretanto a maiorias dos arquivos começa com 01REMESSA, + # o banco de brasilia começa com DCB... + # Dúvidas verificar exemplos: + # https://github.com/kivanio/brcobranca/tree/master/spec/fixtures/remessa remessa = res.content else: raise UserError(res.text) - # Get user TIME ZONE to avoid generate file in 'future' - now_user_tz = fields.Datetime.context_timestamp(self, datetime.now()) - if self.payment_mode_id.payment_method_id.code == '240': + context_today = fields.Date.context_today(self) + + if cnab_type == '240': file_name = 'CB%s%s.REM' % ( - datetime.strftime(now_user_tz, '%d%m'), str(self.file_number)) - elif self.payment_mode_id.payment_method_id.code == '400': + context_today.strftime('%d%m'), str(self.file_number)) + elif cnab_type == '400': file_name = 'CB%s%02d.REM' % ( - datetime.strftime(now_user_tz, '%d%m'), self.file_number or 1) - elif self.payment_mode_id.payment_method_id.code == '500': + context_today.strftime('%d%m'), self.file_number or 1) + elif cnab_type == '500': file_name = 'PG%s%s.REM' % ( - datetime.strftime(now_user_tz, '%d%m'), str(self.file_number)) + context_today.strftime('%d%m'), str(self.file_number)) return remessa, file_name From 5bc35c02540c649146cdfe4a0db6cb95c9792f38 Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Fri, 11 Sep 2020 08:19:52 -0300 Subject: [PATCH 040/308] =?UTF-8?q?[IMP]=20Lista=20de=20integra=C3=A7?= =?UTF-8?q?=C3=B5es=20com=20o=20brcobranca=20e=20valida=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Luis Felipe Mileo --- .../models/account_payment_order.py | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index d8ab1da5c1f8..0ef6ec1889a5 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -18,35 +18,42 @@ import requests import json import tempfile +from collections import namedtuple from odoo.exceptions import Warning as UserError + _logger = logging.getLogger(__name__) try: from cnab240.errors import (Cnab240Error) except ImportError as err: _logger.debug = err -dict_brcobranca_bank = { - '001': 'banco_brasil', - '041': 'banrisul', - '237': 'bradesco', - '104': 'caixa', - '399': 'hsbc', - '341': 'itau', - '033': 'santander', - '748': 'sicred', - '004': 'banco_nordeste', - '021': 'banestes', - '756': 'sicoob', - '136': 'unicred', -} - dict_brcobranca_cnab_type = { '240': 'cnab240', '400': 'cnab400', } +BankRecord = namedtuple('Bank', 'name, retorno, remessa') + +dict_brcobranca_bank = { + '001': BankRecord('banco_brasil', retorno=['400'], remessa=['240', '400']), + '004': BankRecord('banco_nordeste', retorno=['400'], remessa=['400']), + '021': BankRecord('banestes', retorno=[], remessa=[]), + '033': BankRecord('santander', retorno=['240'], remessa=['400']), + '041': BankRecord('banrisul', retorno=['400'], remessa=['400']), + '070': BankRecord('banco_brasilia', retorno=[], remessa=['400']), + '097': BankRecord('credisis', retorno=['400'], remessa=['400']), + '104': BankRecord('caixa', retorno=['240'], remessa=['240']), + '136': BankRecord('unicred', retorno=['400'], remessa=['240', '400']), + '237': BankRecord('bradesco', retorno=['400'], remessa=['400']), + '341': BankRecord('itau', retorno=['400'], remessa=['400']), + '399': BankRecord('hsbc', retorno=[], remessa=[]), + '745': BankRecord('citibank', retorno=[], remessa=['400']), + '748': BankRecord('sicred', retorno=['240'], remessa=['240']), + '756': BankRecord('sicoob', retorno=['240'], remessa=['240', '400']), +} + class PaymentOrder(models.Model): _inherit = "account.payment.order" @@ -64,28 +71,19 @@ def generate_payment_file(self): # https://github.com/kivanio/brcobranca/blob/master/spec/brcobranca/remessa/cnab400/itau_spec.rb cnab_type = self.payment_mode_id.payment_method_code - - bank_account = \ - self.payment_mode_id.fixed_journal_id.bank_account_id - - # Lista de bancos não implentados no BRCobranca - if bank_account.bank_id.code_bc in \ - dict_brcobranca_bank: - bank_name_brcobranca = \ - dict_brcobranca_bank[bank_account.bank_id.code_bc], - else: + bank_account = self.journal_id.bank_account_id + bank_name_brcobranca = dict_brcobranca_bank.get(bank_account.bank_id.code_bc) + if not bank_name_brcobranca: + # Lista de bancos não implentados no BRCobranca raise UserError( _('The Bank %s is not implemented in BRCobranca.') % bank_account.bank_id.name) - - # Informa se o CNAB especifico de um Banco não está implementado - # no BRCobranca, evitando a mensagem de erro mais extensa da lib - if (bank_name_brcobranca[0] == 'itau' - and cnab_type == '240'): + if cnab_type not in bank_name_brcobranca.remessa: + # Informa se o CNAB especifico de um Banco não está implementado + # no BRCobranca, evitando a mensagem de erro mais extensa da lib raise UserError( _('The CNAB %s for Bank %s are not implemented in BRCobranca.') - % (self.payment_mode_id.payment_method_id.code, - bank_account.bank_id.name,)) + % (cnab_type, bank_account.bank_id.name,)) pagamentos = [] for line in self.payment_line_ids: From c449f6a8bfa7b9277c5564b39a21a33d60d84158 Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Fri, 11 Sep 2020 08:20:49 -0300 Subject: [PATCH 041/308] [REF] payment_line_ids to bank_line_ids Signed-off-by: Luis Felipe Mileo --- .../models/account_payment_order.py | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 0ef6ec1889a5..c33838b6f1d6 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -86,11 +86,11 @@ def generate_payment_file(self): % (cnab_type, bank_account.bank_id.name,)) pagamentos = [] - for line in self.payment_line_ids: + for line in self.bank_line_ids: linhas_pagamentos = { 'valor': line.amount_currency, - 'data_vencimento': line.move_line_id.date_maturity.strftime('%Y/%m/%d'), - 'nosso_numero': line.move_line_id.own_number, + 'data_vencimento': line.date.strftime('%Y/%m/%d'), + 'nosso_numero': line.own_number, 'documento_sacado': misc.punctuation_rm(line.partner_id.cnpj_cpf), 'nome_sacado': line.partner_id.legal_name.strip()[:40], @@ -107,8 +107,6 @@ def generate_payment_file(self): 'identificacao_ocorrencia': self.movement_instruction_code } - payment_mode = line.move_line_id.payment_mode_id - if bank_name_brcobranca[0] == 'unicred': # TODO - Verificar se é uma tabela unica por banco ou há padrão # Identificação da Ocorrência: @@ -126,9 +124,9 @@ def generate_payment_file(self): # 40 - Alteração de Carteira linhas_pagamentos['identificacao_ocorrencia'] = '01' linhas_pagamentos['codigo_protesto'] = \ - payment_mode.boleto_protest_code or '3' + self.payment_mode_id.boleto_protest_code or '3' linhas_pagamentos['dias_protesto'] = \ - payment_mode.boleto_days_protest or '0' + self.payment_mode_id.boleto_days_protest or '0' # Código adotado pela FEBRABAN para identificação # do tipo de pagamento de multa. @@ -151,17 +149,17 @@ def generate_payment_file(self): # 00000005/01 linhas_pagamentos['numero'] = str(line.document_number)[1:11] - if payment_mode.boleto_fee_perc: + if self.payment_mode_id.boleto_fee_perc: linhas_pagamentos['codigo_multa'] = \ - payment_mode.boleto_fee_code + self.payment_mode_id.boleto_fee_code linhas_pagamentos['percentual_multa'] = \ - payment_mode.boleto_fee_perc + self.payment_mode_id.boleto_fee_perc precision = self.env['decimal.precision'] precision_account = precision.precision_get('Account') - if payment_mode.boleto_interest_perc: + if self.payment_mode_id.boleto_interest_perc: linhas_pagamentos['tipo_mora'] = \ - payment_mode.boleto_interest_perc + self.payment_mode_id.boleto_interest_perc # TODO - É padrão em todos os bancos ? # Código adotado pela FEBRABAN para identificação do tipo de # pagamento de mora de juros. @@ -176,37 +174,37 @@ def generate_payment_file(self): # segundo manual. Cógido mantido # para Correspondentes que ainda utilizam. # Isento de Mora caso não exista percentual - if payment_mode.boleto_interest_code == '1': + if self.payment_mode_id.boleto_interest_code == '1': linhas_pagamentos['valor_mora'] = round( - line.move_line_id.debit * - ((payment_mode.boleto_interest_code / 100) + line.amount_currency * + ((self.payment_mode_id.boleto_interest_code / 100) / 30), precision_account) - if payment_mode.boleto_interest_code == '2': + if self.payment_mode_id.boleto_interest_code == '2': linhas_pagamentos['valor_mora'] = \ - payment_mode.boleto_interest_code + self.payment_mode_id.boleto_interest_code - if payment_mode.boleto_discount_perc: + if self.payment_mode_id.boleto_discount_perc: linhas_pagamentos['data_desconto'] =\ - line.move_line_id.date_maturity.strftime('%Y/%m/%d') + line.date.strftime('%Y/%m/%d') linhas_pagamentos['valor_desconto'] = round( - line.move_line_id.debit * ( - payment_mode.boleto_discount_perc / 100), + line.amount_currency * ( + self.payment_mode_id.boleto_discount_perc / 100), precision_account) if bank_name_brcobranca[0] == 'unicred': linhas_pagamentos['cod_desconto'] = '1' # Protesto - if payment_mode.boleto_protest_code: + if self.payment_mode_id.boleto_protest_code: linhas_pagamentos['codigo_protesto'] = \ - payment_mode.boleto_protest_code - if payment_mode.boleto_days_protest: + self.payment_mode_id.boleto_protest_code + if self.payment_mode_id.boleto_days_protest: linhas_pagamentos['dias_protesto'] = \ - payment_mode.boleto_days_protest + self.payment_mode_id.boleto_days_protest pagamentos.append(linhas_pagamentos) remessa_values = { - 'carteira': str(payment_mode.boleto_wallet), + 'carteira': str(self.payment_mode_id.boleto_wallet), 'agencia': int(bank_account.bra_number), 'conta_corrente': int(misc.punctuation_rm(bank_account.acc_number)), 'digito_conta': bank_account.acc_number_dig[0], @@ -214,26 +212,26 @@ def generate_payment_file(self): 'documento_cedente': misc.punctuation_rm( bank_account.partner_id.cnpj_cpf), 'pagamentos': pagamentos, - 'sequencial_remessa': payment_mode.cnab_sequence_id.next_by_id(), + 'sequencial_remessa': self.payment_mode_id.cnab_sequence_id.next_by_id(), } # Campos especificos de cada Banco if bank_name_brcobranca[0] == 'bradesco': remessa_values[ - 'codigo_empresa'] = int(payment_mode.code_convetion) + 'codigo_empresa'] = int(self.payment_mode_id.code_convetion) # Field used in Sicoob Banks if bank_account.bank_id.code_bc == '756': remessa_values.update({ - 'codigo_transmissao': int(payment_mode.code_convetion), + 'codigo_transmissao': int(self.payment_mode_id.code_convetion), }) # Field used in Sicredi Banks if bank_account.bank_id.code_bc == '748': remessa_values.update({ - 'codigo_transmissao': int(payment_mode.code_convetion), - 'posto': payment_mode.boleto_post, - 'byte_idt': payment_mode.boleto_byte_idt, + 'codigo_transmissao': int(self.payment_mode_id.code_convetion), + 'posto': self.payment_mode_id.boleto_post, + 'byte_idt': self.payment_mode_id.boleto_byte_idt, }) # Field used in Unicred Bank @@ -244,7 +242,7 @@ def generate_payment_file(self): # Field used in Caixa Economica Federal if bank_account.bank_id.code_bc == '104': remessa_values.update({ - 'convenio': int(payment_mode.code_convetion), + 'convenio': int(self.payment_mode_id.code_convetion), 'digito_agencia': bank_account.bra_number_dig, }) @@ -253,8 +251,8 @@ def generate_payment_file(self): # TODO - BRCobranca retornando erro de agencia deve ter 4 digitos, # mesmo o valor estando correto, é preciso verificar melhor remessa_values.update({ - 'convenio': int(payment_mode.code_convetion), - 'variacao_carteira': payment_mode.boleto_variation, + 'convenio': int(self.payment_mode_id.code_convetion), + 'variacao_carteira': self.payment_mode_id.boleto_variation, # TODO - Mapear e se necessário criar os campos abaixo devido # ao erro comentado acima não está sendo possível validar 'tipo_cobranca': '04DSC', From 5d80fb8b8ea742a27d1e55db916fb629393605d8 Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Fri, 11 Sep 2020 09:10:35 -0300 Subject: [PATCH 042/308] [REM] Duplicated imports Signed-off-by: Luis Felipe Mileo --- .../models/account_payment_order.py | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index c33838b6f1d6..639eb57983d4 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -1,10 +1,17 @@ # Copyright 2020 Akretion # @author Magno Costa +# Copyright 2020 KMEE +# @author Luis Felipe Mileo # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import json import logging +import tempfile +from collections import namedtuple +import requests from odoo import models, api, fields, _ +from odoo.exceptions import Warning as UserError _logger = logging.getLogger(__name__) @@ -13,22 +20,6 @@ except ImportError: _logger.error("Biblioteca erpbrasil.base não instalada") -import logging - -import requests -import json -import tempfile -from collections import namedtuple -from odoo.exceptions import Warning as UserError - - - -_logger = logging.getLogger(__name__) -try: - from cnab240.errors import (Cnab240Error) -except ImportError as err: - _logger.debug = err - dict_brcobranca_cnab_type = { '240': 'cnab240', '400': 'cnab400', From d6f72457ca155282feccd41d57399bb248b8a61e Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Fri, 11 Sep 2020 09:10:52 -0300 Subject: [PATCH 043/308] [NEW] Check if payment mode is not grouped Signed-off-by: Luis Felipe Mileo --- .../models/account_payment_order.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 639eb57983d4..5173ac9a7393 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -64,6 +64,12 @@ def generate_payment_file(self): cnab_type = self.payment_mode_id.payment_method_code bank_account = self.journal_id.bank_account_id bank_name_brcobranca = dict_brcobranca_bank.get(bank_account.bank_id.code_bc) + + if self.payment_mode_id.group_lines: + raise UserError( + _('The Payment mode can not be used with the group lines active, \n ' + 'please uncheck it on payment mode configuration to c') + ) if not bank_name_brcobranca: # Lista de bancos não implentados no BRCobranca raise UserError( From dd662f032c1c1d60a838267e6843b343e16d09e4 Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Fri, 11 Sep 2020 09:11:37 -0300 Subject: [PATCH 044/308] [NEW] Split bank payment line in a new file Signed-off-by: Luis Felipe Mileo --- .../models/__init__.py | 1 + .../models/account_payment_order.py | 116 +-------------- .../models/bank_payment_line.py | 137 ++++++++++++++++++ 3 files changed, 139 insertions(+), 115 deletions(-) create mode 100644 l10n_br_account_payment_brcobranca/models/bank_payment_line.py diff --git a/l10n_br_account_payment_brcobranca/models/__init__.py b/l10n_br_account_payment_brcobranca/models/__init__.py index a8a8dddb6918..9f9a9aff0e51 100644 --- a/l10n_br_account_payment_brcobranca/models/__init__.py +++ b/l10n_br_account_payment_brcobranca/models/__init__.py @@ -7,3 +7,4 @@ from . import account_payment_order from . import l10n_br_cnab from . import res_config_settings +from . import bank_payment_line diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 5173ac9a7393..1bff1153d86d 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -84,121 +84,7 @@ def generate_payment_file(self): pagamentos = [] for line in self.bank_line_ids: - linhas_pagamentos = { - 'valor': line.amount_currency, - 'data_vencimento': line.date.strftime('%Y/%m/%d'), - 'nosso_numero': line.own_number, - 'documento_sacado': misc.punctuation_rm(line.partner_id.cnpj_cpf), - 'nome_sacado': - line.partner_id.legal_name.strip()[:40], - 'numero': str(line.document_number)[:10], - 'endereco_sacado': str( - line.partner_id.street + ', ' + str( - line.partner_id.street_number))[:40], - 'bairro_sacado': - line.partner_id.district.strip(), - 'cep_sacado': misc.punctuation_rm(line.partner_id.zip), - 'cidade_sacado': - line.partner_id.city_id.name, - 'uf_sacado': line.partner_id.state_id.code, - 'identificacao_ocorrencia': self.movement_instruction_code - } - - if bank_name_brcobranca[0] == 'unicred': - # TODO - Verificar se é uma tabela unica por banco ou há padrão - # Identificação da Ocorrência: - # 01 - Remessa* - # 02 - Pedido de Baixa - # 04 - Concessão de Abatimento* - # 05 - Cancelamento de Abatimento - # 06 - Alteração de vencimento - # 08 - Alteração de Seu Número - # 09 - Protestar* - # 11 - Sustar Protesto e Manter em Carteira - # 25 - Sustar Protesto e Baixar Título - # 26 – Protesto automático - # 31 - Alteração de outros dados (Alteração de dados do pagador) - # 40 - Alteração de Carteira - linhas_pagamentos['identificacao_ocorrencia'] = '01' - linhas_pagamentos['codigo_protesto'] = \ - self.payment_mode_id.boleto_protest_code or '3' - linhas_pagamentos['dias_protesto'] = \ - self.payment_mode_id.boleto_days_protest or '0' - - # Código adotado pela FEBRABAN para identificação - # do tipo de pagamento de multa. - # Domínio: - # ‘1’ = Valor Fixo (R$) - # ‘2’ = Taxa (%) - # ‘3’ = Isento - # Isento de Multa caso não exista percentual - linhas_pagamentos['codigo_multa'] = '3' - - # Isento de Mora - linhas_pagamentos['tipo_mora'] = '5' - - # TODO - # Código adotado pela FEBRABAN para identificação do desconto. - # Domínio: - # 0 = Isento - # 1 = Valor Fixo - linhas_pagamentos['cod_desconto'] = '0' - # 00000005/01 - linhas_pagamentos['numero'] = str(line.document_number)[1:11] - - if self.payment_mode_id.boleto_fee_perc: - linhas_pagamentos['codigo_multa'] = \ - self.payment_mode_id.boleto_fee_code - linhas_pagamentos['percentual_multa'] = \ - self.payment_mode_id.boleto_fee_perc - - precision = self.env['decimal.precision'] - precision_account = precision.precision_get('Account') - if self.payment_mode_id.boleto_interest_perc: - linhas_pagamentos['tipo_mora'] = \ - self.payment_mode_id.boleto_interest_perc - # TODO - É padrão em todos os bancos ? - # Código adotado pela FEBRABAN para identificação do tipo de - # pagamento de mora de juros. - # Domínio: - # ‘1’ = Valor Diário (R$) - # ‘2’ = Taxa Mensal (%) - # ‘3’= Valor Mensal (R$) * - # ‘4’ = Taxa diária (%) - # ‘5’ = Isento - # *OBSERVAÇÃO: - # ‘3’ - Valor Mensal (R$): a CIP não acata valor mensal, - # segundo manual. Cógido mantido - # para Correspondentes que ainda utilizam. - # Isento de Mora caso não exista percentual - if self.payment_mode_id.boleto_interest_code == '1': - linhas_pagamentos['valor_mora'] = round( - line.amount_currency * - ((self.payment_mode_id.boleto_interest_code / 100) - / 30), precision_account) - if self.payment_mode_id.boleto_interest_code == '2': - linhas_pagamentos['valor_mora'] = \ - self.payment_mode_id.boleto_interest_code - - if self.payment_mode_id.boleto_discount_perc: - linhas_pagamentos['data_desconto'] =\ - line.date.strftime('%Y/%m/%d') - linhas_pagamentos['valor_desconto'] = round( - line.amount_currency * ( - self.payment_mode_id.boleto_discount_perc / 100), - precision_account) - if bank_name_brcobranca[0] == 'unicred': - linhas_pagamentos['cod_desconto'] = '1' - - # Protesto - if self.payment_mode_id.boleto_protest_code: - linhas_pagamentos['codigo_protesto'] = \ - self.payment_mode_id.boleto_protest_code - if self.payment_mode_id.boleto_days_protest: - linhas_pagamentos['dias_protesto'] = \ - self.payment_mode_id.boleto_days_protest - - pagamentos.append(linhas_pagamentos) + pagamentos.append(line.prepare_bank_payment_line(bank_name_brcobranca)) remessa_values = { 'carteira': str(self.payment_mode_id.boleto_wallet), diff --git a/l10n_br_account_payment_brcobranca/models/bank_payment_line.py b/l10n_br_account_payment_brcobranca/models/bank_payment_line.py new file mode 100644 index 000000000000..bd2d33796168 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/models/bank_payment_line.py @@ -0,0 +1,137 @@ +# Copyright 2020 Akretion +# @author Magno Costa +# Copyright 2020 KMEE +# @author Luis Felipe Mileo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import models, api, fields, _ + +_logger = logging.getLogger(__name__) + +try: + from erpbrasil.base import misc +except ImportError: + _logger.error("Biblioteca erpbrasil.base não instalada") + + +class BankPaymentLine(models.Model): + _inherit = "bank.payment.line" + + def prepare_bank_payment_line(self, bank_name_brcobranca): + payment_mode_id = self.order_id.payment_mode_id + linhas_pagamentos = { + 'valor': self.amount_currency, + 'data_vencimento': self.date.strftime('%Y/%m/%d'), + 'nosso_numero': self.own_number, + 'documento_sacado': misc.punctuation_rm(self.partner_id.cnpj_cpf), + 'nome_sacado': + self.partner_id.legal_name.strip()[:40], + 'numero': str(self.document_number)[:10], + 'endereco_sacado': str( + self.partner_id.street + ', ' + str( + self.partner_id.street_number))[:40], + 'bairro_sacado': + self.partner_id.district.strip(), + 'cep_sacado': misc.punctuation_rm(self.partner_id.zip), + 'cidade_sacado': + self.partner_id.city_id.name, + 'uf_sacado': self.partner_id.state_id.code, + 'identificacao_ocorrencia': self.order_id.movement_instruction_code + } + + if bank_name_brcobranca[0] == 'unicred': + # TODO - Verificar se é uma tabela unica por banco ou há padrão + # Identificação da Ocorrência: + # 01 - Remessa* + # 02 - Pedido de Baixa + # 04 - Concessão de Abatimento* + # 05 - Cancelamento de Abatimento + # 06 - Alteração de vencimento + # 08 - Alteração de Seu Número + # 09 - Protestar* + # 11 - Sustar Protesto e Manter em Carteira + # 25 - Sustar Protesto e Baixar Título + # 26 – Protesto automático + # 31 - Alteração de outros dados (Alteração de dados do pagador) + # 40 - Alteração de Carteira + linhas_pagamentos['identificacao_ocorrencia'] = '01' + linhas_pagamentos['codigo_protesto'] = \ + payment_mode_id.boleto_protest_code or '3' + linhas_pagamentos['dias_protesto'] = \ + payment_mode_id.boleto_days_protest or '0' + + # Código adotado pela FEBRABAN para identificação + # do tipo de pagamento de multa. + # Domínio: + # ‘1’ = Valor Fixo (R$) + # ‘2’ = Taxa (%) + # ‘3’ = Isento + # Isento de Multa caso não exista percentual + linhas_pagamentos['codigo_multa'] = '3' + + # Isento de Mora + linhas_pagamentos['tipo_mora'] = '5' + + # TODO + # Código adotado pela FEBRABAN para identificação do desconto. + # Domínio: + # 0 = Isento + # 1 = Valor Fixo + linhas_pagamentos['cod_desconto'] = '0' + # 00000005/01 + linhas_pagamentos['numero'] = str(self.document_number)[1:11] + + if payment_mode_id.boleto_fee_perc: + linhas_pagamentos['codigo_multa'] = \ + payment_mode_id.boleto_fee_code + linhas_pagamentos['percentual_multa'] = \ + payment_mode_id.boleto_fee_perc + + precision = self.env['decimal.precision'] + precision_account = precision.precision_get('Account') + if payment_mode_id.boleto_interest_perc: + linhas_pagamentos['tipo_mora'] = \ + payment_mode_id.boleto_interest_perc + # TODO - É padrão em todos os bancos ? + # Código adotado pela FEBRABAN para identificação do tipo de + # pagamento de mora de juros. + # Domínio: + # ‘1’ = Valor Diário (R$) + # ‘2’ = Taxa Mensal (%) + # ‘3’= Valor Mensal (R$) * + # ‘4’ = Taxa diária (%) + # ‘5’ = Isento + # *OBSERVAÇÃO: + # ‘3’ - Valor Mensal (R$): a CIP não acata valor mensal, + # segundo manual. Cógido mantido + # para Correspondentes que ainda utilizam. + # Isento de Mora caso não exista percentual + if payment_mode_id.boleto_interest_code == '1': + linhas_pagamentos['valor_mora'] = round( + self.amount_currency * + ((payment_mode_id.boleto_interest_code / 100) + / 30), precision_account) + if payment_mode_id.boleto_interest_code == '2': + linhas_pagamentos['valor_mora'] = \ + payment_mode_id.boleto_interest_code + + if payment_mode_id.boleto_discount_perc: + linhas_pagamentos['data_desconto'] = \ + self.date.strftime('%Y/%m/%d') + linhas_pagamentos['valor_desconto'] = round( + self.amount_currency * ( + payment_mode_id.boleto_discount_perc / 100), + precision_account) + if bank_name_brcobranca[0] == 'unicred': + linhas_pagamentos['cod_desconto'] = '1' + + # Protesto + if payment_mode_id.boleto_protest_code: + linhas_pagamentos['codigo_protesto'] = \ + payment_mode_id.boleto_protest_code + if payment_mode_id.boleto_days_protest: + linhas_pagamentos['dias_protesto'] = \ + payment_mode_id.boleto_days_protest + return linhas_pagamentos From d7072173cb0cd8ea5cbe33209799aaf369381291 Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Fri, 11 Sep 2020 10:28:51 -0300 Subject: [PATCH 045/308] [NEW] Constants and code split Signed-off-by: Luis Felipe Mileo --- .../constants/br_cobranca.py | 32 ++++ .../models/account_payment_order.py | 160 ++++++++---------- .../models/bank_payment_line.py | 100 ++++++----- 3 files changed, 163 insertions(+), 129 deletions(-) create mode 100644 l10n_br_account_payment_brcobranca/constants/br_cobranca.py diff --git a/l10n_br_account_payment_brcobranca/constants/br_cobranca.py b/l10n_br_account_payment_brcobranca/constants/br_cobranca.py new file mode 100644 index 000000000000..e3e508268280 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/constants/br_cobranca.py @@ -0,0 +1,32 @@ +# Copyright 2020 Akretion +# @author Magno Costa +# Copyright 2020 KMEE +# @author Luis Felipe Mileo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from collections import namedtuple + +DICT_BRCOBRANCA_CNAB_TYPE = { + '240': 'cnab240', + '400': 'cnab400', +} + +BankRecord = namedtuple('Bank', 'name, retorno, remessa') + +DICT_BRCOBRANCA_BANK = { + '001': BankRecord('banco_brasil', retorno=['400'], remessa=['240', '400']), + '004': BankRecord('banco_nordeste', retorno=['400'], remessa=['400']), + '021': BankRecord('banestes', retorno=[], remessa=[]), + '033': BankRecord('santander', retorno=['240'], remessa=['400']), + '041': BankRecord('banrisul', retorno=['400'], remessa=['400']), + '070': BankRecord('banco_brasilia', retorno=[], remessa=['400']), + '097': BankRecord('credisis', retorno=['400'], remessa=['400']), + '104': BankRecord('caixa', retorno=['240'], remessa=['240']), + '136': BankRecord('unicred', retorno=['400'], remessa=['240', '400']), + '237': BankRecord('bradesco', retorno=['400'], remessa=['400']), + '341': BankRecord('itau', retorno=['400'], remessa=['400']), + '399': BankRecord('hsbc', retorno=[], remessa=[]), + '745': BankRecord('citibank', retorno=[], remessa=['400']), + '748': BankRecord('sicred', retorno=['240'], remessa=['240']), + '756': BankRecord('sicoob', retorno=['240'], remessa=['240', '400']), +} diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 1bff1153d86d..f347a3ebb78e 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -7,11 +7,15 @@ import json import logging import tempfile -from collections import namedtuple import requests from odoo import models, api, fields, _ from odoo.exceptions import Warning as UserError +from ..constants.br_cobranca import ( + DICT_BRCOBRANCA_CNAB_TYPE, + DICT_BRCOBRANCA_BANK, +) + _logger = logging.getLogger(__name__) @@ -20,35 +24,60 @@ except ImportError: _logger.error("Biblioteca erpbrasil.base não instalada") -dict_brcobranca_cnab_type = { - '240': 'cnab240', - '400': 'cnab400', -} - -BankRecord = namedtuple('Bank', 'name, retorno, remessa') - -dict_brcobranca_bank = { - '001': BankRecord('banco_brasil', retorno=['400'], remessa=['240', '400']), - '004': BankRecord('banco_nordeste', retorno=['400'], remessa=['400']), - '021': BankRecord('banestes', retorno=[], remessa=[]), - '033': BankRecord('santander', retorno=['240'], remessa=['400']), - '041': BankRecord('banrisul', retorno=['400'], remessa=['400']), - '070': BankRecord('banco_brasilia', retorno=[], remessa=['400']), - '097': BankRecord('credisis', retorno=['400'], remessa=['400']), - '104': BankRecord('caixa', retorno=['240'], remessa=['240']), - '136': BankRecord('unicred', retorno=['400'], remessa=['240', '400']), - '237': BankRecord('bradesco', retorno=['400'], remessa=['400']), - '341': BankRecord('itau', retorno=['400'], remessa=['400']), - '399': BankRecord('hsbc', retorno=[], remessa=[]), - '745': BankRecord('citibank', retorno=[], remessa=['400']), - '748': BankRecord('sicred', retorno=['240'], remessa=['240']), - '756': BankRecord('sicoob', retorno=['240'], remessa=['240', '400']), -} class PaymentOrder(models.Model): _inherit = "account.payment.order" + def _prepare_remessa_banco_brasil(self, remessa_values): + # TODO - BRCobranca retornando erro de agencia deve ter 4 digitos, + # mesmo o valor estando correto, é preciso verificar melhor + remessa_values.update({ + 'convenio': int(self.payment_mode_id.code_convetion), + 'variacao_carteira': self.payment_mode_id.boleto_variation, + # TODO - Mapear e se necessário criar os campos abaixo devido + # ao erro comentado acima não está sendo possível validar + 'tipo_cobranca': '04DSC', + 'convenio_lider': '7654321', + }) + + def _prepare_remessa_caixa(self, remessa_values): + remessa_values.update({ + 'convenio': int(self.payment_mode_id.code_convetion), + 'digito_agencia': self.journal_id.bank_account_id.bra_number_dig, + }) + + def _prepare_remessa_unicred(self, remessa_values): + remessa_values[ + 'codigo_beneficiario'] = int(self.payment_mode_id.code_convetion) + + def _prepare_remessa_sicred(self, remessa_values): + remessa_values.update({ + 'codigo_transmissao': int(self.payment_mode_id.code_convetion), + 'posto': self.payment_mode_id.boleto_post, + 'byte_idt': self.payment_mode_id.boleto_byte_idt, + }) + + def _prepare_remessa_sicoob(self, remessa_values): + remessa_values.update({ + 'codigo_transmissao': int(self.payment_mode_id.code_convetion), + }) + + def _prepare_remessa_bradesco(self, remessa_values): + remessa_values['codigo_empresa'] = int(self.payment_mode_id.code_convetion) + + def get_file_name(self, cnab_type): + context_today = fields.Date.context_today(self) + if cnab_type == '240': + return 'CB%s%s.REM' % ( + context_today.strftime('%d%m'), str(self.file_number)) + elif cnab_type == '400': + return 'CB%s%02d.REM' % ( + context_today.strftime('%d%m'), self.file_number or 1) + elif cnab_type == '500': + return 'PG%s%s.REM' % ( + context_today.strftime('%d%m'), str(self.file_number)) + @api.multi def generate_payment_file(self): """Returns (payment file as string, filename)""" @@ -63,12 +92,18 @@ def generate_payment_file(self): cnab_type = self.payment_mode_id.payment_method_code bank_account = self.journal_id.bank_account_id - bank_name_brcobranca = dict_brcobranca_bank.get(bank_account.bank_id.code_bc) + bank_name_brcobranca = DICT_BRCOBRANCA_BANK.get(bank_account.bank_id.code_bc) if self.payment_mode_id.group_lines: raise UserError( _('The Payment mode can not be used with the group lines active, \n ' - 'please uncheck it on payment mode configuration to c') + 'please uncheck it on payment mode configuration to continue') + ) + if self.payment_mode_id.generate_move or self.payment_mode_id.post_move: + raise UserError( + _('The Payment mode can not be used with the generated moves or' + ' post moves active \n Please uncheck it on payment mode' + ' configuration to continue') ) if not bank_name_brcobranca: # Lista de bancos não implentados no BRCobranca @@ -84,7 +119,9 @@ def generate_payment_file(self): pagamentos = [] for line in self.bank_line_ids: - pagamentos.append(line.prepare_bank_payment_line(bank_name_brcobranca)) + pagamentos.append(line.prepare_bank_payment_line( + bank_name_brcobranca + )) remessa_values = { 'carteira': str(self.payment_mode_id.boleto_wallet), @@ -98,49 +135,14 @@ def generate_payment_file(self): 'sequencial_remessa': self.payment_mode_id.cnab_sequence_id.next_by_id(), } - # Campos especificos de cada Banco - if bank_name_brcobranca[0] == 'bradesco': - remessa_values[ - 'codigo_empresa'] = int(self.payment_mode_id.code_convetion) - - # Field used in Sicoob Banks - if bank_account.bank_id.code_bc == '756': - remessa_values.update({ - 'codigo_transmissao': int(self.payment_mode_id.code_convetion), - }) - - # Field used in Sicredi Banks - if bank_account.bank_id.code_bc == '748': - remessa_values.update({ - 'codigo_transmissao': int(self.payment_mode_id.code_convetion), - 'posto': self.payment_mode_id.boleto_post, - 'byte_idt': self.payment_mode_id.boleto_byte_idt, - }) - - # Field used in Unicred Bank - if bank_account.bank_id.code_bc == '136': - remessa_values[ - 'codigo_beneficiario'] = int(self.payment_mode_id.code_convetion) - - # Field used in Caixa Economica Federal - if bank_account.bank_id.code_bc == '104': - remessa_values.update({ - 'convenio': int(self.payment_mode_id.code_convetion), - 'digito_agencia': bank_account.bra_number_dig, - }) - - # Field used in Banco do Brasil - if bank_account.bank_id.code_bc == '001': - # TODO - BRCobranca retornando erro de agencia deve ter 4 digitos, - # mesmo o valor estando correto, é preciso verificar melhor - remessa_values.update({ - 'convenio': int(self.payment_mode_id.code_convetion), - 'variacao_carteira': self.payment_mode_id.boleto_variation, - # TODO - Mapear e se necessário criar os campos abaixo devido - # ao erro comentado acima não está sendo possível validar - 'tipo_cobranca': '04DSC', - 'convenio_lider': '7654321', - }) + try: + bank_method = getattr( + self, '_prepare_remessa_{}'.format(bank_name_brcobranca.name) + ) + if bank_method: + bank_method(remessa_values) + except: + pass content = json.dumps(remessa_values) f = open(tempfile.mktemp(), 'w') @@ -164,7 +166,7 @@ def generate_payment_file(self): res = requests.post( api_service_address, data={ - 'type': dict_brcobranca_cnab_type[ + 'type': DICT_BRCOBRANCA_CNAB_TYPE[ cnab_type], 'bank': bank_name_brcobranca[0], }, files=files) @@ -183,19 +185,7 @@ def generate_payment_file(self): else: raise UserError(res.text) - context_today = fields.Date.context_today(self) - - if cnab_type == '240': - file_name = 'CB%s%s.REM' % ( - context_today.strftime('%d%m'), str(self.file_number)) - elif cnab_type == '400': - file_name = 'CB%s%02d.REM' % ( - context_today.strftime('%d%m'), self.file_number or 1) - elif cnab_type == '500': - file_name = 'PG%s%s.REM' % ( - context_today.strftime('%d%m'), str(self.file_number)) - - return remessa, file_name + return remessa, self.get_file_name(cnab_type) def generated2uploaded(self): super().generated2uploaded() diff --git a/l10n_br_account_payment_brcobranca/models/bank_payment_line.py b/l10n_br_account_payment_brcobranca/models/bank_payment_line.py index bd2d33796168..8d3821bd7d06 100644 --- a/l10n_br_account_payment_brcobranca/models/bank_payment_line.py +++ b/l10n_br_account_payment_brcobranca/models/bank_payment_line.py @@ -18,10 +18,9 @@ class BankPaymentLine(models.Model): _inherit = "bank.payment.line" - - def prepare_bank_payment_line(self, bank_name_brcobranca): - payment_mode_id = self.order_id.payment_mode_id - linhas_pagamentos = { + + def _prepare_bank_line_vals(self): + return { 'valor': self.amount_currency, 'data_vencimento': self.date.strftime('%Y/%m/%d'), 'nosso_numero': self.own_number, @@ -41,47 +40,62 @@ def prepare_bank_payment_line(self, bank_name_brcobranca): 'identificacao_ocorrencia': self.order_id.movement_instruction_code } - if bank_name_brcobranca[0] == 'unicred': - # TODO - Verificar se é uma tabela unica por banco ou há padrão - # Identificação da Ocorrência: - # 01 - Remessa* - # 02 - Pedido de Baixa - # 04 - Concessão de Abatimento* - # 05 - Cancelamento de Abatimento - # 06 - Alteração de vencimento - # 08 - Alteração de Seu Número - # 09 - Protestar* - # 11 - Sustar Protesto e Manter em Carteira - # 25 - Sustar Protesto e Baixar Título - # 26 – Protesto automático - # 31 - Alteração de outros dados (Alteração de dados do pagador) - # 40 - Alteração de Carteira - linhas_pagamentos['identificacao_ocorrencia'] = '01' - linhas_pagamentos['codigo_protesto'] = \ - payment_mode_id.boleto_protest_code or '3' - linhas_pagamentos['dias_protesto'] = \ - payment_mode_id.boleto_days_protest or '0' + def _prepare_bank_line_unicred(self, payment_mode_id, linhas_pagamentos): + # TODO - Verificar se é uma tabela unica por banco ou há padrão + # Identificação da Ocorrência: + # 01 - Remessa* + # 02 - Pedido de Baixa + # 04 - Concessão de Abatimento* + # 05 - Cancelamento de Abatimento + # 06 - Alteração de vencimento + # 08 - Alteração de Seu Número + # 09 - Protestar* + # 11 - Sustar Protesto e Manter em Carteira + # 25 - Sustar Protesto e Baixar Título + # 26 – Protesto automático + # 31 - Alteração de outros dados (Alteração de dados do pagador) + # 40 - Alteração de Carteira + linhas_pagamentos['identificacao_ocorrencia'] = '01' + linhas_pagamentos['codigo_protesto'] = \ + payment_mode_id.boleto_protest_code or '3' + linhas_pagamentos['dias_protesto'] = \ + payment_mode_id.boleto_days_protest or '0' - # Código adotado pela FEBRABAN para identificação - # do tipo de pagamento de multa. - # Domínio: - # ‘1’ = Valor Fixo (R$) - # ‘2’ = Taxa (%) - # ‘3’ = Isento - # Isento de Multa caso não exista percentual - linhas_pagamentos['codigo_multa'] = '3' + # Código adotado pela FEBRABAN para identificação + # do tipo de pagamento de multa. + # Domínio: + # ‘1’ = Valor Fixo (R$) + # ‘2’ = Taxa (%) + # ‘3’ = Isento + # Isento de Multa caso não exista percentual + linhas_pagamentos['codigo_multa'] = '3' - # Isento de Mora - linhas_pagamentos['tipo_mora'] = '5' + # Isento de Mora + linhas_pagamentos['tipo_mora'] = '5' - # TODO - # Código adotado pela FEBRABAN para identificação do desconto. - # Domínio: - # 0 = Isento - # 1 = Valor Fixo - linhas_pagamentos['cod_desconto'] = '0' - # 00000005/01 - linhas_pagamentos['numero'] = str(self.document_number)[1:11] + # TODO + # Código adotado pela FEBRABAN para identificação do desconto. + # Domínio: + # 0 = Isento + # 1 = Valor Fixo + linhas_pagamentos['cod_desconto'] = '0' + # 00000005/01 + linhas_pagamentos['numero'] = str(self.document_number)[1:11] + + if payment_mode_id.boleto_discount_perc: + linhas_pagamentos['cod_desconto'] = '1' + + def prepare_bank_payment_line(self, bank_name_brcobranca): + payment_mode_id = self.order_id.payment_mode_id + linhas_pagamentos = self._prepare_bank_line_vals() + try: + bank_method = getattr( + self, '_prepare_bank_line_{}'.format(bank_name_brcobranca.name) + ) + if bank_method: + bank_method(payment_mode_id, linhas_pagamentos) + except: + pass if payment_mode_id.boleto_fee_perc: linhas_pagamentos['codigo_multa'] = \ @@ -124,8 +138,6 @@ def prepare_bank_payment_line(self, bank_name_brcobranca): self.amount_currency * ( payment_mode_id.boleto_discount_perc / 100), precision_account) - if bank_name_brcobranca[0] == 'unicred': - linhas_pagamentos['cod_desconto'] = '1' # Protesto if payment_mode_id.boleto_protest_code: From 6252e4f514fc46f58e71440d57bdca50a51253cb Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Fri, 11 Sep 2020 12:18:20 -0300 Subject: [PATCH 046/308] [REF] One method per bank cnab type Signed-off-by: Luis Felipe Mileo --- .../models/account_payment_order.py | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index f347a3ebb78e..5197562914d6 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -29,41 +29,37 @@ class PaymentOrder(models.Model): _inherit = "account.payment.order" - def _prepare_remessa_banco_brasil(self, remessa_values): + def _prepare_remessa_banco_brasil_400(self, remessa_values): # TODO - BRCobranca retornando erro de agencia deve ter 4 digitos, # mesmo o valor estando correto, é preciso verificar melhor remessa_values.update({ 'convenio': int(self.payment_mode_id.code_convetion), - 'variacao_carteira': self.payment_mode_id.boleto_variation, + 'variacao_carteira': self.payment_mode_id.boleto_variation.zfill(3), # TODO - Mapear e se necessário criar os campos abaixo devido # ao erro comentado acima não está sendo possível validar 'tipo_cobranca': '04DSC', 'convenio_lider': '7654321', + 'carteira': str(self.payment_mode_id.boleto_wallet).zfill(2), }) - def _prepare_remessa_caixa(self, remessa_values): + def _prepare_remessa_caixa_240(self, remessa_values): remessa_values.update({ 'convenio': int(self.payment_mode_id.code_convetion), 'digito_agencia': self.journal_id.bank_account_id.bra_number_dig, }) - def _prepare_remessa_unicred(self, remessa_values): + def _prepare_remessa_unicred_400(self, remessa_values): remessa_values[ 'codigo_beneficiario'] = int(self.payment_mode_id.code_convetion) - def _prepare_remessa_sicred(self, remessa_values): + def _prepare_remessa_sicred_240(self, remessa_values): remessa_values.update({ 'codigo_transmissao': int(self.payment_mode_id.code_convetion), 'posto': self.payment_mode_id.boleto_post, 'byte_idt': self.payment_mode_id.boleto_byte_idt, }) - def _prepare_remessa_sicoob(self, remessa_values): - remessa_values.update({ - 'codigo_transmissao': int(self.payment_mode_id.code_convetion), - }) - - def _prepare_remessa_bradesco(self, remessa_values): + def _prepare_remessa_bradesco_400(self, remessa_values): remessa_values['codigo_empresa'] = int(self.payment_mode_id.code_convetion) def get_file_name(self, cnab_type): @@ -125,7 +121,7 @@ def generate_payment_file(self): remessa_values = { 'carteira': str(self.payment_mode_id.boleto_wallet), - 'agencia': int(bank_account.bra_number), + 'agencia': bank_account.bra_number, 'conta_corrente': int(misc.punctuation_rm(bank_account.acc_number)), 'digito_conta': bank_account.acc_number_dig[0], 'empresa_mae': bank_account.partner_id.legal_name[:30], @@ -137,7 +133,10 @@ def generate_payment_file(self): try: bank_method = getattr( - self, '_prepare_remessa_{}'.format(bank_name_brcobranca.name) + self, '_prepare_remessa_{}_{}'.format( + bank_name_brcobranca.name, + cnab_type + ) ) if bank_method: bank_method(remessa_values) From 27ac3e12f1911fd6cd825e1c42a15dc811c777da Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Fri, 11 Sep 2020 13:13:36 -0300 Subject: [PATCH 047/308] [REF] Remove duplicated code Signed-off-by: Luis Felipe Mileo --- .../constants/br_cobranca.py | 17 +++++++ .../models/account_move_line.py | 47 +++++-------------- .../models/account_payment_order.py | 32 +++++-------- 3 files changed, 41 insertions(+), 55 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/constants/br_cobranca.py b/l10n_br_account_payment_brcobranca/constants/br_cobranca.py index e3e508268280..a59fca955ccb 100644 --- a/l10n_br_account_payment_brcobranca/constants/br_cobranca.py +++ b/l10n_br_account_payment_brcobranca/constants/br_cobranca.py @@ -6,6 +6,9 @@ from collections import namedtuple +from odoo import _ +from odoo.exceptions import Warning as UserError + DICT_BRCOBRANCA_CNAB_TYPE = { '240': 'cnab240', '400': 'cnab400', @@ -30,3 +33,17 @@ '748': BankRecord('sicred', retorno=['240'], remessa=['240']), '756': BankRecord('sicoob', retorno=['240'], remessa=['240', '400']), } + +DICT_BRCOBRANCA_CURRENCY = { + 'R$': '9', +} + + +def get_brcobranca_bank(bank_account_id): + bank_name_brcobranca = DICT_BRCOBRANCA_BANK.get(bank_account_id.bank_id.code_bc) + if not bank_name_brcobranca: + # Lista de bancos não implentados no BRCobranca + raise UserError( + _('The Bank %s is not implemented in BRCobranca.') + % bank_account_id.bank_id.name) + return bank_name_brcobranca \ No newline at end of file diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index f54ad1c5de93..2b4412be61ef 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -3,10 +3,12 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging -from datetime import datetime from odoo import models, api, _ -from odoo.exceptions import Warning as UserError +from ..constants.br_cobranca import ( + get_brcobranca_bank, + DICT_BRCOBRANCA_CURRENCY, +) _logger = logging.getLogger(__name__) @@ -28,26 +30,6 @@ def __getattr__(self, attr): return getattr(self._wrapped_obj, attr) -dict_brcobranca_bank = { - '001': 'banco_brasil', - '041': 'banrisul', - '237': 'bradesco', - '104': 'caixa', - '399': 'hsbc', - '341': 'itau', - '033': 'santander', - '748': 'sicred', - '004': 'banco_nordeste', - '021': 'banestes', - '756': 'sicoob', - '136': 'unicred', -} - -dict_brcobranca_currency = { - 'R$': '9', -} - - class AccountMoveLine(models.Model): _inherit = 'account.move.line' # see the list of brcobranca boleto fields: @@ -64,16 +46,9 @@ def send_payment(self): wrapped_boleto_list = [] for move_line in self: - bank_account = \ - move_line.payment_mode_id.fixed_journal_id.bank_account_id - if bank_account.bank_id.code_bc in dict_brcobranca_bank: - bank_name_brcobranca = dict_brcobranca_bank[ - bank_account.bank_id.code_bc], - else: - raise UserError( - _('The Bank %s is not implemented in BRCobranca.') % - bank_account.bank_id.name) + bank_account_id = move_line.payment_mode_id.fixed_journal_id.bank_account_id + bank_name_brcobranca = get_brcobranca_bank(bank_account_id) precision = self.env['decimal.precision'] precision_account = precision.precision_get('Account') @@ -91,8 +66,8 @@ def send_payment(self): 'documento_cedente': move_line.company_id.cnpj_cpf, 'sacado': move_line.partner_id.legal_name, 'sacado_documento': move_line.partner_id.cnpj_cpf, - 'agencia': bank_account.bra_number, - 'conta_corrente': bank_account.acc_number, + 'agencia': bank_account_id.bra_number, + 'conta_corrente': bank_account_id.acc_number, 'convenio': move_line.payment_mode_id.boleto_convetion, 'carteira': str(move_line.payment_mode_id.boleto_wallet), 'nosso_numero': int(''.join( @@ -103,7 +78,7 @@ def send_payment(self): 'data_documento': move_line.invoice_id.date_invoice.strftime('%Y/%m/%d'), 'especie': move_line.payment_mode_id.boleto_species, - 'moeda': dict_brcobranca_currency['R$'], + 'moeda': DICT_BRCOBRANCA_CURRENCY['R$'], 'aceite': move_line.payment_mode_id.boleto_accept, 'sacado_endereco': move_line.partner_id.street or '' + ', ' + @@ -165,14 +140,14 @@ def send_payment(self): 'instrucao5': instrucao_desconto_vencimento, }) - if bank_account.bank_id.code_bc in ('021', '004'): + if bank_account_id.bank_id.code_bc in ('021', '004'): boleto_cnab_api_data.update({ 'digito_conta_corrente': move_line.payment_mode_id.bank_id.acc_number_dig, }) # Fields used in Sicredi and Sicoob Banks - if bank_account.bank_id.code_bc in ('748', '756'): + if bank_account_id.bank_id.code_bc in ('748', '756'): boleto_cnab_api_data.update({ 'byte_idt': move_line.payment_mode_id.boleto_byte_idt, 'posto': move_line.payment_mode_id.boleto_posto, diff --git a/l10n_br_account_payment_brcobranca/models/account_payment_order.py b/l10n_br_account_payment_brcobranca/models/account_payment_order.py index 5197562914d6..5282ceb88e18 100644 --- a/l10n_br_account_payment_brcobranca/models/account_payment_order.py +++ b/l10n_br_account_payment_brcobranca/models/account_payment_order.py @@ -13,7 +13,7 @@ from odoo.exceptions import Warning as UserError from ..constants.br_cobranca import ( DICT_BRCOBRANCA_CNAB_TYPE, - DICT_BRCOBRANCA_BANK, + get_brcobranca_bank, ) @@ -25,7 +25,6 @@ _logger.error("Biblioteca erpbrasil.base não instalada") - class PaymentOrder(models.Model): _inherit = "account.payment.order" @@ -87,8 +86,8 @@ def generate_payment_file(self): # https://github.com/kivanio/brcobranca/blob/master/spec/brcobranca/remessa/cnab400/itau_spec.rb cnab_type = self.payment_mode_id.payment_method_code - bank_account = self.journal_id.bank_account_id - bank_name_brcobranca = DICT_BRCOBRANCA_BANK.get(bank_account.bank_id.code_bc) + bank_account_id = self.journal_id.bank_account_id + bank_brcobranca = get_brcobranca_bank(bank_account_id) if self.payment_mode_id.group_lines: raise UserError( @@ -101,32 +100,27 @@ def generate_payment_file(self): ' post moves active \n Please uncheck it on payment mode' ' configuration to continue') ) - if not bank_name_brcobranca: - # Lista de bancos não implentados no BRCobranca - raise UserError( - _('The Bank %s is not implemented in BRCobranca.') - % bank_account.bank_id.name) - if cnab_type not in bank_name_brcobranca.remessa: + if cnab_type not in bank_brcobranca.remessa: # Informa se o CNAB especifico de um Banco não está implementado # no BRCobranca, evitando a mensagem de erro mais extensa da lib raise UserError( _('The CNAB %s for Bank %s are not implemented in BRCobranca.') - % (cnab_type, bank_account.bank_id.name,)) + % (cnab_type, bank_account_id.bank_id.name,)) pagamentos = [] for line in self.bank_line_ids: pagamentos.append(line.prepare_bank_payment_line( - bank_name_brcobranca + bank_brcobranca )) remessa_values = { 'carteira': str(self.payment_mode_id.boleto_wallet), - 'agencia': bank_account.bra_number, - 'conta_corrente': int(misc.punctuation_rm(bank_account.acc_number)), - 'digito_conta': bank_account.acc_number_dig[0], - 'empresa_mae': bank_account.partner_id.legal_name[:30], + 'agencia': bank_account_id.bra_number, + 'conta_corrente': int(misc.punctuation_rm(bank_account_id.acc_number)), + 'digito_conta': bank_account_id.acc_number_dig[0], + 'empresa_mae': bank_account_id.partner_id.legal_name[:30], 'documento_cedente': misc.punctuation_rm( - bank_account.partner_id.cnpj_cpf), + bank_account_id.partner_id.cnpj_cpf), 'pagamentos': pagamentos, 'sequencial_remessa': self.payment_mode_id.cnab_sequence_id.next_by_id(), } @@ -134,7 +128,7 @@ def generate_payment_file(self): try: bank_method = getattr( self, '_prepare_remessa_{}_{}'.format( - bank_name_brcobranca.name, + bank_brcobranca.name, cnab_type ) ) @@ -167,7 +161,7 @@ def generate_payment_file(self): data={ 'type': DICT_BRCOBRANCA_CNAB_TYPE[ cnab_type], - 'bank': bank_name_brcobranca[0], + 'bank': bank_brcobranca.name, }, files=files) if cnab_type == '240' and 'R01' in res.text[242:254]: From 22addc94c7ba83f5c25e4201f0fd95eb3a9c3a5d Mon Sep 17 00:00:00 2001 From: Luis Felipe Mileo Date: Fri, 11 Sep 2020 13:33:31 -0300 Subject: [PATCH 048/308] [REM] Dead code Signed-off-by: Luis Felipe Mileo --- .../models/account_invoice.py | 6 ++---- .../models/account_move_line.py | 19 +------------------ 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_invoice.py b/l10n_br_account_payment_brcobranca/models/account_invoice.py index ddbf2d07f221..26374906b70c 100644 --- a/l10n_br_account_payment_brcobranca/models/account_invoice.py +++ b/l10n_br_account_payment_brcobranca/models/account_invoice.py @@ -29,15 +29,13 @@ def gera_boleto_pdf(self): receivable_ids = self.mapped('move_line_receivable_ids') - boleto_list = receivable_ids.send_payment() - if not boleto_list: + boletos = receivable_ids.send_payment() + if not boletos: raise UserError( ('Não é possível gerar os boletos\n' 'Certifique-se que a fatura esteja confirmada e o ' 'forma de pagamento seja duplicatas')) - boletos = [b.boleto_cnab_api_data for b in boleto_list] - content = json.dumps(boletos) f = open(tempfile.mktemp(), 'w') f.write(content) diff --git a/l10n_br_account_payment_brcobranca/models/account_move_line.py b/l10n_br_account_payment_brcobranca/models/account_move_line.py index 2b4412be61ef..690ee0b5fbb7 100644 --- a/l10n_br_account_payment_brcobranca/models/account_move_line.py +++ b/l10n_br_account_payment_brcobranca/models/account_move_line.py @@ -13,23 +13,6 @@ _logger = logging.getLogger(__name__) -class BoletoWrapper(object): - def __init__(self, boleto_cnab_api_data): - # wrap the object - # self._wrapped_obj = obj - self.boleto_cnab_api_data = boleto_cnab_api_data - - def __getattr__(self, attr): - # see if this object has attr - # NOTE do not use hasattr, it goes into - # infinite recurrsion - if attr in self.__dict__: - # this object has it - return getattr(self, attr) - # proxy to the wrapped object - return getattr(self._wrapped_obj, attr) - - class AccountMoveLine(models.Model): _inherit = 'account.move.line' # see the list of brcobranca boleto fields: @@ -153,5 +136,5 @@ def send_payment(self): 'posto': move_line.payment_mode_id.boleto_posto, }) - wrapped_boleto_list.append(BoletoWrapper(boleto_cnab_api_data)) + wrapped_boleto_list.append(boleto_cnab_api_data) return wrapped_boleto_list From 195eb875249628db5f8c0e53263782c368586cd3 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 30 Jul 2020 16:13:26 -0300 Subject: [PATCH 049/308] [WIP] Integrate account_move_base_import module. --- .../__init__.py | 1 + .../__manifest__.py | 2 +- .../models/__init__.py | 1 + .../models/account_journal.py | 13 + .../parser/__init__.py | 6 + .../parser/cnab_file_parser.py | 702 ++++++++++++++++++ 6 files changed, 724 insertions(+), 1 deletion(-) create mode 100644 l10n_br_account_payment_brcobranca/models/account_journal.py create mode 100644 l10n_br_account_payment_brcobranca/parser/__init__.py create mode 100644 l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py diff --git a/l10n_br_account_payment_brcobranca/__init__.py b/l10n_br_account_payment_brcobranca/__init__.py index eb08a105c2b8..0a4eebe079b8 100644 --- a/l10n_br_account_payment_brcobranca/__init__.py +++ b/l10n_br_account_payment_brcobranca/__init__.py @@ -3,3 +3,4 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import models +from . import parser diff --git a/l10n_br_account_payment_brcobranca/__manifest__.py b/l10n_br_account_payment_brcobranca/__manifest__.py index 089462f36111..1408b855fd76 100644 --- a/l10n_br_account_payment_brcobranca/__manifest__.py +++ b/l10n_br_account_payment_brcobranca/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion # @author Raphaël Valyi # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -14,6 +13,7 @@ 'website': 'www.akretion.com', 'depends': [ 'l10n_br_account_payment_order', + 'account_move_base_import', ], 'data': [ 'views/account_invoice_view.xml', diff --git a/l10n_br_account_payment_brcobranca/models/__init__.py b/l10n_br_account_payment_brcobranca/models/__init__.py index 9f9a9aff0e51..7b799dc044ee 100644 --- a/l10n_br_account_payment_brcobranca/models/__init__.py +++ b/l10n_br_account_payment_brcobranca/models/__init__.py @@ -8,3 +8,4 @@ from . import l10n_br_cnab from . import res_config_settings from . import bank_payment_line +from . import account_journal diff --git a/l10n_br_account_payment_brcobranca/models/account_journal.py b/l10n_br_account_payment_brcobranca/models/account_journal.py new file mode 100644 index 000000000000..b04fd0935360 --- /dev/null +++ b/l10n_br_account_payment_brcobranca/models/account_journal.py @@ -0,0 +1,13 @@ +# @ 2020 Akretion - www.akretion.com.br - +# Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import fields, models + + +class AccountJournal(models.Model): + _inherit = "account.journal" + + import_type = fields.Selection( + selection_add=[('cnab400', 'CNAB 400'), ('cnab240', 'CNAB 240')] + ) diff --git a/l10n_br_account_payment_brcobranca/parser/__init__.py b/l10n_br_account_payment_brcobranca/parser/__init__.py new file mode 100644 index 000000000000..10be98323ffc --- /dev/null +++ b/l10n_br_account_payment_brcobranca/parser/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion +# @author Raphaël Valyi +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import cnab_file_parser diff --git a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py new file mode 100644 index 000000000000..4b0b83a8bd5a --- /dev/null +++ b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py @@ -0,0 +1,702 @@ +# @ 2020 Akretion - www.akretion.com.br - +# Magno Costa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +import base64 +import requests +import json + +import datetime +from odoo import models, fields, api, _ +from odoo.exceptions import Warning as UserError + +from odoo.addons.account_move_base_import.parser.file_parser import ( + FileParser, + float_or_zero, +) + +DICT_OCORRENCIAS_BRADESCO = { + '02': u'Entrada Confirmada (verificar motivo na posição 319 a 328)', + '03': u'Entrada Rejeitada ( verificar motivo na posição 319 a 328)', + '06': u'Liquidação normal (sem motivo)', + '09': u'Baixado Automat. via Arquivo (verificar motivo posição 319 a 328)', + '10': u'Baixado conforme instruções da Agência(' + u'verificar motivo pos.319 a 328)', + '11': u'Em Ser - Arquivo de Títulos pendentes (sem motivo)', + '12': u'Abatimento Concedido (sem motivo)', + '13': u'Abatimento Cancelado (sem motivo)', + '14': u'Vencimento Alterado (sem motivo)', + '15': u'Liquidação em Cartório (sem motivo)', + '16': u'Título Pago em Cheque – Vinculado', + '17': u'Liquidação após baixa ou Título não registrado (sem motivo)', + '18': u'Acerto de Depositária (sem motivo)', + '19': u'Confirmação Receb. Inst. de Protesto ' + u'(verificar motivo pos.295 a 295)', + '20': u'Confirmação Recebimento Instrução Sustação de' + u' Protesto (sem motivo)', + '21': u'Acerto do Controle do Participante (sem motivo)', + '22': u'Título Com Pagamento Cancelado', + '23': u'Entrada do Título em Cartório (sem motivo)', + '24': u'Entrada rejeitada por CEP Irregular' + u' (verificar motivo pos.319 a 328)', + '25': u'Confirmação Receb.Inst.de Protesto Falimentar' + u' (verificar pos.295 a 295)', + '27': u'Baixa Rejeitada (verificar motivo posição 319 a 328)', + '28': u'Débito de tarifas/custas (verificar motivo na posição 319 a 328)', + '29': u'Ocorrências do Pagador (NOVO)', + '30': u'Alteração de Outros Dados Rejeitados ' + u'(verificar motivo pos.319 a 328)', + '32': u'Instrução Rejeitada (verificar motivo posição 319 a 328)', + '33': u'Confirmação Pedido Alteração Outros Dados (sem motivo)', + '34': u'Retirado de Cartório e Manutenção Carteira (sem motivo)', + '35': u'Desagendamento do débito automático ' + u'(verificar motivos pos. 319 a 328)', + '40': u'Estorno de pagamento (NOVO)', + '55': u'Sustado judicial (NOVO)', + '68': u'Acerto dos dados do rateio de Crédito (verificar motivo posição de' + u' status do registro tipo 3)', + '69': u'Cancelamento dos dados do rateio (verificar motivo posição de' + u' status do registro tipo 3)', + '073': u'Confirmação Receb. Pedido de Negativação (NOVO)', + '074': u'Confir Pedido de Excl de Negat (com ou sem baixa) (NOVO)', + '00': u'Nota: Para as ocorrências sem motivos, as posições serão' + u' informadas com Zeros.', +} + +DICT_OCORRENCIAS_ITAU = { + '02': u'ENTRADA CONFIRMADA COM POSSIBILIDADE DE MENSAGEM' + u' (NOTA 20 – TABELA 10)', + '03': u'ENTRADA REJEITADA (NOTA 20 – TABELA 1)', + '04': u'ALTERAÇÃO DE DADOS – NOVA ENTRADA OU ALTERAÇÃO/EXCLUSÃO' + u' DE DADOS ACATADA', + '05': u'ALTERAÇÃO DE DADOS – BAIXA', + '06': u'LIQUIDAÇÃO NORMAL', + '07': u'LIQUIDAÇÃO PARCIAL – COBRANÇA INTELIGENTE (B2B)', + '08': u'LIQUIDAÇÃO EM CARTÓRIO', + '09': u'BAIXA SIMPLES', + '10': u'BAIXA POR TER SIDO LIQUIDADO', + '11': u'EM SER (SÓ NO RETORNO MENSAL)', + '12': u'ABATIMENTO CONCEDIDO', + '13': u'ABATIMENTO CANCELADO', + '14': u'VENCIMENTO ALTERADO', + '15': u'BAIXAS REJEITADAS (NOTA 20 – TABELA 4)', + '16': u'INSTRUÇÕES REJEITADAS (NOTA 20 – TABELA 3)', + '17': u'ALTERAÇÃO/EXCLUSÃO DE DADOS REJEITADOS (NOTA 20 – TABELA 2)', + '18': u'COBRANÇA CONTRATUAL – INSTRUÇÕES/ALTERAÇÕES' + u' REJEITADAS/PENDENTES (NOTA 20 – TABELA 5)', + '19': u'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE PROTESTO', + '20': u'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE SUSTAÇÃO' + u' DE PROTESTO /TARIFA', + '21': u'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE NÃO PROTESTAR', + '23': u'TÍTULO ENVIADO A CARTÓRIO/TARIFA', + '24': u'INSTRUÇÃO DE PROTESTO REJEITADA / SUSTADA / PENDENTE' + u' (NOTA 20 – TABELA 7)', + '25': u'ALEGAÇÕES DO PAGADOR (NOTA 20 – TABELA 6)', + '26': u'TARIFA DE AVISO DE COBRANÇA', + '27': u'TARIFA DE EXTRATO POSIÇÃO (B40X)', + '28': u'TARIFA DE RELAÇÃO DAS LIQUIDAÇÕES', + '29': u'TARIFA DE MANUTENÇÃO DE TÍTULOS VENCIDOS', + '30': u'DÉBITO MENSAL DE TARIFAS (PARA ENTRADAS E BAIXAS)', + '32': u'BAIXA POR TER SIDO PROTESTADO', + '33': u'CUSTAS DE PROTESTO', + '34': u'CUSTAS DE SUSTAÇÃO', + '35': u'CUSTAS DE CARTÓRIO DISTRIBUIDOR', + '36': u'CUSTAS DE EDITAL', + '37': u'TARIFA DE EMISSÃO DE BOLETO/TARIFA DE ENVIO DE DUPLICATA', + '38': u'TARIFA DE INSTRUÇÃO', + '39': u'TARIFA DE OCORRÊNCIAS', + '40': u'TARIFA MENSAL DE EMISSÃO DE BOLETO/TARIFA MENSAL' + u' DE ENVIO DE DUPLICATA', + '41': u'DÉBITO MENSAL DE TARIFAS – EXTRATO DE POSIÇÃO (B4EP/B4OX)', + '42': u'DÉBITO MENSAL DE TARIFAS – OUTRAS INSTRUÇÕES', + '43': u'DÉBITO MENSAL DE TARIFAS – MANUTENÇÃO DE TÍTULOS VENCIDOS', + '44': u'DÉBITO MENSAL DE TARIFAS – OUTRAS OCORRÊNCIAS', + '45': u'DÉBITO MENSAL DE TARIFAS – PROTESTO', + '46': u'DÉBITO MENSAL DE TARIFAS – SUSTAÇÃO DE PROTESTO', + '47': u'BAIXA COM TRANSFERÊNCIA PARA DESCONTO', + '48': u'CUSTAS DE SUSTAÇÃO JUDICIAL', + '51': u'TARIFA MENSAL REF A ENTRADAS BANCOS CORRESPONDENTES NA CARTEIRA', + '52': u'TARIFA MENSAL BAIXAS NA CARTEIRA', + '53': u'TARIFA MENSAL BAIXAS EM BANCOS CORRESPONDENTES NA CARTEIRA', + '54': u'TARIFA MENSAL DE LIQUIDAÇÕES NA CARTEIRA', + '55': u'TARIFA MENSAL DE LIQUIDAÇÕES EM BANCOS' + u' CORRESPONDENTES NA CARTEIRA', + '56': u'CUSTAS DE IRREGULARIDADE', + '57': u'INSTRUÇÃO CANCELADA (NOTA 20 – TABELA 8)', + '59': u'BAIXA POR CRÉDITO EM C/C ATRAVÉS DO SISPAG', + '60': u'ENTRADA REJEITADA CARNÊ (NOTA 20 – TABELA 1)', + '61': u'TARIFA EMISSÃO AVISO DE MOVIMENTAÇÃO DE TÍTULOS (2154)', + '62': u'DÉBITO MENSAL DE TARIFA – AVISO DE MOVIMENTAÇÃO DE TÍTULOS (2154)', + '63': u'TÍTULO SUSTADO JUDICIALMENTE', + '64': u'ENTRADA CONFIRMADA COM RATEIO DE CRÉDITO', + '65': u'PAGAMENTO COM CHEQUE – AGUARDANDO COMPENSAÇÃO', + '69': u'CHEQUE DEVOLVIDO (NOTA 20 – TABELA 9)', + '71': u'ENTRADA REGISTRADA, AGUARDANDO AVALIAÇÃO', + '72': u'BAIXA POR CRÉDITO EM C/C ATRAVÉS DO SISPAG' + u' SEM TÍTULO CORRESPONDENTE', + '73': u'CONFIRMAÇÃO DE ENTRADA NA COBRANÇA SIMPLES –' + u' ENTRADA NÃO ACEITA NA COBRANÇA CONTRATUAL', + '74': u'INSTRUÇÃO DE NEGATIVAÇÃO EXPRESSA REJEITADA (NOTA 20 – TABELA 11)', + '75': u'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE ENTRADA' + u' EM NEGATIVAÇÃO EXPRESSA', + '76': u'CHEQUE COMPENSADO', + '77': u'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE EXCLUSÃO DE' + u' ENTRADA EM NEGATIVAÇÃO EXPRESSA', + '78': u'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE CANCELAMENTO DE' + u' NEGATIVAÇÃO EXPRESSA', + '79': u'NEGATIVAÇÃO EXPRESSA INFORMACIONAL (NOTA 20 – TABELA 12)', + '80': u'CONFIRMAÇÃO DE ENTRADA EM NEGATIVAÇÃO EXPRESSA – TARIFA', + '82': u'CONFIRMAÇÃO DO CANCELAMENTO DE NEGATIVAÇÃO EXPRESSA – TARIFA', + '83': u'CONFIRMAÇÃO DE EXCLUSÃO DE ENTRADA EM NEGATIVAÇÃO' + u' EXPRESSA POR LIQUIDAÇÃO – TARIFA', + '85': u'TARIFA POR BOLETO (ATÉ 03 ENVIOS) COBRANÇA ATIVA ELETRÔNICA', + '86': u'TARIFA EMAIL COBRANÇA ATIVA ELETRÔNICA', + '87': u'TARIFA SMS COBRANÇA ATIVA ELETRÔNICA', + '88': u'TARIFA MENSAL POR BOLETO (ATÉ 03 ENVIOS)' + u' COBRANÇA ATIVA ELETRÔNICA', + '89': u'TARIFA MENSAL EMAIL COBRANÇA ATIVA ELETRÔNICA', + '90': u'TARIFA MENSAL SMS COBRANÇA ATIVA ELETRÔNICA', + '91': u'TARIFA MENSAL DE EXCLUSÃO DE ENTRADA DE NEGATIVAÇÃO EXPRESSA', + '92': u'TARIFA MENSAL DE CANCELAMENTO DE NEGATIVAÇÃO EXPRESSA', + '93': u'TARIFA MENSAL DE EXCLUSÃO DE NEGATIVAÇÃO EXPRESSA POR LIQUIDAÇÃO', +} + +DICT_OCORRENCIAS_UNICRED = { + '01': '01 - Pago (Título protestado pago em cartório)', + '02': '02 - Instrução Confirmada*', + '03': '03 - Instrução Rejeitada*', + '04': '04 - Sustado Judicial (Título protestado sustado judicialmente)', + '06': '06 - Liquidação Normal *', + '07': '07 - Liquidação em Condicional (Título liquidado em cartório com' + ' cheque do próprio devedor)', + '08': '08 - Sustado Definitivo (Título protestado sustado judicialmente)', + '09': '09 - Liquidação de Título Descontado', + '10': '10 - Protesto solicitado', + '11': '11 - Protesto Em cartório', + '12': '12 - Sustação solicitada' +} + +dict_brcobranca_bank = { + '001': 'banco_brasil', + '041': 'banrisul', + '237': 'bradesco', + '104': 'caixa', + '399': 'hsbc', + '341': 'itau', + '033': 'santander', + '748': 'sicred', + '004': 'banco_nordeste', + '021': 'banestes', + '756': 'sicoob', + '136': 'unicred', +} + +dict_brcobranca_cnab_type = { + '240': 'cnab240', + '400': 'cnab400', +} + + +class CNABFileParser(FileParser): + """CNAB parser that use a define format in CNAB to import + bank statement. + """ + + def __init__(self, journal, *args, **kwargs): + # The name of the parser as it will be called + self.parser_name = journal.import_type + # The result as a list of row. One row per line of data in the file, + # but not the commission one! + self.result_row_list = None + # The file buffer on which to work on + self.filebuffer = None + # The profile record to access its parameters in any parser method + self.journal = journal + self.move_date = None + self.move_name = None + self.move_ref = None + self.support_multi_moves = None + self.env = journal.env + self.bank = self.journal.bank_account_id.bank_id + + @classmethod + def parser_for(cls, parser_name): + if parser_name == "cnab400": + return parser_name == "cnab400" + elif parser_name == "cnab240": + return parser_name == "cnab240" + + def parse(self, filebuffer): + + files = {'data': base64.b64decode(filebuffer)} + + api_address = self.env[ + "ir.config_parameter"].sudo().get_param( + "l10n_br_account_payment_brcobranca.boleto_cnab_api") + if not api_address: + raise UserError( + ('Não é possível gerar o retorno.\n' + 'Informe o Endereço IP ou Nome do' + ' Boleto CNAB API.')) + # Ex.: "http://boleto_cnab_api:9292/api/retorno" + bank_name_brcobranca = \ + dict_brcobranca_bank[self.bank.code_bc] + api_service_address = \ + 'http://' + api_address + ':9292/api/retorno' + res = requests.post( + api_service_address, + data={ + 'type': self.journal.import_type, + 'bank': bank_name_brcobranca, + }, files=files) + + if res.status_code != 201: + raise UserError(res.text) + + string_result = res.json() + data = json.loads(string_result) + + self.result_row_list = self.processar_arquivo_retorno_cnab400(data) + + yield self.result_row_list + + @api.multi + def processar_arquivo_retorno_cnab400(self, data): + + quantidade_registros = 0 + total_valores = 0 + balance_end_real = 0.0 + line_statement_vals = [] + reconcile_statement_vals = [] + + # Lista com os dados q poderão ser usados na criação das account move line + result_row_list = [] + + for linha_cnab in data: + cnab = self.env['l10n_br.cnab'].create(vals={}) + lote_id = self.env['l10n_br.cnab.lote'].create({'cnab_id': cnab.id}) + + if int(linha_cnab['codigo_registro']) != 1: + # Bradesco + # Existe o codigo de registro 9 que eh um totalizador + # porem os campos estao colocados em outras posicoes + # que nao estao mapeadas no BRCobranca + # Itau + # 9 - Registro Trailer do Arquivo + # 4 e 5 - Registro de Detalhe (Opcional) + # continue + continue + + bank_name_brcobranca = \ + dict_brcobranca_bank[self.bank.code_bc] + + # Nosso Numero sem o Digito Verificador + if bank_name_brcobranca == 'bradesco': + account_move_line = self.env['account.move.line'].search( + [('nosso_numero', '=', linha_cnab['nosso_numero'][:11])] + ) + elif bank_name_brcobranca == 'unicred': + account_move_line = self.env['account.move.line'].search( + [('nosso_numero', '=', linha_cnab['nosso_numero'][6:16])] + ) + + payment_line = self.env['account.payment.line'].search( + [('move_line_id', '=', account_move_line.id)] + ) + + valor_titulo = float( + str(linha_cnab['valor_titulo'][0:11] + '.' + + linha_cnab['valor_titulo'][11:])) + + total_valores += valor_titulo + + data_ocorrencia = datetime.date.today() + cod_ocorrencia = str(linha_cnab['codigo_ocorrencia']) + # Cada Banco pode possuir um Codigo de Ocorrencia distinto + if bank_name_brcobranca == 'bradesco': + if (linha_cnab['data_ocorrencia'] == '000000' or + not linha_cnab['data_ocorrencia']): + data_ocorrencia = linha_cnab['data_de_ocorrencia'] + else: + data_ocorrencia = datetime.datetime.strptime( + str(linha_cnab['data_ocorrencia']), "%d%m%y").date() + descricao_ocorrencia = DICT_OCORRENCIAS_BRADESCO[ + cod_ocorrencia].encode('utf-8') + + if bank_name_brcobranca == 'itau': + descricao_ocorrencia = DICT_OCORRENCIAS_ITAU[ + cod_ocorrencia].encode('utf-8') + + if bank_name_brcobranca == 'unicred': + descricao_ocorrencia = DICT_OCORRENCIAS_UNICRED[ + cod_ocorrencia].encode('utf-8') + + # Linha não encontrada + if not account_move_line: + vals_evento = { + 'lote_id': lote_id.id, + 'ocorrencias': descricao_ocorrencia, + 'data_ocorrencia': data_ocorrencia, + 'str_motiv_a': + u' * - BOLETO NÃO ENCONTRADO.', + 'nosso_numero': linha_cnab['nosso_numero'], + 'seu_numero': linha_cnab['documento_numero'], + 'valor': valor_titulo, + } + self.env['l10n_br.cnab.evento'].create(vals_evento) + continue + + valor_recebido = 0.0 + if linha_cnab['valor_recebido']: + valor_recebido = self.cnab_str_to_float( + linha_cnab['valor_recebido']) + + if (linha_cnab['data_credito'] == '000000' or + not linha_cnab['data_credito']): + data_credito = linha_cnab['data_credito'] + else: + data_credito = datetime.datetime.strptime( + str(linha_cnab['data_credito']), "%d%m%y").date() + + # Codigos de Ocorrencia - Liquidação + if ((cod_ocorrencia in ('06', '17') and bank_name_brcobranca == 'bradesco') or + (cod_ocorrencia in ('06', '10') and bank_name_brcobranca == 'itau') or + (cod_ocorrencia in ('01', '06', '07', '09') and + bank_name_brcobranca == 'unicred')): + + # TODO - deve ser uma hash p/ evitar + # erros com mais de boleto cadastrado + reconcile_line_vals = { + 'numero_documento': account_move_line.numero_documento} + counterpart_line_vals = [] + new_aml_vals = [] + + # Valor Desconto + if linha_cnab.get('desconto'): + valor_desconto = self.cnab_str_to_float( + linha_cnab['desconto']) + + # TODO - Atualizar o Valor Recebido ou isso + # ja vem informado ? Confirmar no arquivo de retorno + # valor_recebido -= valor_desconto + + new_aml_vals.append({ + 'name': 'Desconto (boleto) ' + account_move_line.numero_documento, + 'debit': valor_desconto, + 'credit': 0.0, + # TODO - Definir Conta Contabil de Desconto + 'account_id': account_move_line.payment_mode_id. + default_tax_account_id.id, + }) + + result_row_list.append({ + 'name': 'Desconto (boleto) ' + account_move_line.numero_documento, + 'amount': valor_desconto, + 'debit': valor_desconto, + 'credit': 0.0, + # TODO - Definir Conta Contabil de Desconto + 'account_id': account_move_line.payment_mode_id. + default_tax_account_id.id, + 'type': 'desconto', + 'bank_payment_line_id': + payment_line.bank_line_id.id or False, + }) + + # Valor Juros Mora - valor de mora e multa pagos pelo sacado + if linha_cnab.get('juros_mora'): + valor_juros_mora = self.cnab_str_to_float( + linha_cnab['juros_mora']) + + # TODO - Atualizar o Valor Recebido ou isso + # ja vem informado ? Confirmar no arquivo de retorno + # valor_recebido += valor_juros_mora + + # TODO - Deveria ser tbm um counterpart ? E ser criado + # um outro lançamento para ser conciliado com esse + result_row_list.append({ + 'name': 'Valor Juros Mora (boleto) ' + account_move_line.numero_documento, + 'debit': 0.0, + 'credit': valor_juros_mora, + 'amount': valor_juros_mora, + # TODO - Definir Conta Contábil de Juros Mora e Multa + 'account_id': account_move_line.payment_mode_id. + fixed_journal_id.default_credit_account_id.id, + 'type': 'juros_mora', + 'bank_payment_line_id': + payment_line.bank_line_id.id or False, + }) + new_aml_vals.append({ + 'name': 'Valor Juros Mora (boleto)' + account_move_line.numero_documento, + 'debit': 0.0, + 'credit': valor_juros_mora, + # TODO - Definir Conta Contábil de Juros Mora e Multa + 'account_id': account_move_line.payment_mode_id. + fixed_journal_id.default_credit_account_id.id, + }) + + # Valor Tarifa + if linha_cnab.get('valor_tarifa'): + valor_tarifa = float( + str(linha_cnab['valor_tarifa'][0:4] + '.' + + linha_cnab['valor_tarifa'][4:])) + + # TODO - Atualizar o Valor Recebido ou isso + # ja vem informado ? Confirmar no arquivo de retorno + # valor_recebido -= valor_tarifa + + result_row_list.append({ + 'name': 'Tarifas bancárias (boleto)' + account_move_line.numero_documento, + 'debit': valor_tarifa, + 'credit': 0.0, + 'amount': valor_tarifa, + 'account_id': account_move_line.payment_mode_id. + default_tax_account_id.id, + 'type': 'tarifa', + 'bank_payment_line_id': + payment_line.bank_line_id.id or False, + }) + + new_aml_vals.append({ + 'name': 'Tarifas bancárias (boleto)' + account_move_line.numero_documento, + 'debit': valor_tarifa, + 'credit': 0.0, + 'account_id': account_move_line.payment_mode_id. + default_tax_account_id.id, + }) + + # Valor Abatimento + if linha_cnab.get('valor_abatimento'): + valor_abatimento = self.cnab_str_to_float( + linha_cnab['valor_abatimento']) + + # TODO - Atualizar o Valor Recebido ou isso + # ja vem informado ? Confirmar no arquivo de retorno + # valor_recebido -= valor_abatimento + + result_row_list.append({ + 'name': 'Abatimento (boleto) ' + account_move_line.numero_documento, + 'debit': valor_abatimento, + 'credit': 0.0, + 'amount': valor_abatimento, + # TODO - Definir Conta Contabil de Abatimento + 'account_id': account_move_line.payment_mode_id. + default_tax_account_id.id, + 'type': 'abatimento', + 'bank_payment_line_id': + payment_line.bank_line_id.id or False, + }) + + new_aml_vals.append({ + 'name': 'Abatimento (boleto) ' + account_move_line.numero_documento, + 'debit': valor_abatimento, + 'credit': 0.0, + # TODO - Definir Conta Contabil de Abatimento + 'account_id': account_move_line.payment_mode_id. + default_tax_account_id.id, + }) + + vals_evento = { + 'lote_id': lote_id.id, + 'data_ocorrencia': data_ocorrencia, + 'data_real_pagamento': data_credito.strftime("%Y-%m-%d"), + # 'segmento': evento.servico_segmento, + # 'favorecido_nome': + # obj_account_move_line.company_id.partner_id.name, + 'favorecido_conta_bancaria_id': + account_move_line.payment_mode_id. + fixed_journal_id.bank_account_id.id, + 'nosso_numero': linha_cnab['nosso_numero'], + 'identificacao_titulo_empresa': + linha_cnab['documento_numero'] or + account_move_line.numero_documento, + # 'tipo_moeda': evento.credito_moeda_tipo, + 'valor': valor_titulo, + 'valor_pagamento': valor_recebido, + 'ocorrencias': descricao_ocorrencia, + 'bank_payment_line_id': + payment_line.bank_line_id.id or False, + 'invoice_id': account_move_line.invoice_id.id, + 'data_vencimento': datetime.datetime.strptime( + str(linha_cnab['data_vencimento']), "%d%m%y").date(), + 'valor_desconto': valor_desconto, + 'juros_mora_multa': valor_juros_mora, + 'valor_abatimento': valor_abatimento, + 'tarifa_cobranca': valor_tarifa, + } + + # Monta o dicionario que sera usado + # para criar o Extrato Bancario + balance_end_real += valor_recebido + line_statement_vals.append({ + 'name': account_move_line.numero_documento or '?', + 'amount': valor_recebido, + 'partner_id': account_move_line.partner_id.id, + 'ref': account_move_line.ref, + 'date': account_move_line.date, + 'amount_currency': valor_recebido, + 'currency_id': account_move_line.currency_id.id, + }) + + # Linha da Fatura a ser reconciliada + result_row_list.append({ + 'name': account_move_line.numero_documento, + 'debit': 0.0, + 'credit': valor_recebido, + 'amount': valor_recebido, + 'move_line': account_move_line, + 'type': 'liquidado', + 'bank_payment_line_id': + payment_line.bank_line_id.id or False, + }) + + counterpart_line_vals.append({ + 'name': account_move_line.numero_documento, + 'debit': 0.0, + 'credit': valor_recebido, + 'move_line': account_move_line, + }) + + # Monta uma Lista com Dicionarios usados para + # reconciliar o extrato bancario + reconcile_line_vals['new_aml_vals'] = new_aml_vals + reconcile_line_vals['counterpart_aml_vals'] = counterpart_line_vals + reconcile_statement_vals.append(reconcile_line_vals) + + else: + vals_evento = { + 'lote_id': lote_id.id, + 'ocorrencias': descricao_ocorrencia, + 'data_ocorrencia': data_ocorrencia, + 'nosso_numero': linha_cnab['nosso_numero'], + 'seu_numero': account_move_line.numero_documento, + 'valor': valor_titulo, + } + + self.env['l10n_br.cnab.evento'].create(vals_evento) + + lote_id.total_valores = total_valores + lote_id.qtd_registros = quantidade_registros + self.num_lotes = 1 + self.num_eventos = quantidade_registros + + # Criacao de um Extrato Bancario a ser conciliado logo abaixo, sendo + # necessário o usuario apenas clicar em Validar na tela do Extrato. + # TODO - automatizar validando o extrato ou deixar de usar o extrato ? + if line_statement_vals: + vals_bank_statement = { + # TODO - Validar usar a sequencia do diário no nome do Extrato + # estaria correto ? + 'name': self.journal.sequence_id.next_by_id(), + 'journal_id': self.journal.id, + 'balance_end_real': balance_end_real, + } + statement = self.env[ + 'account.bank.statement'].create(vals_bank_statement) + statement_line_obj = self.env['account.bank.statement.line'] + for line in line_statement_vals: + line['statement_id'] = statement.id + statement_line_obj.create(line) + + # Reconciliação do Extrato e Fatura + # for line in statement.line_ids: + # for reconcile_line in reconcile_statement_vals: + # if line.name == reconcile_line.get('numero_documento'): + # line.process_reconciliation( + # counterpart_aml_dicts=reconcile_line.get( + # 'counterpart_aml_vals'), + # new_aml_dicts=reconcile_line.get( + # 'new_aml_vals') + # ) + + return result_row_list + + def cnab_str_to_float(self, value): + if len(value) == 13: + value_float = float( + str(value[0:11] + '.' + value[11:])) + return value_float + + def get_move_vals(self): + """This method return a dict of vals that ca be passed to create method + of statement. + :return: dict of vals that represent additional infos for the statement + """ + return { + 'name': self.move_name or '/', + 'date': self.move_date or fields.Datetime.now(), + 'ref': self.move_ref or '/' + } + + def get_move_line_vals(self, line, *args, **kwargs): + """This method must return a dict of vals that can be passed to create + method of statement line in order to record it. It is the + responsibility of every parser to give this dict of vals, so each one + can implement his own way of recording the lines. + :param: line: a dict of vals that represent a line of + result_row_list + :return: dict of values to give to the create method of statement + line, it MUST contain at least: + { + 'name':value, + 'date':value, + 'amount':value, + 'ref':value, + 'label':value, + 'commission_amount':value, + } + """ + + vals = { + "name": line["name"] or line.get("source"), + # "date_maturity": date.fromtimestamp(line["created"]), + "credit": line['credit'], + "debit": line['debit'], + "ref": line.get("source"), + "already_completed": False, + "partner_id": None, + "account_id": None, + # 'bank_payment_line_id': line['bank_payment_line_id'], + } + if line["type"] == 'liquidada': + vals.update( + { + #"partner_id": self.journal.partner_id.id, + 'move_line': line['move_line'], + "account_id": line['account_id'], + "already_completed": True, + } + ) + elif line["type"] == "desconto": + vals.update( + { + #"partner_id": self.journal.partner_id.id, + "account_id": line['account_id'], + # "already_completed": True, + } + ) + elif line["type"] == "tarifa": + vals.update( + { + #"partner_id": self.journal.partner_id.id, + "account_id": line['account_id'], + #"already_completed": True, + } + ) + elif line["type"] == "abatimento": + vals.update( + { + #"partner_id": self.journal.partner_id.id, + "account_id": line['account_id'], + #"already_completed": True, + } + ) + elif line["type"] == "juros_mora": + vals.update( + { + #"partner_id": self.journal.partner_id.id, + "account_id": line['account_id'], + #"already_completed": True, + } + ) + + return vals From 9fd09e193cdc3dee4d71f04b5731274f831f2468 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Tue, 11 Aug 2020 17:10:19 -0300 Subject: [PATCH 050/308] [WIP] Configure Manual and Automatic return CNAB methods. --- .../data/res_config_settings_data.xml | 4 + .../models/account_journal.py | 66 +++- .../models/res_config_settings.py | 18 +- .../parser/cnab_file_parser.py | 321 ++++++++---------- .../views/res_config_settings_view.xml | 9 + 5 files changed, 236 insertions(+), 182 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml b/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml index 1c93af57ec2a..b6b7816aa073 100644 --- a/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml +++ b/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml @@ -10,4 +10,8 @@ search="[('id', '=', ref('boleto_cnab_api_config_settings'))]"/> + + manual + + \ No newline at end of file diff --git a/l10n_br_account_payment_brcobranca/models/account_journal.py b/l10n_br_account_payment_brcobranca/models/account_journal.py index b04fd0935360..262f228a6ace 100644 --- a/l10n_br_account_payment_brcobranca/models/account_journal.py +++ b/l10n_br_account_payment_brcobranca/models/account_journal.py @@ -2,7 +2,7 @@ # Magno Costa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -from odoo import fields, models +from odoo import fields, models, api, _ class AccountJournal(models.Model): @@ -11,3 +11,67 @@ class AccountJournal(models.Model): import_type = fields.Selection( selection_add=[('cnab400', 'CNAB 400'), ('cnab240', 'CNAB 240')] ) + + @api.multi + def _write_extra_move_lines(self, parser, move): + """Insert extra lines after the main statement lines. + + After the main statement lines have been created, you can override this + method to create extra statement lines. + + :param: browse_record of the current parser + :param: result_row_list: [{'key':value}] + :param: profile: browserecord of account.statement.profile + :param: statement_id: int/long of the current importing + statement ID + :param: context: global context + """ + + move_line_obj = self.env["account.move.line"] + + cnab_return_method = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'l10n_br_account_payment_brcobranca.cnab_return_method') + + if cnab_return_method == 'automatic': + # TODO - os outros valores deveriam ser conciliados ? + # Porque para isso deverão estar na mesma Conta Contabil + + # Conciliação entre a Linha da Fatura e a Linha criada + for line in move.line_ids: + # Pesquisando pelo Nosso Numero e Invoice evito o problema + # de existirem move lines com o mesmo valor no campo + # nosso numero, que pode acontecer qdo existem mais de um banco + # configurado para gerar Boletos + line_to_reconcile = move_line_obj.search([ + ('nosso_numero', '=', line.ref), + ('invoice_id', '=', line.invoice_id.id) + ]) + + if line_to_reconcile: + (line + line_to_reconcile).reconcile() + + def _move_import( + self, parser, file_stream, result_row_list=None, ftype="csv"): + """Create a bank statement with the given profile and parser. It will + fulfill the bank statement with the values of the file provided, but + will not complete data (like finding the partner, or the right + account). This will be done in a second step with the completion rules. + + :param prof : The profile used to import the file + :param parser: the parser + :param filebuffer file_stream: binary of the provided file + :param char: ftype represent the file extension (csv by default) + :return: ID of the created account.bank.statement + """ + move = super()._move_import( + parser, file_stream, result_row_list=None, ftype="csv") + + cnab_return_method = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'l10n_br_account_payment_brcobranca.cnab_return_method') + + if cnab_return_method == 'automatic': + move.post() + + return move diff --git a/l10n_br_account_payment_brcobranca/models/res_config_settings.py b/l10n_br_account_payment_brcobranca/models/res_config_settings.py index 79710ee36961..1734278e5b4a 100644 --- a/l10n_br_account_payment_brcobranca/models/res_config_settings.py +++ b/l10n_br_account_payment_brcobranca/models/res_config_settings.py @@ -14,13 +14,22 @@ class ResConfigSettings(models.TransientModel): default='boleto_cnab_api' ) + cnab_return_method = fields.Selection( + [('manual', 'Manual'), + ('automatic', 'Automatico')], 'CNAB Return method', + default='manual', + ) + @api.model def get_values(self): res = super(ResConfigSettings, self).get_values() res.update( boleto_cnab_api=self.env[ 'ir.config_parameter'].sudo().get_param( - 'l10n_br_account_payment_brcobranca.boleto_cnab_api') + 'l10n_br_account_payment_brcobranca.boleto_cnab_api'), + cnab_return_method=self.env[ + 'ir.config_parameter'].sudo().get_param( + 'l10n_br_account_payment_brcobranca.cnab_return_method') ) return res @@ -35,3 +44,10 @@ def set_values(self): param.set_param( 'l10n_br_account_payment_brcobranca.boleto_cnab_api', boleto_cnab_api) + + cnab_return_method =\ + self.cnab_return_method or 'manual' + + param.set_param( + 'l10n_br_account_payment_brcobranca.cnab_return_method', + cnab_return_method) diff --git a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py index 4b0b83a8bd5a..30621304d926 100644 --- a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py +++ b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py @@ -191,11 +191,6 @@ '136': 'unicred', } -dict_brcobranca_cnab_type = { - '240': 'cnab240', - '400': 'cnab400', -} - class CNABFileParser(FileParser): """CNAB parser that use a define format in CNAB to import @@ -267,7 +262,19 @@ def processar_arquivo_retorno_cnab400(self, data): total_valores = 0 balance_end_real = 0.0 line_statement_vals = [] - reconcile_statement_vals = [] + + # Forma de Lançamento do Retorno + # Manual - Criação de uma Entrada de Diário com os valores de + # desconto, juros/mora, tarifa bancaria e abatimento + # e um Extrato Bancario com o valor total ( valor + # liquido + desconto + tarifa bancaria + abatimanto ) + # Automatico - Criação de uma Entrada de Diário com os valores + # da forma Manual mais o valor total, conciliado com + # a Fatura correspondente + # + cnab_return_method = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'l10n_br_account_payment_brcobranca.cnab_return_method') # Lista com os dados q poderão ser usados na criação das account move line result_row_list = [] @@ -304,9 +311,8 @@ def processar_arquivo_retorno_cnab400(self, data): [('move_line_id', '=', account_move_line.id)] ) - valor_titulo = float( - str(linha_cnab['valor_titulo'][0:11] + '.' + - linha_cnab['valor_titulo'][11:])) + valor_titulo = self.cnab_str_to_float( + linha_cnab['valor_titulo']) total_valores += valor_titulo @@ -359,47 +365,44 @@ def processar_arquivo_retorno_cnab400(self, data): str(linha_cnab['data_credito']), "%d%m%y").date() # Codigos de Ocorrencia - Liquidação - if ((cod_ocorrencia in ('06', '17') and bank_name_brcobranca == 'bradesco') or - (cod_ocorrencia in ('06', '10') and bank_name_brcobranca == 'itau') or + # TODO - esses codigos deveriam ser informados em um campo ao inves + # de estarem chumbados aqui + if ((cod_ocorrencia in ('06', '17') and + bank_name_brcobranca == 'bradesco') or + (cod_ocorrencia in ('06', '10') and + bank_name_brcobranca == 'itau') or (cod_ocorrencia in ('01', '06', '07', '09') and bank_name_brcobranca == 'unicred')): - # TODO - deve ser uma hash p/ evitar - # erros com mais de boleto cadastrado - reconcile_line_vals = { - 'numero_documento': account_move_line.numero_documento} - counterpart_line_vals = [] - new_aml_vals = [] - # Valor Desconto if linha_cnab.get('desconto'): valor_desconto = self.cnab_str_to_float( linha_cnab['desconto']) - # TODO - Atualizar o Valor Recebido ou isso - # ja vem informado ? Confirmar no arquivo de retorno - # valor_recebido -= valor_desconto + # Valor Liquido precisa ser somado + # p/ conciliar com a Fatura + valor_recebido += valor_desconto - new_aml_vals.append({ - 'name': 'Desconto (boleto) ' + account_move_line.numero_documento, + result_row_list.append({ + 'name': 'Desconto (boleto) ' + + account_move_line.numero_documento, + 'amount': valor_desconto, 'debit': valor_desconto, 'credit': 0.0, - # TODO - Definir Conta Contabil de Desconto - 'account_id': account_move_line.payment_mode_id. - default_tax_account_id.id, + 'account_id': self.journal.default_debit_account_id.id, + 'type': 'desconto', + 'ref': account_move_line.numero_documento, }) - result_row_list.append({ - 'name': 'Desconto (boleto) ' + account_move_line.numero_documento, + 'name': 'Desconto (boleto) ' + + account_move_line.numero_documento, 'amount': valor_desconto, - 'debit': valor_desconto, - 'credit': 0.0, - # TODO - Definir Conta Contabil de Desconto + 'debit': 0.0, + 'credit': valor_desconto, 'account_id': account_move_line.payment_mode_id. - default_tax_account_id.id, + default_discount_account_id.id, 'type': 'desconto', - 'bank_payment_line_id': - payment_line.bank_line_id.id or False, + 'ref': account_move_line.numero_documento, }) # Valor Juros Mora - valor de mora e multa pagos pelo sacado @@ -407,31 +410,33 @@ def processar_arquivo_retorno_cnab400(self, data): valor_juros_mora = self.cnab_str_to_float( linha_cnab['juros_mora']) - # TODO - Atualizar o Valor Recebido ou isso - # ja vem informado ? Confirmar no arquivo de retorno + # O valor não está sendo somando pois isso + # altera o Valor recebido desbalanceando a + # conciliação com a Fatura # valor_recebido += valor_juros_mora - # TODO - Deveria ser tbm um counterpart ? E ser criado - # um outro lançamento para ser conciliado com esse result_row_list.append({ - 'name': 'Valor Juros Mora (boleto) ' + account_move_line.numero_documento, + 'name': 'Valor Juros Mora (boleto) ' + + account_move_line.numero_documento, 'debit': 0.0, 'credit': valor_juros_mora, 'amount': valor_juros_mora, - # TODO - Definir Conta Contábil de Juros Mora e Multa 'account_id': account_move_line.payment_mode_id. - fixed_journal_id.default_credit_account_id.id, + default_juros_mora_account_id.id, 'type': 'juros_mora', - 'bank_payment_line_id': - payment_line.bank_line_id.id or False, + 'ref': account_move_line.numero_documento, + 'partner_id': account_move_line.partner_id.id, }) - new_aml_vals.append({ - 'name': 'Valor Juros Mora (boleto)' + account_move_line.numero_documento, - 'debit': 0.0, - 'credit': valor_juros_mora, - # TODO - Definir Conta Contábil de Juros Mora e Multa - 'account_id': account_move_line.payment_mode_id. - fixed_journal_id.default_credit_account_id.id, + + result_row_list.append({ + 'name': 'Valor Juros Mora (boleto) ' + + account_move_line.numero_documento, + 'debit': valor_juros_mora, + 'credit': 0.0, + 'amount': valor_juros_mora, + 'account_id': self.journal.default_debit_account_id.id, + 'type': 'juros_mora', + 'ref': account_move_line.numero_documento, }) # Valor Tarifa @@ -440,28 +445,31 @@ def processar_arquivo_retorno_cnab400(self, data): str(linha_cnab['valor_tarifa'][0:4] + '.' + linha_cnab['valor_tarifa'][4:])) - # TODO - Atualizar o Valor Recebido ou isso - # ja vem informado ? Confirmar no arquivo de retorno - # valor_recebido -= valor_tarifa + # Valor Liquido precisa ser somado + # p/ conciliar com a Fatura + valor_recebido += valor_tarifa result_row_list.append({ - 'name': 'Tarifas bancárias (boleto)' + account_move_line.numero_documento, - 'debit': valor_tarifa, - 'credit': 0.0, + 'name': 'Tarifas bancárias (boleto) ' + + account_move_line.numero_documento, + 'debit': 0.0, + 'credit': valor_tarifa, 'amount': valor_tarifa, 'account_id': account_move_line.payment_mode_id. - default_tax_account_id.id, + default_tax_account_id.id, 'type': 'tarifa', - 'bank_payment_line_id': - payment_line.bank_line_id.id or False, + 'ref': account_move_line.numero_documento, }) - new_aml_vals.append({ - 'name': 'Tarifas bancárias (boleto)' + account_move_line.numero_documento, + result_row_list.append({ + 'name': 'Tarifas bancárias (boleto) ' + + account_move_line.numero_documento, 'debit': valor_tarifa, 'credit': 0.0, - 'account_id': account_move_line.payment_mode_id. - default_tax_account_id.id, + 'amount': valor_tarifa, + 'type': 'tarifa', + 'account_id': self.journal.default_debit_account_id.id, + 'ref': account_move_line.numero_documento, }) # Valor Abatimento @@ -469,30 +477,31 @@ def processar_arquivo_retorno_cnab400(self, data): valor_abatimento = self.cnab_str_to_float( linha_cnab['valor_abatimento']) - # TODO - Atualizar o Valor Recebido ou isso - # ja vem informado ? Confirmar no arquivo de retorno - # valor_recebido -= valor_abatimento + # Valor Liquido precisa ser somado + # p/ conciliar com a Fatura + valor_recebido += valor_abatimento result_row_list.append({ - 'name': 'Abatimento (boleto) ' + account_move_line.numero_documento, + 'name': 'Abatimento (boleto) ' + + account_move_line.numero_documento, 'debit': valor_abatimento, 'credit': 0.0, 'amount': valor_abatimento, - # TODO - Definir Conta Contabil de Abatimento - 'account_id': account_move_line.payment_mode_id. - default_tax_account_id.id, + 'account_id': self.journal.default_debit_account_id.id, 'type': 'abatimento', - 'bank_payment_line_id': - payment_line.bank_line_id.id or False, + 'ref': account_move_line.numero_documento, }) - new_aml_vals.append({ - 'name': 'Abatimento (boleto) ' + account_move_line.numero_documento, - 'debit': valor_abatimento, - 'credit': 0.0, - # TODO - Definir Conta Contabil de Abatimento + result_row_list.append({ + 'name': 'Abatimento (boleto) ' + + account_move_line.numero_documento, + 'debit': 0.0, + 'credit': valor_abatimento, + 'amount': valor_abatimento, 'account_id': account_move_line.payment_mode_id. - default_tax_account_id.id, + default_abatimento_account_id.id, + 'type': 'abatimento', + 'ref': account_move_line.numero_documento, }) vals_evento = { @@ -504,7 +513,7 @@ def processar_arquivo_retorno_cnab400(self, data): # obj_account_move_line.company_id.partner_id.name, 'favorecido_conta_bancaria_id': account_move_line.payment_mode_id. - fixed_journal_id.bank_account_id.id, + fixed_journal_id.bank_account_id.id, 'nosso_numero': linha_cnab['nosso_numero'], 'identificacao_titulo_empresa': linha_cnab['documento_numero'] or @@ -524,43 +533,36 @@ def processar_arquivo_retorno_cnab400(self, data): 'tarifa_cobranca': valor_tarifa, } - # Monta o dicionario que sera usado - # para criar o Extrato Bancario - balance_end_real += valor_recebido - line_statement_vals.append({ - 'name': account_move_line.numero_documento or '?', - 'amount': valor_recebido, - 'partner_id': account_move_line.partner_id.id, - 'ref': account_move_line.ref, - 'date': account_move_line.date, - 'amount_currency': valor_recebido, - 'currency_id': account_move_line.currency_id.id, - }) + if cnab_return_method == 'manual': + # Monta o dicionario que sera usado + # para criar o Extrato Bancario + balance_end_real += valor_recebido + line_statement_vals.append({ + 'name': account_move_line.numero_documento or '?', + 'amount': valor_recebido, + 'partner_id': account_move_line.partner_id.id, + 'ref': account_move_line.ref, + 'date': account_move_line.date, + 'amount_currency': valor_recebido, + 'currency_id': account_move_line.currency_id.id, + }) # Linha da Fatura a ser reconciliada - result_row_list.append({ - 'name': account_move_line.numero_documento, - 'debit': 0.0, - 'credit': valor_recebido, - 'amount': valor_recebido, - 'move_line': account_move_line, - 'type': 'liquidado', - 'bank_payment_line_id': - payment_line.bank_line_id.id or False, - }) - - counterpart_line_vals.append({ - 'name': account_move_line.numero_documento, - 'debit': 0.0, - 'credit': valor_recebido, - 'move_line': account_move_line, - }) - - # Monta uma Lista com Dicionarios usados para - # reconciliar o extrato bancario - reconcile_line_vals['new_aml_vals'] = new_aml_vals - reconcile_line_vals['counterpart_aml_vals'] = counterpart_line_vals - reconcile_statement_vals.append(reconcile_line_vals) + if cnab_return_method == 'automatic': + result_row_list.append({ + 'name': account_move_line.invoice_id.number, + 'debit': 0.0, + 'credit': valor_recebido, + 'amount': valor_recebido, + 'move_line': account_move_line, + 'invoice_id': account_move_line.invoice_id.id, + 'type': 'liquidado', + 'bank_payment_line_id': + payment_line.bank_line_id.id or False, + 'ref': account_move_line.nosso_numero, + 'account_id': account_move_line.account_id.id, + 'partner_id': account_move_line.partner_id.id, + }) else: vals_evento = { @@ -579,13 +581,14 @@ def processar_arquivo_retorno_cnab400(self, data): self.num_lotes = 1 self.num_eventos = quantidade_registros - # Criacao de um Extrato Bancario a ser conciliado logo abaixo, sendo - # necessário o usuario apenas clicar em Validar na tela do Extrato. - # TODO - automatizar validando o extrato ou deixar de usar o extrato ? + # Forma Manual do Retorno CNAB + # Criacao de um Extrato Bancario, isso permite o tratamento de alguma + # diferença que tenha restado já que os valores de Juros/Mora, Tarifas, + # Desconto e Abatimento estão sendo lançados em uma entrada de Diário + # separada e portanto na maioria dos casos o valor no extrato vai estar + # de acordo com o valor da fatura if line_statement_vals: vals_bank_statement = { - # TODO - Validar usar a sequencia do diário no nome do Extrato - # estaria correto ? 'name': self.journal.sequence_id.next_by_id(), 'journal_id': self.journal.id, 'balance_end_real': balance_end_real, @@ -597,17 +600,6 @@ def processar_arquivo_retorno_cnab400(self, data): line['statement_id'] = statement.id statement_line_obj.create(line) - # Reconciliação do Extrato e Fatura - # for line in statement.line_ids: - # for reconcile_line in reconcile_statement_vals: - # if line.name == reconcile_line.get('numero_documento'): - # line.process_reconciliation( - # counterpart_aml_dicts=reconcile_line.get( - # 'counterpart_aml_vals'), - # new_aml_dicts=reconcile_line.get( - # 'new_aml_vals') - # ) - return result_row_list def cnab_str_to_float(self, value): @@ -622,8 +614,9 @@ def get_move_vals(self): :return: dict of vals that represent additional infos for the statement """ return { - 'name': self.move_name or '/', - 'date': self.move_date or fields.Datetime.now(), + 'name': 'Retorno CNAB - ' + str( + fields.Datetime.now().date().strftime('%d/%m/%Y')), + 'date': fields.Datetime.now(), 'ref': self.move_ref or '/' } @@ -647,56 +640,24 @@ def get_move_line_vals(self, line, *args, **kwargs): """ vals = { - "name": line["name"] or line.get("source"), - # "date_maturity": date.fromtimestamp(line["created"]), - "credit": line['credit'], - "debit": line['debit'], - "ref": line.get("source"), - "already_completed": False, - "partner_id": None, - "account_id": None, - # 'bank_payment_line_id': line['bank_payment_line_id'], + 'name': line['name'] or line.get('source'), + 'credit': line['credit'], + 'debit': line['debit'], + 'already_completed': False, + 'partner_id': None, + 'account_id': None, + 'ref': line['ref'], + 'account_id': line['account_id'], } - if line["type"] == 'liquidada': - vals.update( - { - #"partner_id": self.journal.partner_id.id, - 'move_line': line['move_line'], - "account_id": line['account_id'], - "already_completed": True, - } - ) - elif line["type"] == "desconto": - vals.update( - { - #"partner_id": self.journal.partner_id.id, - "account_id": line['account_id'], - # "already_completed": True, - } - ) - elif line["type"] == "tarifa": - vals.update( - { - #"partner_id": self.journal.partner_id.id, - "account_id": line['account_id'], - #"already_completed": True, - } - ) - elif line["type"] == "abatimento": - vals.update( - { - #"partner_id": self.journal.partner_id.id, - "account_id": line['account_id'], - #"already_completed": True, - } - ) - elif line["type"] == "juros_mora": - vals.update( - { - #"partner_id": self.journal.partner_id.id, - "account_id": line['account_id'], - #"already_completed": True, + if line['type'] == 'liquidado': + vals.update({ + 'invoice_id': line['invoice_id'], + 'partner_id': line['partner_id'], + 'already_completed': True, } ) + elif line["type"] == "juros_mora" and line['credit'] > 0.0: + vals.update({ + 'partner_id': line['partner_id']}) return vals diff --git a/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml b/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml index 96925cd37d5e..6f7c1e5a9a3a 100644 --- a/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml +++ b/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml @@ -19,6 +19,15 @@
+
From fed357f3a5c7f86bc84d4a0af563e850d88e76ed Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 20 Aug 2020 19:41:37 -0300 Subject: [PATCH 051/308] [REF] Update change made in dependency module, Remessa CNAB. --- .../models/account_journal.py | 2 +- .../parser/cnab_file_parser.py | 104 +++++++++--------- 2 files changed, 50 insertions(+), 56 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/models/account_journal.py b/l10n_br_account_payment_brcobranca/models/account_journal.py index 262f228a6ace..0429bb0dfa79 100644 --- a/l10n_br_account_payment_brcobranca/models/account_journal.py +++ b/l10n_br_account_payment_brcobranca/models/account_journal.py @@ -44,7 +44,7 @@ def _write_extra_move_lines(self, parser, move): # nosso numero, que pode acontecer qdo existem mais de um banco # configurado para gerar Boletos line_to_reconcile = move_line_obj.search([ - ('nosso_numero', '=', line.ref), + ('own_number', '=', line.ref), ('invoice_id', '=', line.invoice_id.id) ]) diff --git a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py index 30621304d926..dbf7eba760e9 100644 --- a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py +++ b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py @@ -300,11 +300,11 @@ def processar_arquivo_retorno_cnab400(self, data): # Nosso Numero sem o Digito Verificador if bank_name_brcobranca == 'bradesco': account_move_line = self.env['account.move.line'].search( - [('nosso_numero', '=', linha_cnab['nosso_numero'][:11])] + [('own_number', '=', linha_cnab['nosso_numero'][:11])] ) elif bank_name_brcobranca == 'unicred': account_move_line = self.env['account.move.line'].search( - [('nosso_numero', '=', linha_cnab['nosso_numero'][6:16])] + [('own_number', '=', linha_cnab['nosso_numero'][6:16])] ) payment_line = self.env['account.payment.line'].search( @@ -374,6 +374,9 @@ def processar_arquivo_retorno_cnab400(self, data): (cod_ocorrencia in ('01', '06', '07', '09') and bank_name_brcobranca == 'unicred')): + valor_desconto = valor_juros_mora =\ + valor_abatimento = valor_tarifa = 0.0 + # Valor Desconto if linha_cnab.get('desconto'): valor_desconto = self.cnab_str_to_float( @@ -385,24 +388,22 @@ def processar_arquivo_retorno_cnab400(self, data): result_row_list.append({ 'name': 'Desconto (boleto) ' + - account_move_line.numero_documento, - 'amount': valor_desconto, + account_move_line.document_number, 'debit': valor_desconto, 'credit': 0.0, 'account_id': self.journal.default_debit_account_id.id, 'type': 'desconto', - 'ref': account_move_line.numero_documento, + 'ref': account_move_line.document_number, }) result_row_list.append({ 'name': 'Desconto (boleto) ' + - account_move_line.numero_documento, - 'amount': valor_desconto, + account_move_line.document_number, 'debit': 0.0, 'credit': valor_desconto, 'account_id': account_move_line.payment_mode_id. - default_discount_account_id.id, + discount_account_id.id, 'type': 'desconto', - 'ref': account_move_line.numero_documento, + 'ref': account_move_line.document_number, }) # Valor Juros Mora - valor de mora e multa pagos pelo sacado @@ -417,26 +418,24 @@ def processar_arquivo_retorno_cnab400(self, data): result_row_list.append({ 'name': 'Valor Juros Mora (boleto) ' + - account_move_line.numero_documento, + account_move_line.document_number, 'debit': 0.0, 'credit': valor_juros_mora, - 'amount': valor_juros_mora, 'account_id': account_move_line.payment_mode_id. - default_juros_mora_account_id.id, + interest_fee_account_id.id, 'type': 'juros_mora', - 'ref': account_move_line.numero_documento, + 'ref': account_move_line.document_number, 'partner_id': account_move_line.partner_id.id, }) result_row_list.append({ 'name': 'Valor Juros Mora (boleto) ' + - account_move_line.numero_documento, + account_move_line.document_number, 'debit': valor_juros_mora, 'credit': 0.0, - 'amount': valor_juros_mora, 'account_id': self.journal.default_debit_account_id.id, 'type': 'juros_mora', - 'ref': account_move_line.numero_documento, + 'ref': account_move_line.document_number, }) # Valor Tarifa @@ -451,25 +450,23 @@ def processar_arquivo_retorno_cnab400(self, data): result_row_list.append({ 'name': 'Tarifas bancárias (boleto) ' + - account_move_line.numero_documento, + account_move_line.document_number, 'debit': 0.0, 'credit': valor_tarifa, - 'amount': valor_tarifa, 'account_id': account_move_line.payment_mode_id. - default_tax_account_id.id, + tax_account_id.id, 'type': 'tarifa', - 'ref': account_move_line.numero_documento, + 'ref': account_move_line.document_number, }) result_row_list.append({ 'name': 'Tarifas bancárias (boleto) ' + - account_move_line.numero_documento, + account_move_line.document_number, 'debit': valor_tarifa, 'credit': 0.0, - 'amount': valor_tarifa, 'type': 'tarifa', 'account_id': self.journal.default_debit_account_id.id, - 'ref': account_move_line.numero_documento, + 'ref': account_move_line.document_number, }) # Valor Abatimento @@ -483,54 +480,53 @@ def processar_arquivo_retorno_cnab400(self, data): result_row_list.append({ 'name': 'Abatimento (boleto) ' + - account_move_line.numero_documento, + account_move_line.document_number, 'debit': valor_abatimento, 'credit': 0.0, - 'amount': valor_abatimento, 'account_id': self.journal.default_debit_account_id.id, 'type': 'abatimento', - 'ref': account_move_line.numero_documento, + 'ref': account_move_line.document_number, }) result_row_list.append({ 'name': 'Abatimento (boleto) ' + - account_move_line.numero_documento, + account_move_line.document_number, 'debit': 0.0, 'credit': valor_abatimento, - 'amount': valor_abatimento, 'account_id': account_move_line.payment_mode_id. - default_abatimento_account_id.id, + rebate_account_id.id, 'type': 'abatimento', - 'ref': account_move_line.numero_documento, + 'ref': account_move_line.document_number, }) vals_evento = { - 'lote_id': lote_id.id, - 'data_ocorrencia': data_ocorrencia, - 'data_real_pagamento': data_credito.strftime("%Y-%m-%d"), + 'lot_id': lote_id.id, + 'occurrence_date': data_ocorrencia, + 'real_payment_date': data_credito.strftime("%Y-%m-%d"), # 'segmento': evento.servico_segmento, # 'favorecido_nome': # obj_account_move_line.company_id.partner_id.name, - 'favorecido_conta_bancaria_id': + 'favored_bank_account_id': account_move_line.payment_mode_id. fixed_journal_id.bank_account_id.id, - 'nosso_numero': linha_cnab['nosso_numero'], - 'identificacao_titulo_empresa': + 'own_number': linha_cnab['nosso_numero'], + 'your_number': account_move_line.document_number, + 'company_title_identification': linha_cnab['documento_numero'] or - account_move_line.numero_documento, + account_move_line.document_number, # 'tipo_moeda': evento.credito_moeda_tipo, - 'valor': valor_titulo, - 'valor_pagamento': valor_recebido, - 'ocorrencias': descricao_ocorrencia, + 'title_value': valor_titulo, + 'payment_value': valor_recebido, + 'occurrences': descricao_ocorrencia, 'bank_payment_line_id': payment_line.bank_line_id.id or False, 'invoice_id': account_move_line.invoice_id.id, - 'data_vencimento': datetime.datetime.strptime( + 'due_date': datetime.datetime.strptime( str(linha_cnab['data_vencimento']), "%d%m%y").date(), - 'valor_desconto': valor_desconto, - 'juros_mora_multa': valor_juros_mora, - 'valor_abatimento': valor_abatimento, - 'tarifa_cobranca': valor_tarifa, + 'discount_value': valor_desconto, + 'interest_fee_value': valor_juros_mora, + 'rebate_value': valor_abatimento, + 'tariff_charge': valor_tarifa, } if cnab_return_method == 'manual': @@ -538,7 +534,7 @@ def processar_arquivo_retorno_cnab400(self, data): # para criar o Extrato Bancario balance_end_real += valor_recebido line_statement_vals.append({ - 'name': account_move_line.numero_documento or '?', + 'name': account_move_line.document_number or '?', 'amount': valor_recebido, 'partner_id': account_move_line.partner_id.id, 'ref': account_move_line.ref, @@ -553,25 +549,24 @@ def processar_arquivo_retorno_cnab400(self, data): 'name': account_move_line.invoice_id.number, 'debit': 0.0, 'credit': valor_recebido, - 'amount': valor_recebido, 'move_line': account_move_line, 'invoice_id': account_move_line.invoice_id.id, 'type': 'liquidado', 'bank_payment_line_id': payment_line.bank_line_id.id or False, - 'ref': account_move_line.nosso_numero, + 'ref': account_move_line.own_number, 'account_id': account_move_line.account_id.id, 'partner_id': account_move_line.partner_id.id, }) else: vals_evento = { - 'lote_id': lote_id.id, - 'ocorrencias': descricao_ocorrencia, - 'data_ocorrencia': data_ocorrencia, - 'nosso_numero': linha_cnab['nosso_numero'], - 'seu_numero': account_move_line.numero_documento, - 'valor': valor_titulo, + 'lot_id': lote_id.id, + 'occurrences': descricao_ocorrencia, + 'occurrence_date': data_ocorrencia, + 'own_number': account_move_line.own_number, + 'your_number': account_move_line.document_number, + 'title_value': valor_titulo, } self.env['l10n_br.cnab.evento'].create(vals_evento) @@ -645,7 +640,6 @@ def get_move_line_vals(self, line, *args, **kwargs): 'debit': line['debit'], 'already_completed': False, 'partner_id': None, - 'account_id': None, 'ref': line['ref'], 'account_id': line['account_id'], } From b20c2008b97574e2beddfd62a6468d02142ec4a6 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Fri, 21 Aug 2020 13:31:17 -0300 Subject: [PATCH 052/308] [REF] Separation of Tariff Charge Account from Product Tax Account. --- l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py index dbf7eba760e9..92fb30264424 100644 --- a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py +++ b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py @@ -454,7 +454,7 @@ def processar_arquivo_retorno_cnab400(self, data): 'debit': 0.0, 'credit': valor_tarifa, 'account_id': account_move_line.payment_mode_id. - tax_account_id.id, + tariff_charge_account_id.id, 'type': 'tarifa', 'ref': account_move_line.document_number, }) From 2ce62be930343bce815ed74a6bcdbc3d512714ea Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Wed, 9 Sep 2020 15:28:25 -0300 Subject: [PATCH 053/308] [FIX] Liquidity and other values should be used to reconcile invoice. --- .../parser/cnab_file_parser.py | 331 +++++++++--------- 1 file changed, 158 insertions(+), 173 deletions(-) diff --git a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py index 92fb30264424..d88fcd8ce7a8 100644 --- a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py +++ b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py @@ -16,149 +16,149 @@ ) DICT_OCORRENCIAS_BRADESCO = { - '02': u'Entrada Confirmada (verificar motivo na posição 319 a 328)', - '03': u'Entrada Rejeitada ( verificar motivo na posição 319 a 328)', - '06': u'Liquidação normal (sem motivo)', - '09': u'Baixado Automat. via Arquivo (verificar motivo posição 319 a 328)', - '10': u'Baixado conforme instruções da Agência(' - u'verificar motivo pos.319 a 328)', - '11': u'Em Ser - Arquivo de Títulos pendentes (sem motivo)', - '12': u'Abatimento Concedido (sem motivo)', - '13': u'Abatimento Cancelado (sem motivo)', - '14': u'Vencimento Alterado (sem motivo)', - '15': u'Liquidação em Cartório (sem motivo)', - '16': u'Título Pago em Cheque – Vinculado', - '17': u'Liquidação após baixa ou Título não registrado (sem motivo)', - '18': u'Acerto de Depositária (sem motivo)', - '19': u'Confirmação Receb. Inst. de Protesto ' - u'(verificar motivo pos.295 a 295)', - '20': u'Confirmação Recebimento Instrução Sustação de' - u' Protesto (sem motivo)', - '21': u'Acerto do Controle do Participante (sem motivo)', - '22': u'Título Com Pagamento Cancelado', - '23': u'Entrada do Título em Cartório (sem motivo)', - '24': u'Entrada rejeitada por CEP Irregular' - u' (verificar motivo pos.319 a 328)', - '25': u'Confirmação Receb.Inst.de Protesto Falimentar' - u' (verificar pos.295 a 295)', - '27': u'Baixa Rejeitada (verificar motivo posição 319 a 328)', - '28': u'Débito de tarifas/custas (verificar motivo na posição 319 a 328)', - '29': u'Ocorrências do Pagador (NOVO)', - '30': u'Alteração de Outros Dados Rejeitados ' - u'(verificar motivo pos.319 a 328)', - '32': u'Instrução Rejeitada (verificar motivo posição 319 a 328)', - '33': u'Confirmação Pedido Alteração Outros Dados (sem motivo)', - '34': u'Retirado de Cartório e Manutenção Carteira (sem motivo)', - '35': u'Desagendamento do débito automático ' - u'(verificar motivos pos. 319 a 328)', - '40': u'Estorno de pagamento (NOVO)', - '55': u'Sustado judicial (NOVO)', - '68': u'Acerto dos dados do rateio de Crédito (verificar motivo posição de' - u' status do registro tipo 3)', - '69': u'Cancelamento dos dados do rateio (verificar motivo posição de' - u' status do registro tipo 3)', - '073': u'Confirmação Receb. Pedido de Negativação (NOVO)', - '074': u'Confir Pedido de Excl de Negat (com ou sem baixa) (NOVO)', - '00': u'Nota: Para as ocorrências sem motivos, as posições serão' - u' informadas com Zeros.', + '02': 'Entrada Confirmada (verificar motivo na posição 319 a 328)', + '03': 'Entrada Rejeitada ( verificar motivo na posição 319 a 328)', + '06': 'Liquidação normal (sem motivo)', + '09': 'Baixado Automat. via Arquivo (verificar motivo posição 319 a 328)', + '10': 'Baixado conforme instruções da Agência(' + 'verificar motivo pos.319 a 328)', + '11': 'Em Ser - Arquivo de Títulos pendentes (sem motivo)', + '12': 'Abatimento Concedido (sem motivo)', + '13': 'Abatimento Cancelado (sem motivo)', + '14': 'Vencimento Alterado (sem motivo)', + '15': 'Liquidação em Cartório (sem motivo)', + '16': 'Título Pago em Cheque – Vinculado', + '17': 'Liquidação após baixa ou Título não registrado (sem motivo)', + '18': 'Acerto de Depositária (sem motivo)', + '19': 'Confirmação Receb. Inst. de Protesto ' + '(verificar motivo pos.295 a 295)', + '20': 'Confirmação Recebimento Instrução Sustação de' + ' Protesto (sem motivo)', + '21': 'Acerto do Controle do Participante (sem motivo)', + '22': 'Título Com Pagamento Cancelado', + '23': 'Entrada do Título em Cartório (sem motivo)', + '24': 'Entrada rejeitada por CEP Irregular' + ' (verificar motivo pos.319 a 328)', + '25': 'Confirmação Receb.Inst.de Protesto Falimentar' + ' (verificar pos.295 a 295)', + '27': 'Baixa Rejeitada (verificar motivo posição 319 a 328)', + '28': 'Débito de tarifas/custas (verificar motivo na posição 319 a 328)', + '29': 'Ocorrências do Pagador (NOVO)', + '30': 'Alteração de Outros Dados Rejeitados ' + '(verificar motivo pos.319 a 328)', + '32': 'Instrução Rejeitada (verificar motivo posição 319 a 328)', + '33': 'Confirmação Pedido Alteração Outros Dados (sem motivo)', + '34': 'Retirado de Cartório e Manutenção Carteira (sem motivo)', + '35': 'Desagendamento do débito automático ' + '(verificar motivos pos. 319 a 328)', + '40': 'Estorno de pagamento (NOVO)', + '55': 'Sustado judicial (NOVO)', + '68': 'Acerto dos dados do rateio de Crédito (verificar motivo posição de' + ' status do registro tipo 3)', + '69': 'Cancelamento dos dados do rateio (verificar motivo posição de' + ' status do registro tipo 3)', + '073': 'Confirmação Receb. Pedido de Negativação (NOVO)', + '074': 'Confir Pedido de Excl de Negat (com ou sem baixa) (NOVO)', + '00': 'Nota: Para as ocorrências sem motivos, as posições serão' + ' informadas com Zeros.', } DICT_OCORRENCIAS_ITAU = { - '02': u'ENTRADA CONFIRMADA COM POSSIBILIDADE DE MENSAGEM' - u' (NOTA 20 – TABELA 10)', - '03': u'ENTRADA REJEITADA (NOTA 20 – TABELA 1)', - '04': u'ALTERAÇÃO DE DADOS – NOVA ENTRADA OU ALTERAÇÃO/EXCLUSÃO' - u' DE DADOS ACATADA', - '05': u'ALTERAÇÃO DE DADOS – BAIXA', - '06': u'LIQUIDAÇÃO NORMAL', - '07': u'LIQUIDAÇÃO PARCIAL – COBRANÇA INTELIGENTE (B2B)', - '08': u'LIQUIDAÇÃO EM CARTÓRIO', - '09': u'BAIXA SIMPLES', - '10': u'BAIXA POR TER SIDO LIQUIDADO', - '11': u'EM SER (SÓ NO RETORNO MENSAL)', - '12': u'ABATIMENTO CONCEDIDO', - '13': u'ABATIMENTO CANCELADO', - '14': u'VENCIMENTO ALTERADO', - '15': u'BAIXAS REJEITADAS (NOTA 20 – TABELA 4)', - '16': u'INSTRUÇÕES REJEITADAS (NOTA 20 – TABELA 3)', - '17': u'ALTERAÇÃO/EXCLUSÃO DE DADOS REJEITADOS (NOTA 20 – TABELA 2)', - '18': u'COBRANÇA CONTRATUAL – INSTRUÇÕES/ALTERAÇÕES' - u' REJEITADAS/PENDENTES (NOTA 20 – TABELA 5)', - '19': u'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE PROTESTO', - '20': u'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE SUSTAÇÃO' - u' DE PROTESTO /TARIFA', - '21': u'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE NÃO PROTESTAR', - '23': u'TÍTULO ENVIADO A CARTÓRIO/TARIFA', - '24': u'INSTRUÇÃO DE PROTESTO REJEITADA / SUSTADA / PENDENTE' - u' (NOTA 20 – TABELA 7)', - '25': u'ALEGAÇÕES DO PAGADOR (NOTA 20 – TABELA 6)', - '26': u'TARIFA DE AVISO DE COBRANÇA', - '27': u'TARIFA DE EXTRATO POSIÇÃO (B40X)', - '28': u'TARIFA DE RELAÇÃO DAS LIQUIDAÇÕES', - '29': u'TARIFA DE MANUTENÇÃO DE TÍTULOS VENCIDOS', - '30': u'DÉBITO MENSAL DE TARIFAS (PARA ENTRADAS E BAIXAS)', - '32': u'BAIXA POR TER SIDO PROTESTADO', - '33': u'CUSTAS DE PROTESTO', - '34': u'CUSTAS DE SUSTAÇÃO', - '35': u'CUSTAS DE CARTÓRIO DISTRIBUIDOR', - '36': u'CUSTAS DE EDITAL', - '37': u'TARIFA DE EMISSÃO DE BOLETO/TARIFA DE ENVIO DE DUPLICATA', - '38': u'TARIFA DE INSTRUÇÃO', - '39': u'TARIFA DE OCORRÊNCIAS', - '40': u'TARIFA MENSAL DE EMISSÃO DE BOLETO/TARIFA MENSAL' - u' DE ENVIO DE DUPLICATA', - '41': u'DÉBITO MENSAL DE TARIFAS – EXTRATO DE POSIÇÃO (B4EP/B4OX)', - '42': u'DÉBITO MENSAL DE TARIFAS – OUTRAS INSTRUÇÕES', - '43': u'DÉBITO MENSAL DE TARIFAS – MANUTENÇÃO DE TÍTULOS VENCIDOS', - '44': u'DÉBITO MENSAL DE TARIFAS – OUTRAS OCORRÊNCIAS', - '45': u'DÉBITO MENSAL DE TARIFAS – PROTESTO', - '46': u'DÉBITO MENSAL DE TARIFAS – SUSTAÇÃO DE PROTESTO', - '47': u'BAIXA COM TRANSFERÊNCIA PARA DESCONTO', - '48': u'CUSTAS DE SUSTAÇÃO JUDICIAL', - '51': u'TARIFA MENSAL REF A ENTRADAS BANCOS CORRESPONDENTES NA CARTEIRA', - '52': u'TARIFA MENSAL BAIXAS NA CARTEIRA', - '53': u'TARIFA MENSAL BAIXAS EM BANCOS CORRESPONDENTES NA CARTEIRA', - '54': u'TARIFA MENSAL DE LIQUIDAÇÕES NA CARTEIRA', - '55': u'TARIFA MENSAL DE LIQUIDAÇÕES EM BANCOS' - u' CORRESPONDENTES NA CARTEIRA', - '56': u'CUSTAS DE IRREGULARIDADE', - '57': u'INSTRUÇÃO CANCELADA (NOTA 20 – TABELA 8)', - '59': u'BAIXA POR CRÉDITO EM C/C ATRAVÉS DO SISPAG', - '60': u'ENTRADA REJEITADA CARNÊ (NOTA 20 – TABELA 1)', - '61': u'TARIFA EMISSÃO AVISO DE MOVIMENTAÇÃO DE TÍTULOS (2154)', - '62': u'DÉBITO MENSAL DE TARIFA – AVISO DE MOVIMENTAÇÃO DE TÍTULOS (2154)', - '63': u'TÍTULO SUSTADO JUDICIALMENTE', - '64': u'ENTRADA CONFIRMADA COM RATEIO DE CRÉDITO', - '65': u'PAGAMENTO COM CHEQUE – AGUARDANDO COMPENSAÇÃO', - '69': u'CHEQUE DEVOLVIDO (NOTA 20 – TABELA 9)', - '71': u'ENTRADA REGISTRADA, AGUARDANDO AVALIAÇÃO', - '72': u'BAIXA POR CRÉDITO EM C/C ATRAVÉS DO SISPAG' - u' SEM TÍTULO CORRESPONDENTE', - '73': u'CONFIRMAÇÃO DE ENTRADA NA COBRANÇA SIMPLES –' - u' ENTRADA NÃO ACEITA NA COBRANÇA CONTRATUAL', - '74': u'INSTRUÇÃO DE NEGATIVAÇÃO EXPRESSA REJEITADA (NOTA 20 – TABELA 11)', - '75': u'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE ENTRADA' - u' EM NEGATIVAÇÃO EXPRESSA', - '76': u'CHEQUE COMPENSADO', - '77': u'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE EXCLUSÃO DE' - u' ENTRADA EM NEGATIVAÇÃO EXPRESSA', - '78': u'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE CANCELAMENTO DE' - u' NEGATIVAÇÃO EXPRESSA', - '79': u'NEGATIVAÇÃO EXPRESSA INFORMACIONAL (NOTA 20 – TABELA 12)', - '80': u'CONFIRMAÇÃO DE ENTRADA EM NEGATIVAÇÃO EXPRESSA – TARIFA', - '82': u'CONFIRMAÇÃO DO CANCELAMENTO DE NEGATIVAÇÃO EXPRESSA – TARIFA', - '83': u'CONFIRMAÇÃO DE EXCLUSÃO DE ENTRADA EM NEGATIVAÇÃO' - u' EXPRESSA POR LIQUIDAÇÃO – TARIFA', - '85': u'TARIFA POR BOLETO (ATÉ 03 ENVIOS) COBRANÇA ATIVA ELETRÔNICA', - '86': u'TARIFA EMAIL COBRANÇA ATIVA ELETRÔNICA', - '87': u'TARIFA SMS COBRANÇA ATIVA ELETRÔNICA', - '88': u'TARIFA MENSAL POR BOLETO (ATÉ 03 ENVIOS)' - u' COBRANÇA ATIVA ELETRÔNICA', - '89': u'TARIFA MENSAL EMAIL COBRANÇA ATIVA ELETRÔNICA', - '90': u'TARIFA MENSAL SMS COBRANÇA ATIVA ELETRÔNICA', - '91': u'TARIFA MENSAL DE EXCLUSÃO DE ENTRADA DE NEGATIVAÇÃO EXPRESSA', - '92': u'TARIFA MENSAL DE CANCELAMENTO DE NEGATIVAÇÃO EXPRESSA', - '93': u'TARIFA MENSAL DE EXCLUSÃO DE NEGATIVAÇÃO EXPRESSA POR LIQUIDAÇÃO', + '02': 'ENTRADA CONFIRMADA COM POSSIBILIDADE DE MENSAGEM' + ' (NOTA 20 – TABELA 10)', + '03': 'ENTRADA REJEITADA (NOTA 20 – TABELA 1)', + '04': 'ALTERAÇÃO DE DADOS – NOVA ENTRADA OU ALTERAÇÃO/EXCLUSÃO' + ' DE DADOS ACATADA', + '05': 'ALTERAÇÃO DE DADOS – BAIXA', + '06': 'LIQUIDAÇÃO NORMAL', + '07': 'LIQUIDAÇÃO PARCIAL – COBRANÇA INTELIGENTE (B2B)', + '08': 'LIQUIDAÇÃO EM CARTÓRIO', + '09': 'BAIXA SIMPLES', + '10': 'BAIXA POR TER SIDO LIQUIDADO', + '11': 'EM SER (SÓ NO RETORNO MENSAL)', + '12': 'ABATIMENTO CONCEDIDO', + '13': 'ABATIMENTO CANCELADO', + '14': 'VENCIMENTO ALTERADO', + '15': 'BAIXAS REJEITADAS (NOTA 20 – TABELA 4)', + '16': 'INSTRUÇÕES REJEITADAS (NOTA 20 – TABELA 3)', + '17': 'ALTERAÇÃO/EXCLUSÃO DE DADOS REJEITADOS (NOTA 20 – TABELA 2)', + '18': 'COBRANÇA CONTRATUAL – INSTRUÇÕES/ALTERAÇÕES' + ' REJEITADAS/PENDENTES (NOTA 20 – TABELA 5)', + '19': 'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE PROTESTO', + '20': 'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE SUSTAÇÃO' + ' DE PROTESTO /TARIFA', + '21': 'CONFIRMA RECEBIMENTO DE INSTRUÇÃO DE NÃO PROTESTAR', + '23': 'TÍTULO ENVIADO A CARTÓRIO/TARIFA', + '24': 'INSTRUÇÃO DE PROTESTO REJEITADA / SUSTADA / PENDENTE' + ' (NOTA 20 – TABELA 7)', + '25': 'ALEGAÇÕES DO PAGADOR (NOTA 20 – TABELA 6)', + '26': 'TARIFA DE AVISO DE COBRANÇA', + '27': 'TARIFA DE EXTRATO POSIÇÃO (B40X)', + '28': 'TARIFA DE RELAÇÃO DAS LIQUIDAÇÕES', + '29': 'TARIFA DE MANUTENÇÃO DE TÍTULOS VENCIDOS', + '30': 'DÉBITO MENSAL DE TARIFAS (PARA ENTRADAS E BAIXAS)', + '32': 'BAIXA POR TER SIDO PROTESTADO', + '33': 'CUSTAS DE PROTESTO', + '34': 'CUSTAS DE SUSTAÇÃO', + '35': 'CUSTAS DE CARTÓRIO DISTRIBUIDOR', + '36': 'CUSTAS DE EDITAL', + '37': 'TARIFA DE EMISSÃO DE BOLETO/TARIFA DE ENVIO DE DUPLICATA', + '38': 'TARIFA DE INSTRUÇÃO', + '39': 'TARIFA DE OCORRÊNCIAS', + '40': 'TARIFA MENSAL DE EMISSÃO DE BOLETO/TARIFA MENSAL' + ' DE ENVIO DE DUPLICATA', + '41': 'DÉBITO MENSAL DE TARIFAS – EXTRATO DE POSIÇÃO (B4EP/B4OX)', + '42': 'DÉBITO MENSAL DE TARIFAS – OUTRAS INSTRUÇÕES', + '43': 'DÉBITO MENSAL DE TARIFAS – MANUTENÇÃO DE TÍTULOS VENCIDOS', + '44': 'DÉBITO MENSAL DE TARIFAS – OUTRAS OCORRÊNCIAS', + '45': 'DÉBITO MENSAL DE TARIFAS – PROTESTO', + '46': 'DÉBITO MENSAL DE TARIFAS – SUSTAÇÃO DE PROTESTO', + '47': 'BAIXA COM TRANSFERÊNCIA PARA DESCONTO', + '48': 'CUSTAS DE SUSTAÇÃO JUDICIAL', + '51': 'TARIFA MENSAL REF A ENTRADAS BANCOS CORRESPONDENTES NA CARTEIRA', + '52': 'TARIFA MENSAL BAIXAS NA CARTEIRA', + '53': 'TARIFA MENSAL BAIXAS EM BANCOS CORRESPONDENTES NA CARTEIRA', + '54': 'TARIFA MENSAL DE LIQUIDAÇÕES NA CARTEIRA', + '55': 'TARIFA MENSAL DE LIQUIDAÇÕES EM BANCOS' + ' CORRESPONDENTES NA CARTEIRA', + '56': 'CUSTAS DE IRREGULARIDADE', + '57': 'INSTRUÇÃO CANCELADA (NOTA 20 – TABELA 8)', + '59': 'BAIXA POR CRÉDITO EM C/C ATRAVÉS DO SISPAG', + '60': 'ENTRADA REJEITADA CARNÊ (NOTA 20 – TABELA 1)', + '61': 'TARIFA EMISSÃO AVISO DE MOVIMENTAÇÃO DE TÍTULOS (2154)', + '62': 'DÉBITO MENSAL DE TARIFA – AVISO DE MOVIMENTAÇÃO DE TÍTULOS (2154)', + '63': 'TÍTULO SUSTADO JUDICIALMENTE', + '64': 'ENTRADA CONFIRMADA COM RATEIO DE CRÉDITO', + '65': 'PAGAMENTO COM CHEQUE – AGUARDANDO COMPENSAÇÃO', + '69': 'CHEQUE DEVOLVIDO (NOTA 20 – TABELA 9)', + '71': 'ENTRADA REGISTRADA, AGUARDANDO AVALIAÇÃO', + '72': 'BAIXA POR CRÉDITO EM C/C ATRAVÉS DO SISPAG' + ' SEM TÍTULO CORRESPONDENTE', + '73': 'CONFIRMAÇÃO DE ENTRADA NA COBRANÇA SIMPLES –' + ' ENTRADA NÃO ACEITA NA COBRANÇA CONTRATUAL', + '74': 'INSTRUÇÃO DE NEGATIVAÇÃO EXPRESSA REJEITADA (NOTA 20 – TABELA 11)', + '75': 'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE ENTRADA' + ' EM NEGATIVAÇÃO EXPRESSA', + '76': 'CHEQUE COMPENSADO', + '77': 'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE EXCLUSÃO DE' + ' ENTRADA EM NEGATIVAÇÃO EXPRESSA', + '78': 'CONFIRMAÇÃO DE RECEBIMENTO DE INSTRUÇÃO DE CANCELAMENTO DE' + ' NEGATIVAÇÃO EXPRESSA', + '79': 'NEGATIVAÇÃO EXPRESSA INFORMACIONAL (NOTA 20 – TABELA 12)', + '80': 'CONFIRMAÇÃO DE ENTRADA EM NEGATIVAÇÃO EXPRESSA – TARIFA', + '82': 'CONFIRMAÇÃO DO CANCELAMENTO DE NEGATIVAÇÃO EXPRESSA – TARIFA', + '83': 'CONFIRMAÇÃO DE EXCLUSÃO DE ENTRADA EM NEGATIVAÇÃO' + ' EXPRESSA POR LIQUIDAÇÃO – TARIFA', + '85': 'TARIFA POR BOLETO (ATÉ 03 ENVIOS) COBRANÇA ATIVA ELETRÔNICA', + '86': 'TARIFA EMAIL COBRANÇA ATIVA ELETRÔNICA', + '87': 'TARIFA SMS COBRANÇA ATIVA ELETRÔNICA', + '88': 'TARIFA MENSAL POR BOLETO (ATÉ 03 ENVIOS)' + ' COBRANÇA ATIVA ELETRÔNICA', + '89': 'TARIFA MENSAL EMAIL COBRANÇA ATIVA ELETRÔNICA', + '90': 'TARIFA MENSAL SMS COBRANÇA ATIVA ELETRÔNICA', + '91': 'TARIFA MENSAL DE EXCLUSÃO DE ENTRADA DE NEGATIVAÇÃO EXPRESSA', + '92': 'TARIFA MENSAL DE CANCELAMENTO DE NEGATIVAÇÃO EXPRESSA', + '93': 'TARIFA MENSAL DE EXCLUSÃO DE NEGATIVAÇÃO EXPRESSA POR LIQUIDAÇÃO', } DICT_OCORRENCIAS_UNICRED = { @@ -226,8 +226,8 @@ def parse(self, filebuffer): files = {'data': base64.b64decode(filebuffer)} api_address = self.env[ - "ir.config_parameter"].sudo().get_param( - "l10n_br_account_payment_brcobranca.boleto_cnab_api") + 'ir.config_parameter'].sudo().get_param( + 'l10n_br_account_payment_brcobranca.boleto_cnab_api') if not api_address: raise UserError( ('Não é possível gerar o retorno.\n' @@ -352,18 +352,6 @@ def processar_arquivo_retorno_cnab400(self, data): self.env['l10n_br.cnab.evento'].create(vals_evento) continue - valor_recebido = 0.0 - if linha_cnab['valor_recebido']: - valor_recebido = self.cnab_str_to_float( - linha_cnab['valor_recebido']) - - if (linha_cnab['data_credito'] == '000000' or - not linha_cnab['data_credito']): - data_credito = linha_cnab['data_credito'] - else: - data_credito = datetime.datetime.strptime( - str(linha_cnab['data_credito']), "%d%m%y").date() - # Codigos de Ocorrencia - Liquidação # TODO - esses codigos deveriam ser informados em um campo ao inves # de estarem chumbados aqui @@ -374,18 +362,25 @@ def processar_arquivo_retorno_cnab400(self, data): (cod_ocorrencia in ('01', '06', '07', '09') and bank_name_brcobranca == 'unicred')): - valor_desconto = valor_juros_mora =\ + valor_recebido = valor_desconto = valor_juros_mora =\ valor_abatimento = valor_tarifa = 0.0 + if linha_cnab['valor_recebido']: + valor_recebido = self.cnab_str_to_float( + linha_cnab['valor_recebido']) + + if (linha_cnab['data_credito'] == '000000' or + not linha_cnab['data_credito']): + data_credito = linha_cnab['data_credito'] + else: + data_credito = datetime.datetime.strptime( + str(linha_cnab['data_credito']), "%d%m%y").date() + # Valor Desconto if linha_cnab.get('desconto'): valor_desconto = self.cnab_str_to_float( linha_cnab['desconto']) - # Valor Liquido precisa ser somado - # p/ conciliar com a Fatura - valor_recebido += valor_desconto - result_row_list.append({ 'name': 'Desconto (boleto) ' + account_move_line.document_number, @@ -411,11 +406,6 @@ def processar_arquivo_retorno_cnab400(self, data): valor_juros_mora = self.cnab_str_to_float( linha_cnab['juros_mora']) - # O valor não está sendo somando pois isso - # altera o Valor recebido desbalanceando a - # conciliação com a Fatura - # valor_recebido += valor_juros_mora - result_row_list.append({ 'name': 'Valor Juros Mora (boleto) ' + account_move_line.document_number, @@ -444,10 +434,6 @@ def processar_arquivo_retorno_cnab400(self, data): str(linha_cnab['valor_tarifa'][0:4] + '.' + linha_cnab['valor_tarifa'][4:])) - # Valor Liquido precisa ser somado - # p/ conciliar com a Fatura - valor_recebido += valor_tarifa - result_row_list.append({ 'name': 'Tarifas bancárias (boleto) ' + account_move_line.document_number, @@ -474,10 +460,6 @@ def processar_arquivo_retorno_cnab400(self, data): valor_abatimento = self.cnab_str_to_float( linha_cnab['valor_abatimento']) - # Valor Liquido precisa ser somado - # p/ conciliar com a Fatura - valor_recebido += valor_abatimento - result_row_list.append({ 'name': 'Abatimento (boleto) ' + account_move_line.document_number, @@ -532,6 +514,10 @@ def processar_arquivo_retorno_cnab400(self, data): if cnab_return_method == 'manual': # Monta o dicionario que sera usado # para criar o Extrato Bancario + # TODO checar possivel BUG, se não houver outro valor + # a ser adicionado no result_row_list o modulo original + # retorna erro de Empty File, mas esse caso só aconteceria + # se a Tarifa Bancaria for ZERO balance_end_real += valor_recebido line_statement_vals.append({ 'name': account_move_line.document_number or '?', @@ -594,7 +580,6 @@ def processar_arquivo_retorno_cnab400(self, data): for line in line_statement_vals: line['statement_id'] = statement.id statement_line_obj.create(line) - return result_row_list def cnab_str_to_float(self, value): From 1b36068b4e998755bc91b38578f4f6ffe000fef3 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Fri, 11 Sep 2020 11:09:09 -0300 Subject: [PATCH 054/308] [REF] Automatic reconciliation should be done by the diary instead of parameter to allow different configurations. --- .../__manifest__.py | 1 + .../data/res_config_settings_data.xml | 4 ---- .../models/account_journal.py | 18 ++++++++---------- .../models/res_config_settings.py | 16 ---------------- .../parser/cnab_file_parser.py | 7 ++----- .../views/account_journal_view.xml | 15 +++++++++++++++ .../views/res_config_settings_view.xml | 9 --------- 7 files changed, 26 insertions(+), 44 deletions(-) create mode 100644 l10n_br_account_payment_brcobranca/views/account_journal_view.xml diff --git a/l10n_br_account_payment_brcobranca/__manifest__.py b/l10n_br_account_payment_brcobranca/__manifest__.py index 1408b855fd76..f9664afc6ebd 100644 --- a/l10n_br_account_payment_brcobranca/__manifest__.py +++ b/l10n_br_account_payment_brcobranca/__manifest__.py @@ -20,6 +20,7 @@ 'views/l10n_br_cnab_retorno_view.xml', 'views/res_config_settings_view.xml', 'views/account_bank_statement_view.xml', + 'views/account_journal_view.xml', 'data/res_config_settings_data.xml', 'security/ir.model.access.csv', ], diff --git a/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml b/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml index b6b7816aa073..1c93af57ec2a 100644 --- a/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml +++ b/l10n_br_account_payment_brcobranca/data/res_config_settings_data.xml @@ -10,8 +10,4 @@ search="[('id', '=', ref('boleto_cnab_api_config_settings'))]"/> - - manual - -
\ No newline at end of file diff --git a/l10n_br_account_payment_brcobranca/models/account_journal.py b/l10n_br_account_payment_brcobranca/models/account_journal.py index 0429bb0dfa79..1b5edd963144 100644 --- a/l10n_br_account_payment_brcobranca/models/account_journal.py +++ b/l10n_br_account_payment_brcobranca/models/account_journal.py @@ -12,6 +12,12 @@ class AccountJournal(models.Model): selection_add=[('cnab400', 'CNAB 400'), ('cnab240', 'CNAB 240')] ) + return_auto_reconcile = fields.Boolean( + string="Automatic Reconcile payment returns", + help="Enable automatic payment return reconciliation.", + default=False, + ) + @api.multi def _write_extra_move_lines(self, parser, move): """Insert extra lines after the main statement lines. @@ -29,11 +35,7 @@ def _write_extra_move_lines(self, parser, move): move_line_obj = self.env["account.move.line"] - cnab_return_method = self.env[ - 'ir.config_parameter'].sudo().get_param( - 'l10n_br_account_payment_brcobranca.cnab_return_method') - - if cnab_return_method == 'automatic': + if self.return_auto_reconcile: # TODO - os outros valores deveriam ser conciliados ? # Porque para isso deverão estar na mesma Conta Contabil @@ -67,11 +69,7 @@ def _move_import( move = super()._move_import( parser, file_stream, result_row_list=None, ftype="csv") - cnab_return_method = self.env[ - 'ir.config_parameter'].sudo().get_param( - 'l10n_br_account_payment_brcobranca.cnab_return_method') - - if cnab_return_method == 'automatic': + if self.return_auto_reconcile: move.post() return move diff --git a/l10n_br_account_payment_brcobranca/models/res_config_settings.py b/l10n_br_account_payment_brcobranca/models/res_config_settings.py index 1734278e5b4a..c74754379863 100644 --- a/l10n_br_account_payment_brcobranca/models/res_config_settings.py +++ b/l10n_br_account_payment_brcobranca/models/res_config_settings.py @@ -14,12 +14,6 @@ class ResConfigSettings(models.TransientModel): default='boleto_cnab_api' ) - cnab_return_method = fields.Selection( - [('manual', 'Manual'), - ('automatic', 'Automatico')], 'CNAB Return method', - default='manual', - ) - @api.model def get_values(self): res = super(ResConfigSettings, self).get_values() @@ -27,9 +21,6 @@ def get_values(self): boleto_cnab_api=self.env[ 'ir.config_parameter'].sudo().get_param( 'l10n_br_account_payment_brcobranca.boleto_cnab_api'), - cnab_return_method=self.env[ - 'ir.config_parameter'].sudo().get_param( - 'l10n_br_account_payment_brcobranca.cnab_return_method') ) return res @@ -44,10 +35,3 @@ def set_values(self): param.set_param( 'l10n_br_account_payment_brcobranca.boleto_cnab_api', boleto_cnab_api) - - cnab_return_method =\ - self.cnab_return_method or 'manual' - - param.set_param( - 'l10n_br_account_payment_brcobranca.cnab_return_method', - cnab_return_method) diff --git a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py index d88fcd8ce7a8..c49e3a823252 100644 --- a/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py +++ b/l10n_br_account_payment_brcobranca/parser/cnab_file_parser.py @@ -272,9 +272,6 @@ def processar_arquivo_retorno_cnab400(self, data): # da forma Manual mais o valor total, conciliado com # a Fatura correspondente # - cnab_return_method = self.env[ - 'ir.config_parameter'].sudo().get_param( - 'l10n_br_account_payment_brcobranca.cnab_return_method') # Lista com os dados q poderão ser usados na criação das account move line result_row_list = [] @@ -511,7 +508,7 @@ def processar_arquivo_retorno_cnab400(self, data): 'tariff_charge': valor_tarifa, } - if cnab_return_method == 'manual': + if not self.journal.return_auto_reconcile: # Monta o dicionario que sera usado # para criar o Extrato Bancario # TODO checar possivel BUG, se não houver outro valor @@ -530,7 +527,7 @@ def processar_arquivo_retorno_cnab400(self, data): }) # Linha da Fatura a ser reconciliada - if cnab_return_method == 'automatic': + if self.journal.return_auto_reconcile: result_row_list.append({ 'name': account_move_line.invoice_id.number, 'debit': 0.0, diff --git a/l10n_br_account_payment_brcobranca/views/account_journal_view.xml b/l10n_br_account_payment_brcobranca/views/account_journal_view.xml new file mode 100644 index 000000000000..ba630085388a --- /dev/null +++ b/l10n_br_account_payment_brcobranca/views/account_journal_view.xml @@ -0,0 +1,15 @@ + + + + + account.journal.form + account.journal + + + + + + + + + \ No newline at end of file diff --git a/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml b/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml index 6f7c1e5a9a3a..96925cd37d5e 100644 --- a/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml +++ b/l10n_br_account_payment_brcobranca/views/res_config_settings_view.xml @@ -19,15 +19,6 @@ -