From 50c389ea444cf31db99f035434f9a361f05548a7 Mon Sep 17 00:00:00 2001 From: SimoRubi Date: Fri, 9 Oct 2020 10:34:42 +0200 Subject: [PATCH] [FIX] account_tax_balance: Account moves linked to bills/invoices should not be considered as refunds, unless all their lines come from refunds or their balance is negative. --- account_tax_balance/models/account_move.py | 35 +++++++--- .../tests/test_account_tax_balance.py | 68 +++++++++++++++++++ 2 files changed, 92 insertions(+), 11 deletions(-) diff --git a/account_tax_balance/models/account_move.py b/account_tax_balance/models/account_move.py index be5ff189..9ff3dd40 100644 --- a/account_tax_balance/models/account_move.py +++ b/account_tax_balance/models/account_move.py @@ -2,7 +2,7 @@ # © 2016 Antonio Espinosa # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api +from odoo import models, fields, api class AccountMove(models.Model): @@ -21,25 +21,38 @@ class AccountMove(models.Model): @api.multi @api.depends( 'line_ids.account_id.internal_type', 'line_ids.balance', - 'line_ids.account_id.user_type_id.type' + 'line_ids.account_id.user_type_id.type', 'line_ids.invoice_id.type' ) def _compute_move_type(self): - def _balance_get(line_ids, internal_type): - return sum(line_ids.filtered( - lambda x: x.account_id.internal_type == internal_type).mapped( - 'balance')) + refund_types = ('in_refund', 'out_refund') + + def _is_refund(line_ids, internal_type): + """Check whether all the lines of type `internal_type` + come from a refund.""" + line_ids = line_ids.filtered( + lambda x: x.account_id.internal_type == internal_type) + line_types = line_ids.mapped('invoice_id.type') + if len(line_types) == 1: + res = line_types[0] in refund_types + else: + # Lines are linked to invoices of different types, + # or to no invoice at all. + # If their summed balance is negative, this is a refund. + res = sum(line_ids.mapped('balance')) < 0 + return res for move in self: - internal_types = move.line_ids.mapped('account_id.internal_type') + move_lines = move.line_ids + internal_types = move_lines.mapped('account_id.internal_type') if 'liquidity' in internal_types: move.move_type = 'liquidity' elif 'payable' in internal_types: - balance = _balance_get(move.line_ids, 'payable') + is_refund = _is_refund(move_lines, 'payable') move.move_type = ( - 'payable' if balance < 0 else 'payable_refund') + 'payable' if not is_refund else 'payable_refund') elif 'receivable' in internal_types: - balance = _balance_get(move.line_ids, 'receivable') + is_refund = _is_refund(move_lines, 'receivable') move.move_type = ( - 'receivable' if balance > 0 else 'receivable_refund') + 'receivable' if not is_refund else 'receivable_refund') else: move.move_type = 'other' diff --git a/account_tax_balance/tests/test_account_tax_balance.py b/account_tax_balance/tests/test_account_tax_balance.py index d354453b..9f765d18 100644 --- a/account_tax_balance/tests/test_account_tax_balance.py +++ b/account_tax_balance/tests/test_account_tax_balance.py @@ -179,3 +179,71 @@ class TestAccountTaxBalance(TransactionCase): tax.refresh() self.assertEquals(tax.base_balance, 175.) self.assertEquals(tax.balance, 17.5) + + def test_receivable_move_type(self): + """ + Receivable moves linked to invoices are not considered as refunds. + Also, receivable moves coming from refunds are considered as such. + """ + receivable_account = self.env['account.account'].search( + [('user_type_id', '=', self.env.ref( + 'account.data_account_type_receivable' + ).id)], limit=1) + invoice = self.env['account.invoice'].create({ + 'partner_id': self.env.ref('base.res_partner_2').id, + 'account_id': receivable_account.id, + 'type': 'out_invoice', + 'invoice_line_ids': [(0, 0, { + 'product_id': self.env.ref('product.product_product_4').id, + 'account_id': receivable_account.id, + 'quantity': 1.0, + 'price_unit': 100.0, + 'name': 'product that costs 100', + }), (0, 0, { + 'product_id': self.env.ref('product.product_product_4').id, + 'account_id': receivable_account.id, + 'quantity': 1.0, + 'price_unit': -100.0, + 'name': 'product that costs -100', + })] + }) + invoice.action_invoice_open() + self.assertEqual(invoice.move_id.move_type, 'receivable') + + refund = invoice.refund() + refund.action_invoice_open() + self.assertEqual(refund.move_id.move_type, 'receivable_refund') + + def test_payable_move_type(self): + """ + Payable moves linked to bills are not considered as refunds. + Also, payable moves coming from refunds are considered as such. + """ + payable_account = self.env['account.account'].search( + [('user_type_id', '=', self.env.ref( + 'account.data_account_type_payable' + ).id)], limit=1) + bill = self.env['account.invoice'].create({ + 'partner_id': self.env.ref('base.res_partner_2').id, + 'account_id': payable_account.id, + 'type': 'in_invoice', + 'invoice_line_ids': [(0, 0, { + 'product_id': self.env.ref('product.product_product_4').id, + 'account_id': payable_account.id, + 'quantity': 1.0, + 'price_unit': 100.0, + 'name': 'product that costs 100', + }), (0, 0, { + 'product_id': self.env.ref('product.product_product_4').id, + 'account_id': payable_account.id, + 'quantity': 1.0, + 'price_unit': -100.0, + 'name': 'product that costs -100', + })] + }) + bill.action_invoice_open() + self.assertEqual(bill.move_id.move_type, 'payable') + + refund = bill.refund() + refund.action_invoice_open() + self.assertEqual(refund.move_id.move_type, 'payable_refund')