Files
enterprise-addons/account_accountant/tests/test_deferred_management.py
T

996 lines
55 KiB
Python

# -*- coding: utf-8 -*-
# pylint: disable=C0326
import datetime
from odoo import Command, fields
from odoo.exceptions import UserError
from odoo.tests import tagged
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from freezegun import freeze_time
@tagged('post_install', '-at_install')
class TestDeferredManagement(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.expense_accounts = [cls.env['account.account'].create({
'name': f'Expense {i}',
'code': f'EXP{i}',
'account_type': 'expense',
}) for i in range(3)]
cls.revenue_accounts = [cls.env['account.account'].create({
'name': f'Revenue {i}',
'code': f'REV{i}',
'account_type': 'income',
}) for i in range(3)]
cls.company.deferred_expense_journal_id = cls.env['account.journal'].create({
'name': 'Deferred Expense Journal',
'code': 'DEFEXP',
'type': 'general',
'company_id': cls.company.id,
})
cls.company.deferred_revenue_journal_id = cls.env['account.journal'].create({
'name': 'Deferred Revenue Journal',
'code': 'DEFREV',
'type': 'general',
'company_id': cls.company.id,
})
cls.company.deferred_expense_account_id = cls.company_data['default_account_deferred_expense'].id
cls.company.deferred_revenue_account_id = cls.company_data['default_account_deferred_revenue'].id
cls.expense_lines = [
[cls.expense_accounts[0], 1000, '2023-01-01', '2023-04-30'], # 4 full months (=250/month)
[cls.expense_accounts[0], 1050, '2023-01-16', '2023-04-30'], # 3 full months + 15 days (=300/month)
[cls.expense_accounts[1], 1225, '2023-01-01', '2023-04-15'], # 3 full months + 15 days (=350/month)
[cls.expense_accounts[2], 1680, '2023-01-21', '2023-04-14'], # 2 full months + 10 days + 14 days (=600/month)
[cls.expense_accounts[2], 225, '2023-04-01', '2023-04-15'], # 15 days (=450/month)
]
cls.revenue_lines = [
[cls.revenue_accounts[0], 1000, '2023-01-01', '2023-04-30'], # 4 full months (=250/month)
[cls.revenue_accounts[0], 1050, '2023-01-16', '2023-04-30'], # 3 full months + 15 days (=300/month)
[cls.revenue_accounts[1], 1225, '2023-01-01', '2023-04-15'], # 3 full months + 15 days (=350/month)
[cls.revenue_accounts[2], 1680, '2023-01-21', '2023-04-14'], # 2 full months + 10 days + 14 days (=600/month)
[cls.revenue_accounts[2], 225, '2023-04-01', '2023-04-15'], # 15 days (=450/month)
]
def create_invoice(self, move_type, move_lines, date=None, post=True):
journal = self.company_data['default_journal_misc']
if move_type in self.env['account.move'].get_purchase_types():
journal = self.company_data['default_journal_purchase']
elif move_type in self.env['account.move'].get_sale_types():
journal = self.company_data['default_journal_sale']
move_vals = {
'move_type': move_type,
'partner_id': self.partner_a.id,
'date': date or '2023-01-01',
'invoice_date': date or '2023-01-01',
'journal_id': journal.id,
}
if move_type != "entry":
move_vals["invoice_line_ids"] = [
Command.create({
'product_id': self.product_a.id,
'account_id': account.id,
'price_unit': balance,
'quantity': 1,
'deferred_start_date': start_date,
'deferred_end_date': end_date,
}) for account, balance, start_date, end_date in move_lines
]
else:
move_vals["line_ids"] = [
Command.create({
'account_id': account.id,
'balance': balance,
'deferred_start_date': start_date,
'deferred_end_date': end_date,
}) for account, balance, start_date, end_date in move_lines
] + [ # Counterpart line
Command.create({
'account_id': self.company_data['default_journal_bank'].default_account_id.id,
'balance': -sum(line[1] for line in move_lines),
})
]
move = self.env['account.move'].create(move_vals)
if post:
move.action_post()
return move
def test_deferred_management_get_diff_dates(self):
def assert_get_diff_dates(start, end, expected):
diff = self.env['account.move']._get_deferred_diff_dates(fields.Date.to_date(start), fields.Date.to_date(end))
self.assertAlmostEqual(diff, expected, 3)
assert_get_diff_dates('2023-01-01', '2023-01-01', 0)
assert_get_diff_dates('2023-01-01', '2023-01-02', 1/30)
assert_get_diff_dates('2023-01-01', '2023-01-20', 19/30)
assert_get_diff_dates('2023-01-01', '2023-01-31', 29/30)
assert_get_diff_dates('2023-01-01', '2023-01-30', 29/30)
assert_get_diff_dates('2023-01-01', '2023-02-01', 1)
assert_get_diff_dates('2023-01-01', '2023-02-28', 1 + 29/30)
assert_get_diff_dates('2023-02-01', '2023-02-28', 29/30)
assert_get_diff_dates('2023-02-10', '2023-02-28', 20/30)
assert_get_diff_dates('2023-01-01', '2023-02-15', 1 + 14/30)
assert_get_diff_dates('2023-01-01', '2023-03-31', 2 + 29/30)
assert_get_diff_dates('2023-01-01', '2023-04-01', 3)
assert_get_diff_dates('2023-01-01', '2023-04-30', 3 + 29/30)
assert_get_diff_dates('2023-01-10', '2023-04-30', 3 + 20/30)
assert_get_diff_dates('2023-01-10', '2023-04-09', 2 + 29/30)
assert_get_diff_dates('2023-01-10', '2023-04-10', 3)
assert_get_diff_dates('2023-01-10', '2023-04-11', 3 + 1/30)
assert_get_diff_dates('2023-02-20', '2023-04-10', 1 + 20/30)
assert_get_diff_dates('2023-01-31', '2023-04-30', 3)
assert_get_diff_dates('2023-02-28', '2023-04-10', 1 + 10/30)
assert_get_diff_dates('2023-03-01', '2023-04-10', 1 + 9/30)
assert_get_diff_dates('2023-04-10', '2023-03-01', 1 + 9/30)
assert_get_diff_dates('2023-01-01', '2023-12-31', 11 + 29/30)
assert_get_diff_dates('2023-01-01', '2024-01-01', 12)
assert_get_diff_dates('2023-01-01', '2024-07-01', 18)
assert_get_diff_dates('2023-01-01', '2024-07-10', 18 + 9/30)
def test_get_ends_of_month(self):
def assertEndsOfMonths(start_date, end_date, expected):
self.assertEqual(
self.env['account.move.line']._get_deferred_ends_of_month(
fields.Date.to_date(start_date),
fields.Date.to_date(end_date)
),
[fields.Date.to_date(date) for date in expected]
)
assertEndsOfMonths('2023-01-01', '2023-01-01', ['2023-01-31'])
assertEndsOfMonths('2023-01-01', '2023-01-02', ['2023-01-31'])
assertEndsOfMonths('2023-01-01', '2023-01-20', ['2023-01-31'])
assertEndsOfMonths('2023-01-01', '2023-01-30', ['2023-01-31'])
assertEndsOfMonths('2023-01-01', '2023-01-31', ['2023-01-31'])
assertEndsOfMonths('2023-01-01', '2023-02-01', ['2023-01-31', '2023-02-28'])
assertEndsOfMonths('2023-01-01', '2023-02-28', ['2023-01-31', '2023-02-28'])
assertEndsOfMonths('2023-02-01', '2023-02-28', ['2023-02-28'])
assertEndsOfMonths('2023-02-10', '2023-02-28', ['2023-02-28'])
assertEndsOfMonths('2023-01-01', '2023-02-15', ['2023-01-31', '2023-02-28'])
assertEndsOfMonths('2023-01-01', '2023-03-31', ['2023-01-31', '2023-02-28', '2023-03-31'])
assertEndsOfMonths('2023-01-01', '2023-04-01', ['2023-01-31', '2023-02-28', '2023-03-31', '2023-04-30'])
assertEndsOfMonths('2023-01-01', '2023-04-30', ['2023-01-31', '2023-02-28', '2023-03-31', '2023-04-30'])
assertEndsOfMonths('2023-01-10', '2023-04-30', ['2023-01-31', '2023-02-28', '2023-03-31', '2023-04-30'])
assertEndsOfMonths('2023-01-10', '2023-04-09', ['2023-01-31', '2023-02-28', '2023-03-31', '2023-04-30'])
def test_deferred_abnormal_dates(self):
"""
Test that we correctly detect abnormal dates.
In the deferred computations, we always assume that both the start and end date are inclusive
E.g: 1st January -> 31st December is *exactly* 1 year = 12 months
However, the user may instead put 1st January -> 1st January of next year which is then
12 months + 1/30 month = 12.03 months which may result in odd amounts when deferrals are created.
This is what we call abnormal dates.
Other cases were the number of months is not round should not be handled and are not considered abnormal.
"""
move = self.create_invoice('in_invoice', [
[self.expense_accounts[0], 0, '2023-01-01', '2023-12-30'],
[self.expense_accounts[0], 1, '2023-01-01', '2023-12-31'],
[self.expense_accounts[0], 2, '2023-01-01', '2024-01-01'],
[self.expense_accounts[0], 3, '2023-01-01', '2024-01-02'],
[self.expense_accounts[0], 4, '2023-01-01', '2024-01-31'],
[self.expense_accounts[0], 5, '2023-01-01', '2024-02-01'],
[self.expense_accounts[0], 6, '2023-01-02', '2024-02-01'],
[self.expense_accounts[0], 7, '2023-01-02', '2024-02-02'],
[self.expense_accounts[0], 8, '2023-01-31', '2024-01-30'],
[self.expense_accounts[0], 9, '2023-01-31', '2024-02-28'], # 29 days in Feb 2024
# Following one is abnormal because we have a full months in February (= 30 accounting days) + 1 day in January
[self.expense_accounts[0], 10, '2023-01-31', '2024-02-29'],
[self.expense_accounts[0], 11, '2023-02-01', '2024-02-29'],
], post=True)
lines = move.invoice_line_ids.sorted('price_unit')
self.assertFalse(lines[0].has_abnormal_deferred_dates)
self.assertFalse(lines[1].has_abnormal_deferred_dates)
self.assertTrue(lines[2].has_abnormal_deferred_dates)
self.assertFalse(lines[3].has_abnormal_deferred_dates)
self.assertFalse(lines[4].has_abnormal_deferred_dates)
self.assertTrue(lines[5].has_abnormal_deferred_dates)
self.assertFalse(lines[6].has_abnormal_deferred_dates)
self.assertTrue(lines[7].has_abnormal_deferred_dates)
self.assertFalse(lines[8].has_abnormal_deferred_dates)
self.assertFalse(lines[9].has_abnormal_deferred_dates)
self.assertTrue(lines[10].has_abnormal_deferred_dates)
self.assertFalse(lines[11].has_abnormal_deferred_dates)
def test_deferred_expense_generate_entries_method(self):
# The deferred entries are NOT generated when the invoice is validated if the method is set to 'manual'.
self.company.generate_deferred_expense_entries_method = 'manual'
move = self.create_invoice('in_invoice', [self.expense_lines[0]], post=True)
self.assertEqual(len(move.deferred_move_ids), 0)
move = self.create_invoice('in_refund', [self.expense_lines[0]], post=True)
self.assertEqual(len(move.deferred_move_ids), 0)
# Test that the deferred entries are generated when the invoice is validated.
self.company.generate_deferred_expense_entries_method = 'on_validation'
move = self.create_invoice('in_invoice', [self.expense_lines[0]], post=True)
self.assertEqual(len(move.deferred_move_ids), 5) # 1 for the invoice deferred + 4 for the deferred entries
move = self.create_invoice('in_refund', [self.expense_lines[0]], post=True)
self.assertEqual(len(move.deferred_move_ids), 5)
# See test_deferred_expense_credit_note for the values
def test_deferred_expense_reset_to_draft(self):
"""
Test that the deferred entries are deleted/reverted when the invoice is reset to draft.
"""
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 1680, '2023-01-21', '2023-04-14')], date='2023-03-15')
self.assertEqual(len(move.deferred_move_ids), 5)
move.button_draft()
self.assertFalse(move.deferred_move_ids)
# With a lock date, we should reverse the moves that cannot be deleted
move.action_post() # Post the move to create the deferred entries with 'on_validation' method
self.assertEqual(len(move.deferred_move_ids), 5)
move.company_id.fiscalyear_lock_date = fields.Date.to_date('2023-02-15')
move.button_draft()
# January deferred entry is in lock period, so it is reversed, not deleted, thus we have one deferred entry and its revert
self.assertEqual(len(move.deferred_move_ids), 2)
self.assertEqual(move.deferred_move_ids[0].date, fields.Date.to_date('2023-01-31'))
self.assertEqual(move.deferred_move_ids[1].date, fields.Date.to_date('2023-02-28'))
# If we repost the move, it should be allowed
move.action_post()
self.assertEqual(len(move.deferred_move_ids), 2 + 5)
@freeze_time('2023-03-15')
def test_deferred_invoice_reset_to_draft_with_audit_trail(self):
"""
Test that the deferred entries in draft are deleted when the invoice is reset to draft
and the posted deferred entries are cancelled.
"""
invoice = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 1680, '2023-02-01', '2023-04-30')], date='2023-03-15')
posted_deferred_entries = invoice.deferred_move_ids.filtered(lambda move: move.state == 'posted')
draft_deferred_move_ids = invoice.deferred_move_ids.filtered(lambda move: move.state =="draft")
self.assertEqual(len(posted_deferred_entries), 2)
self.assertEqual(len(draft_deferred_move_ids), 2)
# Try with restricted mode
self.env.company.restrictive_audit_trail = True
invoice.button_draft()
# Assert that the draft moves no longer exist
remaining_draft_moves = self.env['account.move'].search([('id', 'in', draft_deferred_move_ids.ids)])
self.assertFalse(remaining_draft_moves)
# The posted deferred entries should be cancelled
self.assertEqual(len(posted_deferred_entries.filtered(lambda move: move.state == 'cancel')), 2)
def assert_invoice_lines(self, move, expected_values, source_account, deferred_account):
deferred_moves = move.deferred_move_ids.sorted('date')
for deferred_move, expected_value in zip(deferred_moves, expected_values):
expected_date, expense_line_debit, expense_line_credit, deferred_line_debit, deferred_line_credit = expected_value
self.assertRecordValues(deferred_move, [{
'state': 'posted',
'move_type': 'entry',
'partner_id': self.partner_a.id,
'date': fields.Date.to_date(expected_date),
}])
expense_line = deferred_move.line_ids.filtered(lambda line: line.account_id == source_account)
self.assertRecordValues(expense_line, [
{'debit': expense_line_debit, 'credit': expense_line_credit, 'partner_id': self.partner_a.id},
])
deferred_line = deferred_move.line_ids.filtered(lambda line: line.account_id == deferred_account)
self.assertEqual(deferred_line.debit, deferred_line_debit)
self.assertEqual(deferred_line.credit, deferred_line_credit)
def test_default_tax_on_account_not_on_deferred_entries(self):
"""
Test that the default taxes on an account are not calculated on deferral entries, since this would impact the
tax report.
"""
revenue_account_with_taxes = self.env['account.account'].create({
'name': 'Revenue with Taxes',
'code': 'REVWTAXES',
'account_type': 'income',
'tax_ids': [Command.set(self.tax_sale_a.ids)]
})
move = self.create_invoice(
'out_invoice',
[[revenue_account_with_taxes, 1000, '2023-01-01', '2023-04-30']],
date='2022-12-10'
)
expected_line_values = [
# Date [Line expense] [Line deferred]
('2022-12-10', 1000, 0, 0, 1000),
('2023-01-31', 0, 250, 250, 0),
('2023-02-28', 0, 250, 250, 0),
('2023-03-31', 0, 250, 250, 0),
]
self.assert_invoice_lines(
move,
expected_line_values,
revenue_account_with_taxes,
self.company_data['default_account_deferred_revenue']
)
for deferred_move in move.deferred_move_ids:
# There are no extra lines besides the two lines we checked before
self.assertEqual(len(deferred_move.line_ids), 2)
def test_deferred_values(self):
"""
Test that the debit/credit values are correctly computed, even after a credit note is issued.
"""
expected_line_values1 = [
# Date [Line expense] [Line deferred]
('2022-12-10', 0, 1000, 1000, 0),
('2023-01-31', 250, 0, 0, 250),
('2023-02-28', 250, 0, 0, 250),
('2023-03-31', 250, 0, 0, 250),
]
expected_line_values2 = [
# Date [Line expense] [Line deferred]
('2022-12-10', 1000, 0, 0, 1000),
('2023-01-31', 0, 250, 250, 0),
('2023-02-28', 0, 250, 250, 0),
('2023-03-31', 0, 250, 250, 0),
]
# Vendor bill and credit note
move = self.create_invoice('in_invoice', [self.expense_lines[0]], post=True, date='2022-12-10')
self.assert_invoice_lines(move, expected_line_values1, self.expense_accounts[0], self.company_data['default_account_deferred_expense'])
reverse_move = move._reverse_moves()
self.assert_invoice_lines(reverse_move, expected_line_values2, self.expense_accounts[0], self.company_data['default_account_deferred_expense'])
# Customer invoice and credit note
move2 = self.create_invoice('out_invoice', [self.revenue_lines[0]], post=True, date='2022-12-10')
self.assert_invoice_lines(move2, expected_line_values2, self.revenue_accounts[0], self.company_data['default_account_deferred_revenue'])
reverse_move2 = move2._reverse_moves()
self.assert_invoice_lines(reverse_move2, expected_line_values1, self.revenue_accounts[0], self.company_data['default_account_deferred_revenue'])
def test_deferred_values_rounding(self):
"""
Test that the debit/credit values are correctly computed when values are rounded
"""
# Vendor Bill
expense_line = [self.expense_accounts[0], 500, '2020-08-07', '2020-12-07']
expected_line_values = [
# Date [Line expense] [Line deferred]
('2020-08-07', 0, 500, 500, 0),
('2020-08-31', 99.17, 0, 0, 99.17),
('2020-09-30', 123.97, 0, 0, 123.97),
('2020-10-31', 123.97, 0, 0, 123.97),
('2020-11-30', 123.97, 0, 0, 123.97),
('2020-12-07', 28.92, 0, 0, 28.92),
]
self.assertEqual(self.company.currency_id.round(sum(x[1] for x in expected_line_values)), 500)
move = self.create_invoice('in_invoice', [expense_line], date='2020-08-07')
self.assert_invoice_lines(move, expected_line_values, self.expense_accounts[0], self.company_data['default_account_deferred_expense'])
# Customer invoice
revenue_line = [self.revenue_accounts[0], 500, '2020-08-07', '2020-12-07']
expected_line_values = [
# Date [Line expense] [Line deferred]
('2020-08-07', 500, 0, 0, 500),
('2020-08-31', 0, 99.17, 99.17, 0),
('2020-09-30', 0, 123.97, 123.97, 0),
('2020-10-31', 0, 123.97, 123.97, 0),
('2020-11-30', 0, 123.97, 123.97, 0),
('2020-12-07', 0, 28.92, 28.92, 0),
]
self.assertEqual(self.company.currency_id.round(sum(x[2] for x in expected_line_values)), 500)
move = self.create_invoice('out_invoice', [revenue_line], post=True, date='2020-08-07')
self.assert_invoice_lines(move, expected_line_values, self.revenue_accounts[0], self.company_data['default_account_deferred_revenue'])
def test_deferred_expense_avoid_useless_deferred_entries(self):
"""
If we have an invoice with a start date in the beginning of the month, and an end date in the end of the month,
we should not create the deferred entries because the original invoice will be totally deferred
on the last day of the month, but the full amount will be accounted for on the same day too, thus
cancelling each other. Therefore we should not create the deferred entries. This is only the case
if the invoice date is also inside the deferred period.
"""
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 1680, '2023-01-01', '2023-01-31')], date='2023-01-01')
self.assertEqual(len(move.deferred_move_ids), 0)
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 1680, '2023-01-01', '2023-01-31')], date='2022-01-01')
self.assertEqual(len(move.deferred_move_ids), 2)
def test_deferred_expense_single_period_entries(self):
"""
If we have an invoice covering only one period, we should only avoid creating deferral entries when the
accounting date is the same as the period for the deferral. Otherwise we should still generate a deferral entry.
"""
self.company.deferred_expense_amount_computation_method = 'month'
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 1680, '2023-02-01', '2023-02-28')])
self.assertRecordValues(move.deferred_move_ids, [
{'date': fields.Date.to_date('2023-01-01')},
{'date': fields.Date.to_date('2023-02-28')},
])
def test_taxes_deferred_after_date_added(self):
"""
Test that applicable taxes get deferred also when the dates of the base line are filled in after a first save.
"""
expected_line_values = [
# Date [Line expense] [Line deferred]
('2022-12-10', 0, 100, 100, 0),
('2022-12-10', 0, 1000, 1000, 0),
('2023-01-31', 25, 0, 0, 25),
('2023-01-31', 250, 0, 0, 250),
('2023-02-28', 25, 0, 0, 25),
('2023-02-28', 250, 0, 0, 250),
('2023-03-31', 25, 0, 0, 25),
('2023-03-31', 250, 0, 0, 250),
]
partially_deductible_tax = self.env['account.tax'].create({
'name': 'Partially deductible Tax',
'amount': 20,
'amount_type': 'percent',
'type_tax_use': 'purchase',
'invoice_repartition_line_ids': [
Command.create({'repartition_type': 'base'}),
Command.create({
'factor_percent': 50,
'repartition_type': 'tax',
'use_in_tax_closing': False
}),
Command.create({
'factor_percent': 50,
'repartition_type': 'tax',
'account_id': self.company_data['default_account_tax_purchase'].id,
'use_in_tax_closing': True
}),
],
'refund_repartition_line_ids': [
Command.create({'repartition_type': 'base'}),
Command.create({
'factor_percent': 50,
'repartition_type': 'tax',
'use_in_tax_closing': False
}),
Command.create({
'factor_percent': 50,
'repartition_type': 'tax',
'account_id': self.company_data['default_account_tax_purchase'].id,
'use_in_tax_closing': True
}),
],
})
move = self.env['account.move'].create({
'move_type': 'in_invoice',
'partner_id': self.partner_a.id,
'date': '2022-12-10',
'invoice_date': '2022-12-10',
'journal_id': self.company_data['default_journal_purchase'].id,
'invoice_line_ids': [
Command.create({
'quantity': 1,
'account_id': self.expense_lines[0][0].id,
'price_unit': self.expense_lines[0][1],
'tax_ids': [Command.set(partially_deductible_tax.ids)],
})
]
})
move.invoice_line_ids.write({
'deferred_start_date': self.expense_lines[0][2],
'deferred_end_date': self.expense_lines[0][3],
})
move.action_post()
self.assert_invoice_lines(move, expected_line_values, self.expense_accounts[0], self.company_data['default_account_deferred_expense'])
def test_deferred_tax_key(self):
"""
Test that the deferred tax key is correctly computed.
and is the same between _compute_tax_key and _compute_all_tax
"""
lines = [
[self.expense_accounts[0], 1000, '2023-01-01', '2023-04-30'],
[self.expense_accounts[0], 1000, False, False],
]
move = self.create_invoice('in_invoice', lines, post=True)
original_amount_total = move.amount_total
self.assertEqual(len(move.line_ids.filtered(lambda l: l.display_type == 'tax')), 1)
move.button_draft()
move.action_post()
# The number of tax lines shouldn't change, nor the total amount
self.assertEqual(len(move.line_ids.filtered(lambda l: l.display_type == 'tax')), 1)
self.assertEqual(move.amount_total, original_amount_total)
def test_compute_empty_start_date(self):
"""
Test that the deferred start date is computed when empty and posting the move.
"""
lines = [[self.expense_accounts[0], 1000, False, '2023-04-30']]
move = self.create_invoice('in_invoice', lines, post=False)
# We don't have a deferred date in the beginning
self.assertFalse(move.line_ids[0].deferred_start_date)
move.action_post()
# Deferred start date is set after post
self.assertEqual(move.line_ids[0].deferred_start_date, datetime.date(2023, 1, 1))
move.button_draft()
move.line_ids[0].deferred_start_date = False
move.invoice_date = '2023-02-01'
# Start date is set when changing invoice date
self.assertEqual(move.line_ids[0].deferred_start_date, datetime.date(2023, 2, 1))
move.line_ids[0].deferred_start_date = False
move.line_ids[0].deferred_end_date = '2023-05-31'
# Start date is set when changing deferred end date
self.assertEqual(move.line_ids[0].deferred_start_date, datetime.date(2023, 2, 1))
def test_deferred_on_accounting_date(self):
"""
When we are in `on_validation` mode, the deferral of the total amount should happen on the
accounting date of the move.
"""
move = self.create_invoice(
'in_invoice',
[(self.expense_accounts[0], 1680, '2023-01-01', '2023-02-28')],
date='2023-01-10',
post=False
)
move.date = '2023-01-15'
move.action_post()
self.assertRecordValues(move.deferred_move_ids, [
{'date': fields.Date.to_date('2023-01-15')},
{'date': fields.Date.to_date('2023-01-31')},
{'date': fields.Date.to_date('2023-02-28')},
])
def test_deferred_entries_not_created_on_future_invoice(self):
"""Test that we don't create deferred entries on a future posted invoice"""
tomorrow = fields.Date.to_date(fields.Date.today()) + datetime.timedelta(days=1)
move = self.create_invoice(
'out_invoice',
[(self.expense_accounts[0], 1680, tomorrow, tomorrow + datetime.timedelta(days=100))],
date=tomorrow,
post=False
)
move.auto_post = "at_date"
move._post()
self.assertFalse(move.deferred_move_ids)
with freeze_time(tomorrow), self.enter_registry_test_mode():
self.env.ref('account.ir_cron_auto_post_draft_entry').method_direct_trigger()
self.assertEqual(move.state, 'posted')
self.assertTrue(move.deferred_move_ids)
def test_deferred_entries_created_on_auto_post_invoice(self):
"""Test that deferred entries are created on an invoice with auto_post set to 'at_date'"""
yesterday = fields.Date.to_date(fields.Date.today()) - datetime.timedelta(days=1)
move = self.create_invoice(
'out_invoice',
[(self.expense_accounts[0], 1680, yesterday, yesterday + datetime.timedelta(days=45))],
date=yesterday,
post=False
)
move.auto_post = "at_date"
move._post()
self.assertEqual(move.state, 'posted')
self.assertTrue(move.deferred_move_ids)
def test_deferred_compute_method_full_months(self):
"""
Test that the deferred amount is correctly computed when the new full_months method computation is used
"""
self.company.deferred_expense_amount_computation_method = 'full_months'
dates = (('2024-06-05', '2025-06-04'), ('2024-06-30', '2025-06-29'))
for (date_from, date_to) in dates:
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, date_from, date_to)], date='2024-06-05')
self.assertRecordValues(move.deferred_move_ids.sorted('date'), [
{'date': fields.Date.to_date('2024-06-05'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-06-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-07-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-08-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-09-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-10-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-11-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-12-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-01-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-02-28'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-03-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-04-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-05-31'), 'amount_total': 1000},
# 0 for June 2025, so no move created
])
# Start of month <=> Equal per month method
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-07-01', '2025-06-30')], date='2024-07-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-07-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-07-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-08-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-09-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-10-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-11-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-12-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-01-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-02-28'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-03-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-04-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-05-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-06-30'), 'amount_total': 1000},
])
# Nothing to defer, everything is in the same month
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-01-01', '2024-01-16')], date='2024-01-01')
self.assertFalse(move.deferred_move_ids)
# Round period of 2 months -> Divide by 2
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-01-01', '2024-02-29')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-01-31'), 'amount_total': 6000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 6000},
])
# Round period of 2 months -> Divide by 2
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-01-15', '2024-03-14')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-01-31'), 'amount_total': 6000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 6000},
])
# Period of exactly one month: full amount should be in Jan. So we revert 1st Jan, and account for 31st Jan <=> don't generate anything
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-01-15', '2024-02-14')], date='2024-01-01')
self.assertFalse(move.deferred_move_ids)
# Not-round period of 1.5 month with only one end of month in January (same explanation as above)
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-01-01', '2024-02-15')], date='2024-01-01')
self.assertFalse(move.deferred_move_ids)
# Not-round period of 1.5+ month with only one end of month in January (same explanation as above)
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-01-05', '2024-02-15')], date='2024-01-01')
self.assertFalse(move.deferred_move_ids)
# Period of exactly one month: full amount should be in Feb. So we revert 1st Jan, and account for all on 29th Feb.
# Deferrals are in different months for this case, so the deferrals should be generated.
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-02-15', '2024-03-14')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 12000},
])
# Not-round period of 1.5+ month: full amount should be in Feb. So we revert 1st Jan, and account for all on 29th Feb.
# Deferrals are in different months for this case, so the deferrals should be generated.
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-02-05', '2024-03-15')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 12000},
])
# Not-round period of 1.5 month with 2 ends of months, so divide balance by 2
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-01-16', '2024-02-29')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-01-31'), 'amount_total': 6000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 6000},
])
# Not-round period of 2.5 month, with 3 ends of months, so divide balance by 3
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], 12000, '2024-01-16', '2024-03-31')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-01-31'), 'amount_total': 4000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 4000},
{'date': fields.Date.to_date('2024-03-31'), 'amount_total': 4000},
])
def test_deferred_compute_method_full_months_revenue(self):
"""
Test that the deferred amount is correctly computed when the new full_months method computation is used
"""
self.company.deferred_revenue_amount_computation_method = 'full_months'
dates = (('2024-06-05', '2025-06-04'), ('2024-06-30', '2025-06-29'))
for (date_from, date_to) in dates:
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, date_from, date_to)], date='2024-06-05')
self.assertRecordValues(move.deferred_move_ids.sorted('date'), [
{'date': fields.Date.to_date('2024-06-05'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-06-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-07-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-08-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-09-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-10-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-11-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-12-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-01-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-02-28'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-03-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-04-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-05-31'), 'amount_total': 1000},
# 0 for June 2025, so no move created
])
# Start of month <=> Equal per month method
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-07-01', '2025-06-30')], date='2024-07-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-07-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-07-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-08-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-09-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-10-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-11-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2024-12-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-01-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-02-28'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-03-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-04-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-05-31'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-06-30'), 'amount_total': 1000},
])
# Nothing to defer, everything is in the same month
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-01-01', '2024-01-16')], date='2024-01-01')
self.assertFalse(move.deferred_move_ids)
# Round period of 2 months -> Divide by 2
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-01-01', '2024-02-29')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-01-31'), 'amount_total': 6000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 6000},
])
# Round period of 2 months -> Divide by 2
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-01-15', '2024-03-14')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-01-31'), 'amount_total': 6000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 6000},
])
# Period of exactly one month: full amount should be in Jan. So we revert 1st Jan, and account for 31st Jan <=> don't generate anything
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-01-15', '2024-02-14')], date='2024-01-01')
self.assertFalse(move.deferred_move_ids)
# Not-round period of 1.5 month with only one end of month in January (same explanation as above)
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-01-01', '2024-02-15')], date='2024-01-01')
self.assertFalse(move.deferred_move_ids)
# Not-round period of 1.5+ month with only one end of month in January (same explanation as above)
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-01-05', '2024-02-15')], date='2024-01-01')
self.assertFalse(move.deferred_move_ids)
# Period of exactly one month: full amount should be in Feb. So we revert 1st Jan, and account for all on 29th Feb.
# Deferrals are in different months for this case, so the deferrals should be generated.
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-02-15', '2024-03-14')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 12000},
])
# Not-round period of 1.5+ month: full amount should be in Feb. So we revert 1st Jan, and account for all on 29th Feb.
# Deferrals are in different months for this case, so the deferrals should be generated.
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-02-05', '2024-03-15')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 12000},
])
# Not-round period of 1.5 month with 2 ends of months, so divide balance by 2
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-01-16', '2024-02-29')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-01-31'), 'amount_total': 6000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 6000},
])
# Not-round period of 2.5 month, with 3 ends of months, so divide balance by 3
move = self.create_invoice('out_invoice', [(self.revenue_accounts[0], 12000, '2024-01-16', '2024-03-31')], date='2024-01-01')
self.assertRecordValues(move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total)), [
{'date': fields.Date.to_date('2024-01-01'), 'amount_total': 12000},
{'date': fields.Date.to_date('2024-01-31'), 'amount_total': 4000},
{'date': fields.Date.to_date('2024-02-29'), 'amount_total': 4000},
{'date': fields.Date.to_date('2024-03-31'), 'amount_total': 4000},
])
def test_deferral_moves_not_removed(self):
"""
Test that when there are multiple amls with the same price on the original invoice the deferral_moves are not removed
"""
move = self.create_invoice('in_invoice', [
(self.expense_accounts[0], 1000, '2025-05-10', '2025-05-25'),
(self.expense_accounts[0], 1000, '2025-05-10', '2025-05-25')
], date='2025-04-11')
self.assertEqual(len(move.deferred_move_ids), 4)
def test_case_1_deferred_entries_computations_period_across_months(self):
"""
Tests deferred expense recognition when the invoice is dated in a month
after the service period has already begun.
Corresponds to Case from Row 6 of the user's image.
- Service Period: June 25, 2024 -> July 7, 2024 (13 days)
- Invoice Date: July 1, 2024
- Expected: The system should correctly create a back-dated expense entry for
the 6 days in June (dated June 30) and an entry for the 7 days in July
(dated July 7).
"""
invoice_date = '2024-07-01'
invoice_line_data = [self.expense_accounts[0], 1300, '2024-06-25', '2024-07-07']
expected_line_values = [
('2024-06-30', 600, 0, 0, 600),
(invoice_date, 0, 1300, 1300, 0),
('2024-07-07', 700, 0, 0, 700),
]
move = self.create_invoice(
'in_invoice', [invoice_line_data], date=invoice_date
)
self.assert_invoice_lines(
move,
expected_line_values,
self.expense_accounts[0],
self.company_data['default_account_deferred_expense']
)
def test_case_2_deferred_entries_computations_period_across_months(self):
"""
Tests deferred expense recognition when the invoice is dated within the
service period, which spans across two months.
Corresponds to Case from Row 7 of the user's image.
- Service Period: June 25, 2024 -> July 7, 2024 (13 days)
- Invoice Date: June 29, 2024
- Expected: The system should correctly prorate the expense with 6 days
in June and 7 days in July.
"""
invoice_date = '2024-06-29'
invoice_line_data = [self.expense_accounts[0], 1300, '2024-06-25', '2024-07-07']
expected_line_values = [
(invoice_date, 0, 1300, 1300, 0),
('2024-06-30', 600, 0, 0, 600),
('2024-07-07', 700, 0, 0, 700),
]
move = self.create_invoice(
'in_invoice', [invoice_line_data], date=invoice_date
)
self.assert_invoice_lines(
move,
expected_line_values,
self.expense_accounts[0],
self.company_data['default_account_deferred_expense']
)
def test_case_3_deferred_entries_computations_period_across_months(self):
"""
Tests deferred expense recognition for a longer service period that spans
across two months, with the invoice dated within the first month.
Corresponds to Case from Row 8 in the user's image.
- Service Period: June 25, 2024 -> July 23, 2024 (29 days)
- Invoice Date: June 29, 2024
- Expected: The system should correctly split the expense between June
(6/29 of total) and July (23/29 of total).
"""
invoice_date = '2024-06-29'
# Using 2900 for the amount to keep the numbers clean
invoice_line_data = [self.expense_accounts[0], 2900, '2024-06-25', '2024-07-23']
expected_line_values = [
(invoice_date, 0, 2900, 2900, 0),
('2024-06-30', 600, 0, 0, 600),
('2024-07-23', 2300, 0, 0, 2300),
]
move = self.create_invoice(
'in_invoice', [invoice_line_data], date=invoice_date
)
self.assert_invoice_lines(
move,
expected_line_values,
self.expense_accounts[0],
self.company_data['default_account_deferred_expense']
)
def test_deferred_moves_from_same_move_different_lines(self):
"""
Test that fully deferred move and deferral move from different lines are not cancelling each other
when having the same amount.
"""
move = self.create_invoice('in_invoice', [(self.expense_accounts[0], amount, '2025-10-01', '2025-11-30') for amount in (1000, 500)], date='2025-11-30')
self.assertRecordValues(move.deferred_move_ids.sorted('date'), [
{'date': fields.Date.to_date('2025-10-31'), 'amount_total': 500},
{'date': fields.Date.to_date('2025-10-31'), 'amount_total': 250},
{'date': fields.Date.to_date('2025-11-30'), 'amount_total': 1000},
{'date': fields.Date.to_date('2025-11-30'), 'amount_total': 500},
{'date': fields.Date.to_date('2025-11-30'), 'amount_total': 500},
{'date': fields.Date.to_date('2025-11-30'), 'amount_total': 250},
])
def test_deferred_misc(self):
"""
Now that we allow deferred on misc entries, we need to make sure that the generated deferrals
entries created are in the right journal/account.
Before, the journal/account was decided based on the move type which was either purchase or sale.
Now, different deferred lines in a single misc move can have different deferral accounts/journals
as the account type is per line, and not per move.
"""
deferred_move = self.create_invoice('entry', [
(self.revenue_accounts[0], 30, '2025-01-01', '2025-03-31'),
(self.expense_accounts[0], 300, '2025-01-01', '2025-03-31'),
], date='2025-01-01')
deferral_moves = deferred_move.deferred_move_ids.sorted(lambda m: (m.date, m.amount_total))
self.assertEqual(len(deferral_moves), 8) # 4 deferrals for expense (Reverse + 3 months), same for revenue
# First the 2 reverse moves
self.assertRecordValues(deferral_moves[:2].line_ids.sorted('balance'), [
{'date': fields.Date.to_date('2025-01-01'), 'balance': -300, 'account_id': self.expense_accounts[0].id, 'journal_id': self.company.deferred_expense_journal_id.id},
{'date': fields.Date.to_date('2025-01-01'), 'balance': -30, 'account_id': self.revenue_accounts[0].id, 'journal_id': self.company.deferred_revenue_journal_id.id},
{'date': fields.Date.to_date('2025-01-01'), 'balance': 30, 'account_id': self.company.deferred_revenue_account_id.id, 'journal_id': self.company.deferred_revenue_journal_id.id},
{'date': fields.Date.to_date('2025-01-01'), 'balance': 300, 'account_id': self.company.deferred_expense_account_id.id, 'journal_id': self.company.deferred_expense_journal_id.id},
])
# Monthly deferrals
for i, date in enumerate(['2025-01-31', '2025-02-28', '2025-03-31']):
self.assertRecordValues(deferral_moves[2 + 2 * i : 2 + 2 * (i + 1)].line_ids.sorted('balance'), [
{'date': fields.Date.to_date(date), 'balance': -100, 'account_id': self.company.deferred_expense_account_id.id, 'journal_id': self.company.deferred_expense_journal_id.id},
{'date': fields.Date.to_date(date), 'balance': -10, 'account_id': self.company.deferred_revenue_account_id.id, 'journal_id': self.company.deferred_revenue_journal_id.id},
{'date': fields.Date.to_date(date), 'balance': 10, 'account_id': self.revenue_accounts[0].id, 'journal_id': self.company.deferred_revenue_journal_id.id},
{'date': fields.Date.to_date(date), 'balance': 100, 'account_id': self.expense_accounts[0].id, 'journal_id': self.company.deferred_expense_journal_id.id},
])
def test_deferred_misc_diff_accounts_diff_methods(self):
"""
Having different deferred entries generation methods for expenses and revenues is not
supported on journal entries involving both expense and revenue accounts. User should split
the entry into two entries instead.
"""
self.company.generate_deferred_expense_entries_method = 'on_validation'
self.company.generate_deferred_revenue_entries_method = 'manual'
deferred_move = self.create_invoice('entry', [
(self.revenue_accounts[0], 30, '2025-01-01', '2025-03-31'),
(self.expense_accounts[0], 300, '2025-01-01', '2025-03-31'),
], date='2025-01-01', post=False)
with self.assertRaisesRegex(UserError, r"Having different deferred entries generation methods for expenses and revenues is not supported..."):
deferred_move.action_post()
def test_misc_entry_no_deferred_dates_with_diff_methods(self):
"""
Misc entries with expense and revenue accounts but NO deferred dates should
post successfully even when different generation methods are configured.
"""
self.company.generate_deferred_expense_entries_method = 'on_validation'
self.company.generate_deferred_revenue_entries_method = 'manual'
move = self.env['account.move'].create({
'move_type': 'entry',
'date': '2025-01-01',
'journal_id': self.company_data['default_journal_misc'].id,
'line_ids': [
Command.create({'account_id': self.expense_accounts[0].id, 'balance': 100}),
Command.create({'account_id': self.revenue_accounts[0].id, 'balance': -100}),
],
})
move.action_post()
self.assertEqual(move.state, 'posted')
def test_deferred_move_lines_partner(self):
""" Test the partner is consistent in all lines of deferred moves """
invoice = self._create_invoice(
invoice_date='2026-04-15',
partner_id=self.partner_a,
partner_shipping_id=self.partner_b,
invoice_line_ids=[self._prepare_invoice_line(price_unit=1000, deferred_start_date='2026-03-01', deferred_end_date='2026-04-30')],
)
# The invoice view can be open with the `default_partner_id` context key, like in odoo.addons.sale.models.sale_order.SaleOrder.action_view_invoice
# Leading to inconsistencies between lines partner_id in the deferred moves
invoice.with_context(default_partner_id=self.partner_b).action_post()
self.assertEqual(invoice.deferred_move_ids.line_ids.mapped('partner_id'), self.partner_a)