[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:
@@ -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")
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user