[IMP] account: add tax lock date
Purpose:
Journal entry can be changed the way we want while the lock date is before then entry date..
Entry : Draft -> Validated (or posted)
Specs:
add a date field in the accounting settings: tax lock date
implement a constraint ensuring that no one can modify the information related to tax on account.move.line prior to this lock date. That means the debit, credit, tax_ids, tax_line_id fields. Other fields can be modified. This also means we cannot create/delete an account.move.line with tax information prior to this lock date.
Task 1914762
closes odoo/odoo#29272
Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
This commit is contained in:
@@ -62,13 +62,13 @@ class AccountMove(models.Model):
|
||||
move.matched_percentage = 1.0
|
||||
else:
|
||||
move.matched_percentage = total_reconciled / total_amount
|
||||
|
||||
|
||||
@api.multi
|
||||
@api.depends('line_ids.reconcile_model_id')
|
||||
def _compute_reconcile_model(self):
|
||||
for move in self:
|
||||
move.reconcile_model_id = move.line_ids.mapped('reconcile_model_id')
|
||||
|
||||
|
||||
@api.model
|
||||
@api.depends('reconcile_model_id')
|
||||
def _search_reconcile_model(self, operator, operand):
|
||||
@@ -688,6 +688,22 @@ class AccountMoveLine(models.Model):
|
||||
rec.update({'credit': balance})
|
||||
return rec
|
||||
|
||||
@api.multi
|
||||
@api.constrains('tax_ids', 'tax_line_id')
|
||||
def _check_tax_lock_date1(self):
|
||||
for line in self:
|
||||
if line.date <= (line.company_id.tax_lock_date or date.min):
|
||||
raise ValidationError(_("The operation is refused as it would impact an already issued tax statement. " +
|
||||
"Please change the journal entry date or the tax lock date set in the settings ({}) to proceed").format(line.company_id.tax_lock_date or date.min))
|
||||
|
||||
@api.multi
|
||||
@api.constrains('credit', 'debit', 'date')
|
||||
def _check_tax_lock_date2(self):
|
||||
for line in self:
|
||||
if (line.tax_ids or line.tax_line_id) and line.date <= (line.company_id.tax_lock_date or date.min):
|
||||
raise ValidationError(_("The operation is refused as it would impact an already issued tax statement. " +
|
||||
"Please change the journal entry date or the tax lock date set in the settings ({}) to proceed").format(line.company_id.tax_lock_date or date.min))
|
||||
|
||||
@api.multi
|
||||
@api.constrains('currency_id', 'account_id')
|
||||
def _check_currency(self):
|
||||
@@ -1174,6 +1190,7 @@ class AccountMoveLine(models.Model):
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
self._update_check()
|
||||
self._check_tax_lock_date2()
|
||||
move_ids = set()
|
||||
for line in self:
|
||||
if line.move_id.id not in move_ids:
|
||||
@@ -1222,8 +1239,6 @@ class AccountMoveLine(models.Model):
|
||||
move_ids = set()
|
||||
for line in self:
|
||||
err_msg = _('Move name (id): %s (%s)') % (line.move_id.name, str(line.move_id.id))
|
||||
if line.move_id.state != 'draft':
|
||||
raise UserError(_('You cannot do this modification on a posted journal entry, you can just change some non legal fields. You must revert the journal entry to cancel it.\n%s.') % err_msg)
|
||||
if line.reconciled and not (line.debit == 0 and line.credit == 0):
|
||||
raise UserError(_('You cannot do this modification on a reconciled entry. You can just change some non legal fields or you must unreconcile first.\n%s.') % err_msg)
|
||||
if line.move_id.id not in move_ids:
|
||||
|
||||
@@ -39,6 +39,7 @@ class ResCompany(models.Model):
|
||||
fiscalyear_last_month = fields.Selection(MONTH_SELECTION, default='12', required=True)
|
||||
period_lock_date = fields.Date(string="Lock Date for Non-Advisers", help="Only users with the 'Adviser' role can edit accounts prior to and inclusive of this date. Use it for period locking inside an open fiscal year, for example.")
|
||||
fiscalyear_lock_date = fields.Date(string="Lock Date", help="No users, including Advisers, can edit accounts prior to and inclusive of this date. Use it for fiscal year locking for example.")
|
||||
tax_lock_date = fields.Date("Tax Lock Date", help="No users can edit journal entries related to a tax prior and inclusive of this date.")
|
||||
transfer_account_id = fields.Many2one('account.account',
|
||||
domain=lambda self: [('reconcile', '=', True), ('user_type_id.id', '=', self.env.ref('account.data_account_type_current_assets').id), ('deprecated', '=', False)], string="Inter-Banks Transfer Account", help="Intermediary account used when moving money from a liquidity account to another")
|
||||
expects_chart_of_accounts = fields.Boolean(string='Expects a Chart of Accounts', default=True)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
from . import test_account_customer_invoice
|
||||
from . import test_account_move_closed_period
|
||||
from . import test_account_move_tax_lock_date
|
||||
from . import test_account_supplier_invoice
|
||||
from . import test_account_validate_account_move
|
||||
from . import test_account_invoice_rounding
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
from odoo.addons.account.tests.account_test_classes import AccountingTestCase
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from calendar import monthrange
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestTaxBlockDate(AccountingTestCase):
|
||||
"""
|
||||
Forbid creation, edition and deletion of Journal Items related to taxes with
|
||||
a date prior to the Tax Block Date.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestTaxBlockDate, self).setUp()
|
||||
self.user_id = self.env.user
|
||||
|
||||
last_day_month = datetime.now()
|
||||
last_day_month = last_day_month.replace(day=monthrange(last_day_month.year, last_day_month.month)[1])
|
||||
self.last_day_month_str = last_day_month.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
first_day_month = datetime.now()
|
||||
first_day_month = first_day_month.replace(day=1)
|
||||
self.first_day_month_str = first_day_month.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
middle_day_month = datetime.now()
|
||||
middle_day_month = middle_day_month.replace(day=15)
|
||||
self.middle_day_month_str = middle_day_month.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
|
||||
self.sale_journal_id = self.env['account.journal'].search([('type', '=', 'sale')], limit=1)[0]
|
||||
self.account_id = self.env['account.account'].search([('internal_type', '=', 'receivable')], limit=1)[0]
|
||||
self.other_account_id = self.env['account.account'].search([('internal_type', '!=', 'receivable')], limit=1)[0]
|
||||
self.tax_id = self.env['account.tax'].search([], limit=1)[0]
|
||||
|
||||
self.move = {
|
||||
'name': '/',
|
||||
'journal_id': self.sale_journal_id.id,
|
||||
'date': self.middle_day_month_str,
|
||||
'line_ids': [(0, 0, {
|
||||
'name': 'foo',
|
||||
'debit': 10,
|
||||
'account_id': self.account_id.id,
|
||||
'tax_ids': [(6, False, [self.tax_id.id])]
|
||||
}), (0, 0, {
|
||||
'name': 'bar',
|
||||
'credit': 10,
|
||||
'account_id': self.account_id.id,
|
||||
})]
|
||||
}
|
||||
|
||||
def test_create_before_block_date(self):
|
||||
"""
|
||||
Checks that you cannot create an account.move with a date before the tax
|
||||
lock date
|
||||
"""
|
||||
self.user_id.company_id.tax_lock_date = self.last_day_month_str
|
||||
with self.assertRaises(ValidationError):
|
||||
move = self.env['account.move'].create(self.move)
|
||||
move.post()
|
||||
|
||||
def test_change_after_block_date(self):
|
||||
"""
|
||||
Checks that you can change an account.move with a date after the tax
|
||||
lock date
|
||||
"""
|
||||
self.user_id.company_id.tax_lock_date = self.first_day_month_str
|
||||
move = self.env['account.move'].create(self.move)
|
||||
move.line_ids[0].write({'account_id': self.other_account_id.id})
|
||||
move.line_ids[1].write({'account_id': self.other_account_id.id})
|
||||
move.line_ids[1].write({'debit': 11})
|
||||
move.line_ids[0].write({'credit': 11})
|
||||
move.post()
|
||||
move.line_ids[1].write({'tax_ids': [(5, False, False)]})
|
||||
|
||||
def test_change_before_block_date(self):
|
||||
"""
|
||||
Checks that you cannot change an account.move with a date before the tax
|
||||
lock date
|
||||
"""
|
||||
self.user_id.company_id.tax_lock_date = self.first_day_month_str
|
||||
move = self.env['account.move'].create(self.move)
|
||||
self.user_id.company_id.tax_lock_date = self.last_day_month_str
|
||||
move.line_ids[0].write({'account_id': self.other_account_id.id})
|
||||
move.line_ids[1].write({'account_id': self.other_account_id.id})
|
||||
with self.assertRaises(ValidationError):
|
||||
with self.cr.savepoint():
|
||||
move.line_ids[1].write({'debit': 11})
|
||||
with self.assertRaises(ValidationError):
|
||||
with self.cr.savepoint():
|
||||
move.line_ids[1].write({'date': self.last_day_month_str, 'tax_ids': [(5, False, False)]})
|
||||
move.line_ids[0].write({'credit': 10})
|
||||
move.post()
|
||||
|
||||
def test_unlink_before_block_date(self):
|
||||
"""
|
||||
Checks that you cannot unlink an account.move with a date before the tax
|
||||
lock date
|
||||
"""
|
||||
self.user_id.company_id.tax_lock_date = self.first_day_month_str
|
||||
move = self.env['account.move'].create(self.move)
|
||||
move.post()
|
||||
self.user_id.company_id.tax_lock_date = self.last_day_month_str
|
||||
with self.assertRaises(ValidationError):
|
||||
move.unlink()
|
||||
|
||||
def test_unlink_after_block_date(self):
|
||||
"""
|
||||
Checks that you can unlink an account.move with a date after the lock
|
||||
date
|
||||
"""
|
||||
self.user_id.company_id.tax_lock_date = self.first_day_month_str
|
||||
move = self.env['account.move'].create(self.move)
|
||||
move.post()
|
||||
move.unlink()
|
||||
Reference in New Issue
Block a user