[FIX] account: allow full reconciliation for tax lines on entries mixing cash basis and regular taxes

Solves https://github.com/odoo/odoo/issues/56803

Use case:

1) Create accounts ACC TAX A, ACC TAX B, with reconcile = True, internal_type = other

2) Create Tax A, with tax_exigibilty = on_invoice and account_id = ACC TAX A, 20%

3) Create Tax B, with tax_exigibilty = on_payment and account_id = ACC TAX B and cash_basic_account_id = ACC TAX A, 20%

4) Create invoice A, account_id = Tax A, unit_price = 1000, Valide invoice

5) Create invoice B, account_id = Tax B, unit_price = 500, Valide invoice,

6) Register full payment for invoice B. The cash basis tax creates a miscellaneous operation MISC A, containing a line on ACC TAX A.

7) Create a mis operation MISC TVA, like this:
line 1 : account_id = ACC TAX A, credit = 300
line 2 : account_id = 401100, debit = 300

8) Reconcile the following move lines:
line of MISC A with account ACC TAX A (300)
line of invoice A with account ACC TAX A (200)
line of MISC TVA with account ACC TAX A (100)

==> When doing that, not full reconcile object was created between the reconciled lines, while it should have been.

X-original-commit: ff35a49

[IMP] account: reintroduce a test that should have been forward-ported …

This test was made together with a bugfix by https://github.com/odoo/odoo/pull/51529 . The fix did not need to be forward-ported to 13.0, but the test should have been, so that we are sure it does not break again in the future.

closes odoo/odoo#58072

X-original-commit: 5d00592f7d803888d8d5befd5a8cd1b096067860
Signed-off-by: oco-odoo <oco-odoo@users.noreply.github.com>
Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
This commit is contained in:
fw-bot
2020-09-18 17:34:46 +00:00
committed by oco-odoo
parent 98cd83aaa0
commit df5dde330c
10 changed files with 126 additions and 27 deletions
+21 -7
View File
@@ -46,7 +46,7 @@ class AccountTestInvoicingCommon(SavepointCase):
cls.cr = cls.env.cr
cls.company_data_2 = cls.setup_company_data('company_2_data', chart_template=chart_template)
cls.company_data = cls.setup_company_data('company_1_data', chart_template=chart_template)
cls.company_data = cls.setup_company_data('company_1_data', chart_template=chart_template)
user.write({
'company_ids': [(6, 0, (cls.company_data['company'] + cls.company_data_2['company']).ids)],
@@ -359,15 +359,29 @@ class AccountTestInvoicingCommon(SavepointCase):
})
@classmethod
def init_invoice(cls, move_type, partner=None, invoice_date=None):
def init_invoice(cls, move_type, partner=None, invoice_date=None, post=False, products=[], amounts=[], taxes=None):
move_form = Form(cls.env['account.move'].with_context(default_move_type=move_type))
move_form.invoice_date = invoice_date or fields.Date.from_string('2019-01-01')
move_form.partner_id = partner or cls.partner_a
with move_form.invoice_line_ids.new() as line_form:
line_form.product_id = cls.product_a
with move_form.invoice_line_ids.new() as line_form:
line_form.product_id = cls.product_b
return move_form.save()
for product in products:
with move_form.invoice_line_ids.new() as line_form:
line_form.product_id = product
if taxes:
line_form.tax_ids.add(taxes)
for amount in amounts:
with move_form.invoice_line_ids.new() as line_form:
line_form.price_unit = amount
if taxes:
line_form.tax_ids.add(taxes)
rslt = move_form.save()
if post:
rslt.action_post()
return rslt
def assertInvoiceValues(self, move, expected_lines_values, expected_move_values):
def sort_lines(lines):
@@ -13,7 +13,7 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon):
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.invoice = cls.init_invoice('in_invoice')
cls.invoice = cls.init_invoice('in_invoice', products=cls.product_a+cls.product_b)
cls.product_line_vals_1 = {
'name': cls.product_a.name,
@@ -12,7 +12,7 @@ class TestAccountMoveInRefundOnchanges(AccountTestInvoicingCommon):
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.invoice = cls.init_invoice('in_refund')
cls.invoice = cls.init_invoice('in_refund', products=cls.product_a+cls.product_b)
cls.product_line_vals_1 = {
'name': cls.product_a.name,
@@ -16,7 +16,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.invoice = cls.init_invoice('out_invoice')
cls.invoice = cls.init_invoice('out_invoice', products=cls.product_a+cls.product_b)
cls.product_line_vals_1 = {
'name': cls.product_a.name,
@@ -12,7 +12,7 @@ class TestAccountMoveOutRefundOnchanges(AccountTestInvoicingCommon):
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.invoice = cls.init_invoice('out_refund')
cls.invoice = cls.init_invoice('out_refund', products=cls.product_a+cls.product_b)
cls.product_line_vals_1 = {
'name': cls.product_a.name,
@@ -43,14 +43,14 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'user_type_id': cls.env.ref('account.data_account_type_revenue').id,
'company_id': cls.company_data['company'].id,
})
cls.tax_account_1 = cls.env['account.account'].create({
'code': 'tax_account_1',
'name': 'tax_account_1',
'user_type_id': cls.env.ref('account.data_account_type_revenue').id,
'company_id': cls.company_data['company'].id,
})
cls.tax_account_2 = cls.env['account.account'].create({
'code': 'tax_account_2',
'name': 'tax_account_2',
@@ -74,6 +74,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'amount': 33.3333,
'company_id': cls.company_data['company'].id,
'cash_basis_transition_account_id': cls.cash_basis_transfer_account.id,
'tax_exigibility': 'on_payment',
'invoice_repartition_line_ids': [
(0, 0, {
'factor_percent': 100,
@@ -82,7 +83,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
}),
(0, 0, {
'factor_percent': 10,
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': cls.tax_account_1.id,
'tag_ids': [(6, 0, cls.tax_tags[1].ids)],
@@ -96,7 +97,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
}),
(0, 0, {
'factor_percent': 10,
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': cls.tax_account_1.id,
'tag_ids': [(6, 0, cls.tax_tags[3].ids)],
@@ -109,6 +110,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
'amount': 0.0001,
'company_id': cls.company_data['company'].id,
'cash_basis_transition_account_id': cls.cash_basis_transfer_account.id,
'tax_exigibility': 'on_payment',
'invoice_repartition_line_ids': [
(0, 0, {
'factor_percent': 100,
@@ -117,7 +119,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
}),
(0, 0, {
'factor_percent': 10,
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': cls.tax_account_2.id,
'tag_ids': [(6, 0, cls.tax_tags[5].ids)],
@@ -131,7 +133,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
}),
(0, 0, {
'factor_percent': 10,
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': cls.tax_account_2.id,
'tag_ids': [(6, 0, cls.tax_tags[7].ids)],
@@ -171,10 +173,10 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
def assertAmountsGroupByAccount(self, amount_per_account):
expected_values = {account.id: (account, balance, amount_currency) for account, balance, amount_currency in amount_per_account}
if not expected_values:
return
self.cr.execute('''
SELECT
line.account_id,
@@ -209,7 +211,7 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
SUM(line.balance)
FROM account_account_tag_account_move_line_rel rel
JOIN account_move_line line ON line.id = rel.account_move_line_id
WHERE line.tax_exigible IS TRUE
WHERE line.tax_exigible IS TRUE
GROUP BY rel.account_account_tag_id
''')
@@ -752,9 +754,9 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
})
(cash_basis_move + payment_move).action_post()
# Initial amounts by accounts:
self.assertAmountsGroupByAccount([
# Account Balance Amount Currency
(self.cash_basis_transfer_account, -33.34, -33.34),
@@ -1633,3 +1635,86 @@ class TestAccountMoveReconcile(AccountTestInvoicingCommon):
(self.tax_tags[4], -100.0),
(self.tax_tags[5], -0.01),
])
def test_caba_mix_reconciliation(self):
""" Test the reconciliation of tax lines (when using a reconcilable tax account)
for cases mixing taxes exigible on payment and on invoices.
"""
# Make the tax account reconcilable
self.tax_account_1.reconcile = True
# Create a tax using the same accounts as the CABA one
non_caba_tax = self.env['account.tax'].create({
'name': 'tax 20%',
'type_tax_use': 'purchase',
'company_id': self.company_data['company'].id,
'amount': 20,
'tax_exigibility': 'on_invoice',
'invoice_repartition_line_ids': [
(0,0, {
'factor_percent': 100,
'repartition_type': 'base',
}),
(0,0, {
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': self.tax_account_1.id,
}),
],
'refund_repartition_line_ids': [
(0,0, {
'factor_percent': 100,
'repartition_type': 'base',
}),
(0,0, {
'factor_percent': 100,
'repartition_type': 'tax',
'account_id': self.tax_account_1.id,
}),
],
})
# Create an invoice with a non-CABA tax
non_caba_inv = self.init_invoice('in_invoice', amounts=[1000], post=True, taxes=non_caba_tax)
# Create an invoice with a CABA tax using the same tax account and pay it
caba_inv = self.init_invoice('in_invoice', amounts=[300], post=True, taxes=self.cash_basis_tax_a_third_amount)
pmt_wizard = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=caba_inv.ids).create({
'payment_date': caba_inv.date,
'journal_id': self.company_data['default_journal_bank'].id,
'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id,
})
pmt_wizard._create_payments()
partial_rec = caba_inv.mapped('line_ids.matched_debit_ids')
caba_move = self.env['account.move'].search([('tax_cash_basis_rec_id', '=', partial_rec.id)])
# Create a misc operation with a line on the tax account, for full reconcile of those tax lines
misc_move = self.env['account.move'].create({
'name': "Misc move",
'journal_id': self.company_data['default_journal_misc'].id,
'line_ids': [
(0, 0, {
'name': 'line 1',
'account_id': self.tax_account_1.id,
'credit': 300,
}),
(0, 0, {
'name': 'line 2',
'account_id': self.company_data['default_account_expense'].id, # Whatever the account here
'debit': 300,
})
],
})
misc_move.action_post()
lines_to_reconcile = (misc_move + caba_move + non_caba_inv).mapped('line_ids').filtered(lambda x: x.account_id == self.tax_account_1)
lines_to_reconcile.reconcile()
# Check full reconciliation
self.assertTrue(all(line.full_reconcile_id for line in lines_to_reconcile), "All tax lines should be fully reconciled")
+1 -1
View File
@@ -67,7 +67,7 @@ class TestAccountInvoiceRounding(AccountTestInvoicingCommon):
],
})
cls.invoice = cls.init_invoice('out_refund')
cls.invoice = cls.init_invoice('out_refund', products=cls.product_a+cls.product_b)
def assertPaymentTerm(self, pay_term, invoice_date, dates):
with Form(self.invoice) as move_form:
@@ -9,7 +9,7 @@ class TestAccountDebitNote(AccountTestInvoicingCommon):
def test_00_debit_note_out_invoice(self):
""" Debit Note of a regular Customer Invoice"""
invoice = self.init_invoice('out_invoice')
invoice = self.init_invoice('out_invoice', products=self.product_a+self.product_b)
invoice.action_post()
move_debit_note_wiz = self.env['account.debit.note'].with_context(active_model="account.move",
active_ids=invoice.ids).create({
@@ -28,7 +28,7 @@ class TestAccountDebitNote(AccountTestInvoicingCommon):
def test_10_debit_note_in_refund(self):
""" Debit Note of a vendor refund (is a regular vendor bill) """
invoice = self.init_invoice('in_refund')
invoice = self.init_invoice('in_refund', products=self.product_a+self.product_b)
invoice.action_post()
move_debit_note_wiz = self.env['account.debit.note'].with_context(active_model="account.move",
active_ids=invoice.ids).create({
+1 -1
View File
@@ -10,7 +10,7 @@ class InvoiceGetReferenceTest(AccountTestInvoicingCommon):
def setUpClass(cls, chart_template_ref='l10n_fi.fi_chart_template'):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.invoice = cls.init_invoice('out_invoice')
cls.invoice = cls.init_invoice('out_invoice', products=cls.product_a+cls.product_b)
def test_get_reference_finnish_invoice(self):
self.assertFalse(self.invoice.payment_reference)
+1 -1
View File
@@ -15,7 +15,7 @@ class PaymentReferenceTest(AccountTestInvoicingCommon):
def setUpClass(cls, chart_template_ref='l10n_fi.fi_chart_template'):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.invoice = cls.init_invoice('out_invoice')
cls.invoice = cls.init_invoice('out_invoice', products=cls.product_a+cls.product_b)
def test_payment_reference_fi(self):