From 753557abd37ab2af199a282f6f0a068376185791 Mon Sep 17 00:00:00 2001 From: Magno Costa Date: Thu, 8 Apr 2021 11:44:54 -0300 Subject: [PATCH] [REF] Treatment of payment outside CNAB centralized in method reconcile at account.move.line . --- .../models/account_invoice.py | 93 ------------------- .../models/account_move_line.py | 35 +++++++ .../models/account_payment.py | 14 +-- .../models/l10n_br_cnab_change_methods.py | 16 +++- .../tests/test_payment_order_inbound.py | 39 ++++++++ 5 files changed, 92 insertions(+), 105 deletions(-) diff --git a/l10n_br_account_payment_order/models/account_invoice.py b/l10n_br_account_payment_order/models/account_invoice.py index c65ebb15a6b6..9e0c404fff2f 100644 --- a/l10n_br_account_payment_order/models/account_invoice.py +++ b/l10n_br_account_payment_order/models/account_invoice.py @@ -146,59 +146,6 @@ def action_move_create(self): self._pos_action_move_create() return result - @api.multi - def create_payment_outside_cnab(self, amount_payment): - """ - Em caso de CNAB é preciso verificar e criar linha(s) - com Codigo de Instrução do Movimento de Baixa - ou de Alteração de Valor do Título quando existir. - :param amount_payment: Valor Pago - :return: - """ - - # Identificar a Linha CNAB que vai ser dado Baixa ou - # terá o Valor do Titulo alterado devido a um pagamento parcial - applicable_lines = change_tittle_value_line = \ - self.env['account.move.line'] - - lines_to_check = self.move_id.line_ids.filtered( - lambda x: x.debit > 0.0 and x.payment_situation in - ('inicial', 'aberta') - ) - - # Valor Total, baixar todas as Parcelas em Aberto - if self.amount_total == amount_payment: - applicable_lines |= lines_to_check - else: - # Verificar se é o pagto de mais de uma parcela - # OBS.: A sequencia/order da alocação de valores e baixas/alteração - # de valor segue as Datas de Vencimento, porque não pode ser pago - # fora dessa ordem. - amount_value = 0.0 - for line in lines_to_check: - applicable_lines |= line - amount_value += line.debit - if amount_value == amount_payment: - # Valor Pago corresponde as linhas já percorridas - break - if amount_value > amount_payment: - # Valor Pago ficou menor que as linhas de debito essa linha - # foi paga parcialmente e essa Parcela deverá ter seu valor - # alterado - change_tittle_value_line = line - break - - reason_write_off = ( - ('Movement Instruction Code Updated for Request' - ' to Write Off, because payment of %s done outside CNAB.') - % amount_payment) - payment_situation = 'baixa_liquidacao' - for line in applicable_lines: - if line == change_tittle_value_line: - line.create_cnab_change_tittle_value() - else: - line.create_cnab_write_off(reason_write_off, payment_situation) - @api.multi def invoice_validate(self): result = super().invoice_validate() @@ -209,43 +156,3 @@ def invoice_validate(self): if filtered_invoice_id.payment_mode_id.payment_order_ok: filtered_invoice_id.create_account_payment_line() return result - - @api.multi - def assign_outstanding_credit(self, credit_aml_id): - self.ensure_one() - # TODO - Existe necessidade de ser feito algo nesse metodo ? - # O Metodo parece ser chamado apenas no modulo sale - # https://github.com/OCA/OCB/blob/12.0/addons/sale/ - # models/account_invoice.py#L68 - # if self.eval_situacao_pagamento in ['paga', 'liquidada', - # 'baixa_liquidacao']: - # raise UserError( - # _( - # 'Não é possível adicionar pagamentos em uma fatura que ' - # 'já está paga.' - # ) - # ) - # if self.eval_state_cnab in ['accepted', 'exported', 'done']: - # raise UserError( - # _( - # 'Não é possível adicionar pagamentos em uma fatura já ' - # 'exportada ou aceita no banco.' - # ) - # ) - return super().assign_outstanding_credit(credit_aml_id) - - @api.multi - def register_payment( - self, payment_line, writeoff_acc_id=False, writeoff_journal_id=False - ): - res = super().register_payment( - payment_line, writeoff_acc_id, writeoff_journal_id) - - self._pos_action_move_create() - - for inv in self: - inv._compute_financial() - receivable_id = inv.financial_move_line_ids - receivable_id.residual = inv.residual - - return res diff --git a/l10n_br_account_payment_order/models/account_move_line.py b/l10n_br_account_payment_order/models/account_move_line.py index 2cbc204c201c..11f80fd3af7a 100644 --- a/l10n_br_account_payment_order/models/account_move_line.py +++ b/l10n_br_account_payment_order/models/account_move_line.py @@ -231,3 +231,38 @@ def _compute_journal_payment_mode(self): if record.payment_mode_id.fixed_journal_id: record.journal_payment_mode_id =\ record.payment_mode_id.fixed_journal_id.id + + @api.multi + def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False): + + res = super().reconcile(writeoff_acc_id, writeoff_journal_id) + for record in self: + # Verificar Casos de CNAB + if (record.payment_mode_id.payment_method_code in + ('240', '400', '500') and + record.payment_mode_id.payment_method_id.payment_type == + 'inbound'): + # Na importação do arquivo de retorno o metodo também é + # chamado no caso do modulo l10n_br_account_payment_brcobranca + # o contexto traz o campo 'file_name' que ao ser encontrado + # ignora o envio de alterações CNAB, outros modulos precisam + # validar isso + # Caso de Não Pagamento já está criando um Pedido de Baixa + if self.env.context.get('file_name') or\ + self.env.context.get('not_payment'): + continue + if record.matched_credit_ids: + for l in record.matched_credit_ids: + if not l.already_send_cnab: + record.create_payment_outside_cnab(l.amount) + l.already_send_cnab = True + + return res + + +class AccountPartialReconcile(models.Model): + _inherit = 'account.partial.reconcile' + + # Evita que uma conciliação parcial seja + # considerada novamente em um pagamento + already_send_cnab = fields.Boolean(string='Already send CNAB') diff --git a/l10n_br_account_payment_order/models/account_payment.py b/l10n_br_account_payment_order/models/account_payment.py index 9886be800ef4..f0a104d3cae3 100644 --- a/l10n_br_account_payment_order/models/account_payment.py +++ b/l10n_br_account_payment_order/models/account_payment.py @@ -14,7 +14,9 @@ class AccountPayment(models.Model): def post(self): for record in self: - if record.payment_method_code in ('240', '400', '500'): + if record.payment_method_code in ('240', '400', '500') and\ + record.payment_mode_id.payment_method_id.payment_type\ + == 'inbound': # TODO - Idealmente isso deveria ser resolvido com um # domain=[('code', 'not in', ('400','240','500'))] # no campo payment_method_id, mas mesmo adicionando isso na @@ -31,13 +33,3 @@ def post(self): ' choose another one.')) super().post() - for invoice in record.invoice_ids: - # Se é CNAB do tipo Recebiveis deve ser feita a validação - # se é possível e se necessário informar o Banco a respeito - # de Baixa ou Alteração do Valor no caso do Pagamento for - # Parcial e permitir esse tipo de instrução. - if (invoice.payment_mode_id.payment_method_code in - ('240', '400', '500') and - invoice.payment_mode_id.payment_method_id.payment_type - == 'inbound'): - invoice.create_payment_outside_cnab(record.amount) diff --git a/l10n_br_account_payment_order/models/l10n_br_cnab_change_methods.py b/l10n_br_account_payment_order/models/l10n_br_cnab_change_methods.py index fa65011dee84..537f5495316e 100644 --- a/l10n_br_account_payment_order/models/l10n_br_cnab_change_methods.py +++ b/l10n_br_account_payment_order/models/l10n_br_cnab_change_methods.py @@ -256,7 +256,8 @@ def _create_cnab_not_payment(self, payorder, new_payorder, reason): move_line_to_reconcile = moves.filtered( lambda m: m.credit > 0.0) - (self + move_line_to_reconcile).reconcile() + (self + move_line_to_reconcile).with_context( + not_payment=True).reconcile() self.create_payment_line_from_move_line(payorder) # Proceso CNAB encerrado @@ -475,3 +476,16 @@ def _create_baixa(self, reason, **kwargs): 'last_change_reason': reason, 'payment_situation': 'baixa', # FIXME: Podem ser múltiplos motivos }) + + def create_payment_outside_cnab(self, amount_payment): + + if self.amount_residual == 0.0: + reason_write_off = ( + ('Movement Instruction Code Updated for' + ' Request to Write Off, because payment' + ' of %s done outside CNAB.') % amount_payment) + payment_situation = 'baixa_liquidacao' + self.create_cnab_write_off( + reason_write_off, payment_situation) + else: + self.create_cnab_change_tittle_value() diff --git a/l10n_br_account_payment_order/tests/test_payment_order_inbound.py b/l10n_br_account_payment_order/tests/test_payment_order_inbound.py index 670e6d4227d0..c24458333c8e 100644 --- a/l10n_br_account_payment_order/tests/test_payment_order_inbound.py +++ b/l10n_br_account_payment_order/tests/test_payment_order_inbound.py @@ -220,3 +220,42 @@ def test_cancel_invoice_payment_order_draft(self): self.assertEquals(len(payment_order.payment_line_ids), 0) # Nesse caso a account.move deverá ter sido apagada self.assertEquals(len(self.invoice_unicred.move_id), 0) + + def test_payment_by_assign_outstanding_credit(self): + """ + Caso de Pagamento com CNAB usando o assign_outstanding_credit + """ + self.partner_akretion = self.env.ref( + 'l10n_br_base.res_partner_akretion' + ) + # I validate invoice by creating on + self.invoice_cef.action_invoice_open() + + payment_order = self.env['account.payment.order'].search([ + ('payment_mode_id', '=', self.invoice_cef.payment_mode_id.id) + ]) + # Open payment order + payment_order.draft2open() + # Generate and upload + # payment_order.open2generated() + # payment_order.generated2uploaded() + # self.assertEquals(payment_order.state, 'done') + + payment = self.env['account.payment'].create({ + 'payment_type': 'inbound', + 'payment_method_id': self.env.ref( + 'account.account_payment_method_manual_in').id, + 'partner_type': 'customer', + 'partner_id': self.partner_akretion.id, + 'amount': 100, + 'journal_id': self.journal_cash.id, + }) + payment.post() + credit_aml = payment.move_line_ids.filtered('credit') + + # Erro de ter uma Instrução CNAB Pendente, como não é possivel gerar a + # Ordem de Pagto o teste completo de pagamento via + # assign_outstanding_credit precisa ser feito no modulo que + # implementa biblioteca a ser usada. + with self.assertRaises(UserError): + self.invoice_cef.assign_outstanding_credit(credit_aml.id)