422 lines
21 KiB
Python
422 lines
21 KiB
Python
from datetime import timedelta
|
|
|
|
from freezegun import freeze_time
|
|
|
|
from odoo import Command, fields
|
|
from odoo.addons.account.models.company import SOFT_LOCK_DATE_FIELDS
|
|
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
|
from odoo.exceptions import UserError
|
|
from odoo.tests import new_test_user, tagged
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestAccountLockException(AccountTestInvoicingCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
cls.fakenow = cls.env.cr.now()
|
|
cls.startClassPatcher(freeze_time(cls.fakenow))
|
|
|
|
cls.other_user = new_test_user(
|
|
cls.env,
|
|
name='Other User',
|
|
login='other_user',
|
|
password='password',
|
|
email='other_user@example.com',
|
|
groups_id=cls.get_default_groups().ids,
|
|
company_id=cls.env.company.id,
|
|
)
|
|
|
|
cls.company_data_2 = cls.setup_other_company()
|
|
|
|
cls.soft_lock_date_info = [
|
|
('fiscalyear_lock_date', 'out_invoice'),
|
|
('tax_lock_date', 'out_invoice'),
|
|
('sale_lock_date', 'out_invoice'),
|
|
('purchase_lock_date', 'in_invoice'),
|
|
]
|
|
|
|
def test_user_exception_move_edit_multi_user(self):
|
|
"""
|
|
Test that an exception for a specific user only works for that user.
|
|
"""
|
|
for lock_date_field, move_type in self.soft_lock_date_info:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
move = self.init_invoice(move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
|
|
# Lock the move
|
|
self.company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
# Add an exception to make the move editable (for the current user)
|
|
self.env['account.lock_exception'].create({
|
|
'company_id': self.company.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_user_exception_move_edit_multi_user',
|
|
})
|
|
move.button_draft()
|
|
move.action_post()
|
|
|
|
# Check that the exception does not apply to other users
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.with_user(self.other_user).button_draft()
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|
|
|
|
def test_global_exception_move_edit_multi_user(self):
|
|
"""
|
|
Test that an exception without a specified user works for any user.
|
|
"""
|
|
for lock_date_field, move_type in self.soft_lock_date_info:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
move = self.init_invoice(move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
|
|
# Lock the move
|
|
self.company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
# Add a global exception to make the move editable for everyone
|
|
self.env['account.lock_exception'].create({
|
|
'company_id': self.company.id,
|
|
'user_id': False,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_global_exception_move_edit_multi_user',
|
|
})
|
|
|
|
move.button_draft()
|
|
move.action_post()
|
|
|
|
move.with_user(self.other_user).button_draft()
|
|
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|
|
|
|
def test_user_exception_branch(self):
|
|
"""
|
|
Test that the locking and exception mechanism works correctly in company hierarchies.
|
|
* A lock in the branch does not lock the parent.
|
|
* A lock in the parent also locks the branch.
|
|
* An exception in the branch does not matter for the lock in the parent.
|
|
* Let both parent and branch be locked.
|
|
To make changes in the locked period in the brranch we need exceptions in both companies.
|
|
"""
|
|
|
|
root_company = self.company_data['company']
|
|
root_company.write({'child_ids': [Command.create({'name': 'branch'})]})
|
|
self.cr.precommit.run() # load the CoA
|
|
branch = root_company.child_ids
|
|
|
|
for lock_date_field, move_type in self.soft_lock_date_info:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
# Create a move in the branch
|
|
branch_move = self.init_invoice(
|
|
move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a, company=branch,
|
|
)
|
|
|
|
# Create a move in the parent company
|
|
root_move = self.init_invoice(
|
|
move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a, company=root_company,
|
|
)
|
|
|
|
# Lock the branch
|
|
branch[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
|
|
# The branch_move is locked while the root_move is not
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
branch_move.button_draft()
|
|
root_move.button_draft()
|
|
root_move.action_post()
|
|
|
|
# Add an exception in the branch to make the branch_move editable (for the current user)
|
|
self.env['account.lock_exception'].create({
|
|
'company_id': branch.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_user_exception_branch branch exception',
|
|
})
|
|
branch_move.button_draft()
|
|
branch_move.action_post()
|
|
|
|
# Lock the parent company
|
|
root_company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
|
|
# Check that both moves are locked now (the branch exception alone is insufficient)
|
|
for move in [branch_move, root_move]:
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
# Add an exception in the parent company to make both moves editable (for the current user)
|
|
self.env['account.lock_exception'].create({
|
|
'company_id': root_company.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_user_exception_branch root_company exception',
|
|
})
|
|
for move in [branch_move, root_move]:
|
|
move.button_draft()
|
|
move.action_post()
|
|
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|
|
|
|
def test_user_exception_wrong_company(self):
|
|
"""
|
|
Test that an exception only works for the specified company.
|
|
"""
|
|
for lock_date_field, move_type in self.soft_lock_date_info:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
move = self.init_invoice(move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
# Lock the move
|
|
self.company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
# Add an exception for another company
|
|
self.env['account.lock_exception'].create({
|
|
'company_id': self.company_data_2['company'].id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_user_exception_move_edit_multi_user',
|
|
})
|
|
|
|
# Check that the exception is insufficient
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|
|
|
|
def test_user_exception_insufficient(self):
|
|
"""
|
|
Test that the exception only works if the specified lock date is actually before the accounting date.
|
|
"""
|
|
for lock_date_field, move_type in self.soft_lock_date_info:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
move = self.init_invoice(move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
|
|
# Lock the move
|
|
self.company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
# Add an exception before the lock date but not before the date of the test_invoice
|
|
self.env['account.lock_exception'].create({
|
|
'company_id': self.company.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2016-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_user_exception_move_edit_multi_user',
|
|
})
|
|
|
|
# Check that the exception is insufficient
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|
|
|
|
def test_expired_exception(self):
|
|
"""
|
|
Test that the exception does not work if we are past the `end_datetime` of the exception.
|
|
"""
|
|
for lock_date_field, move_type in self.soft_lock_date_info:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
move = self.init_invoice(move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
|
|
# Lock the move
|
|
self.company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
# Add an expired exception
|
|
self.env['account.lock_exception'].create({
|
|
'company_id': self.company.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'create_date': self.fakenow - timedelta(hours=24),
|
|
'end_datetime': self.fakenow - timedelta(milliseconds=1),
|
|
'reason': 'test_expired_exception',
|
|
})
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|
|
|
|
def test_revoked_exception(self):
|
|
for lock_date_field, move_type in self.soft_lock_date_info:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
move = self.init_invoice(move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
|
|
# Lock the move
|
|
self.company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
# Add an exception to make the move editable (for the current user)
|
|
exception = self.env['account.lock_exception'].create({
|
|
'company_id': self.company.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_user_exception_move_edit_multi_user',
|
|
})
|
|
move.button_draft()
|
|
move.action_post()
|
|
|
|
exception.action_revoke()
|
|
|
|
# Check that the exception does not work anymore
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|
|
|
|
def test_user_exception_wrong_field(self):
|
|
for lock_date_field, move_type, exception_lock_date_field in [
|
|
('fiscalyear_lock_date', 'out_invoice', 'tax_lock_date'),
|
|
('tax_lock_date', 'out_invoice', 'fiscalyear_lock_date'),
|
|
('sale_lock_date', 'out_invoice', 'purchase_lock_date'),
|
|
('purchase_lock_date', 'in_invoice', 'sale_lock_date'),
|
|
]:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
move = self.init_invoice(move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
# Lock the move
|
|
self.company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
# Add an exception for a different lock date field
|
|
self.env['account.lock_exception'].create({
|
|
'company_id': self.company_data_2['company'].id,
|
|
'user_id': self.env.user.id,
|
|
exception_lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_user_exception_wrong_field',
|
|
})
|
|
|
|
# Check that the exception is insufficient
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|
|
|
|
def test_hard_lock_date(self):
|
|
"""
|
|
Test that
|
|
* exceptions (for other lock date fields) do not allow bypassing the hard lock date
|
|
* the hard lock date cannot be decreased or removed
|
|
"""
|
|
in_move = self.init_invoice('in_invoice', invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
out_move = self.init_invoice('out_invoice', invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
|
|
self.company.hard_lock_date = fields.Date.to_date('2020-01-01')
|
|
|
|
# Check that we cannot remove the hard lock date.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.company.hard_lock_date = False
|
|
|
|
# Check that we cannot decrease the hard lock date.
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
self.company.hard_lock_date = fields.Date.to_date('2019-01-01')
|
|
|
|
# Create exceptions for all lock date fields except the hard lock date
|
|
self.env['account.lock_exception'].create([
|
|
{
|
|
'company_id': self.company_data_2['company'].id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': f'test_hard_lock_ignores_exceptions {lock_date_field}',
|
|
}
|
|
for lock_date_field in SOFT_LOCK_DATE_FIELDS
|
|
])
|
|
|
|
# Check that the exceptions are insufficient
|
|
for move in [in_move, out_move]:
|
|
with self.assertRaises(UserError), self.cr.savepoint():
|
|
move.button_draft()
|
|
|
|
def test_company_lock_date(self):
|
|
"""
|
|
Test the `company_lock_date` field is set corretly on exception creation.
|
|
Test the behavior when a company lock date is changed.
|
|
* Every active exception gets revoked and recreated with the new company lock date
|
|
* Non-active exceptions are not affected
|
|
"""
|
|
self.env['account.lock_exception'].search([]).sudo().unlink()
|
|
for lock_date_field, move_type in self.soft_lock_date_info:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
self.company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
|
|
revoked_exception = self.env['account.lock_exception'].create({
|
|
'company_id': self.company.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_exception_recreated_on_lock_date_change revoked',
|
|
})
|
|
revoked_exception.action_revoke()
|
|
active_exception = self.env['account.lock_exception'].create({
|
|
'company_id': self.company.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_exception_recreated_on_lock_date_change active',
|
|
})
|
|
|
|
# Check that the company lock date field was set correcyly on exception creation
|
|
self.assertEqual(revoked_exception.company_lock_date, fields.Date.to_date('2020-01-01'))
|
|
self.assertEqual(active_exception.company_lock_date, fields.Date.to_date('2020-01-01'))
|
|
|
|
# The lock date change should trigger the "recreation" proces
|
|
self.company[lock_date_field] = fields.Date.to_date('2021-01-01')
|
|
|
|
self.assertEqual(revoked_exception.company_lock_date, fields.Date.to_date('2020-01-01'))
|
|
|
|
self.assertEqual(active_exception.state, 'revoked')
|
|
|
|
exceptions = self.env['account.lock_exception'].with_context(active_test=False).search([])
|
|
self.assertEqual(len(exceptions), 3)
|
|
new_exception = exceptions - revoked_exception - active_exception
|
|
# Check that the new exception is a "recreation" of the `active_exception`
|
|
self.assertRecordValues(new_exception, [{
|
|
'company_id': self.company.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: fields.Date.to_date('2010-01-01'),
|
|
'company_lock_date': fields.Date.to_date('2021-01-01'),
|
|
'end_datetime': self.env.cr.now() + timedelta(hours=24),
|
|
'reason': 'test_exception_recreated_on_lock_date_change active',
|
|
}])
|
|
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|
|
|
|
def test_user_exception_remove_lock_date(self):
|
|
"""
|
|
Test that an exception removing a lock date (instead of just decreasing it) works.
|
|
"""
|
|
for lock_date_field, move_type in self.soft_lock_date_info:
|
|
with self.subTest(lock_date_field=lock_date_field, move_type=move_type), self.cr.savepoint() as sp:
|
|
move = self.init_invoice(move_type, invoice_date='2016-01-01', post=True, amounts=[1000.0], taxes=self.tax_sale_a)
|
|
|
|
# Lock the move
|
|
self.company[lock_date_field] = fields.Date.to_date('2020-01-01')
|
|
with self.assertRaises(UserError):
|
|
move.button_draft()
|
|
|
|
# Add an exception removing the lock date
|
|
self.env['account.lock_exception'].create({
|
|
'company_id': self.company.id,
|
|
'user_id': self.env.user.id,
|
|
lock_date_field: False,
|
|
'end_datetime': self.fakenow + timedelta(hours=24),
|
|
'reason': 'test_user_exception_move_edit_multi_user',
|
|
})
|
|
move.button_draft()
|
|
|
|
sp.close() # Rollback to ensure all subtests start in the same situation
|