[FIX] account: automatic entry wizard: avoid clashes with lock dates
Currently, if you use the automatic entry wizard to change the period of a journal item dated prior to the lock date, you'll just get blocked with a UserError, with no workaround. This commit changes the date of the adjustment entry to be the first end of month after the lock date. As a result, the adjustment entry can be created. Based on PR #92439 closes odoo/odoo#122997 Taskid: 2823170 X-original-commit: fdeaff0a879bdf1242ed0a8d85e3e0363dd3276b Signed-off-by: Antoine Dupuis (andu) <andu@odoo.com> Signed-off-by: William André (wan) <wan@odoo.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import fields, Command
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged, Form
|
||||
import time
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestTransferWizard(AccountTestInvoicingCommon):
|
||||
|
||||
@@ -285,3 +287,58 @@ class TestTransferWizard(AccountTestInvoicingCommon):
|
||||
self.assertEqual(len(destination_lines), 2, "Two lines should have been created on destination account: one for each currency (the lines with same partner and currency should have been aggregated)")
|
||||
self.assertAlmostEqual(destination_lines.filtered(lambda x: x.currency_id == self.test_currency_1).amount_currency, -10, self.test_currency_1.decimal_places)
|
||||
self.assertAlmostEqual(destination_lines.filtered(lambda x: x.currency_id == self.test_currency_2).amount_currency, -756, self.test_currency_2.decimal_places)
|
||||
|
||||
def test_period_change_lock_date(self):
|
||||
""" Test that the period change wizard correctly handles the lock date: if the original entry is dated
|
||||
before the lock date, the adjustment entry is created on the first end of month after the lock date.
|
||||
"""
|
||||
# Set up accrual accounts
|
||||
self.company_data['company'].expense_accrual_account_id = self.env['account.account'].create({
|
||||
'name': 'Expense Accrual Account',
|
||||
'code': '113226',
|
||||
'account_type': 'asset_prepayments',
|
||||
'reconcile': True,
|
||||
})
|
||||
self.company_data['company'].revenue_accrual_account_id = self.env['account.account'].create({
|
||||
'name': 'Revenue Accrual Account',
|
||||
'code': '226113',
|
||||
'account_type': 'liability_current',
|
||||
'reconcile': True,
|
||||
})
|
||||
|
||||
# Create a move before the lock date
|
||||
move = self.env['account.move'].create({
|
||||
'journal_id': self.company_data['default_journal_sale'].id,
|
||||
'date': '2019-01-01',
|
||||
'line_ids': [
|
||||
Command.create({'account_id': self.accounts[0].id, 'debit': 1000, }),
|
||||
Command.create({'account_id': self.accounts[0].id, 'credit': 1000, }),
|
||||
]
|
||||
})
|
||||
move.action_post()
|
||||
|
||||
# Set the lock date
|
||||
move.company_id.write({'period_lock_date': '2019-02-28', 'fiscalyear_lock_date': '2019-02-28'})
|
||||
|
||||
# Open the transfer wizard at a date after the lock date
|
||||
wizard = self.env['account.automatic.entry.wizard'] \
|
||||
.with_context(active_model='account.move.line', active_ids=move.line_ids[0].ids) \
|
||||
.create({
|
||||
'action': 'change_period',
|
||||
'date': '2019-05-01',
|
||||
'journal_id': self.company_data['default_journal_misc'].id,
|
||||
})
|
||||
|
||||
# Check that the 'The date is being set prior to the user lock date' message appears.
|
||||
self.assertRecordValues(wizard, [{
|
||||
'lock_date_message': 'The date is being set prior to the user lock date 02/28/2019. '
|
||||
'The Journal Entry will be accounted on 03/31/2019 upon posting.'
|
||||
}])
|
||||
|
||||
# Create the adjustment move.
|
||||
wizard_res = wizard.do_action()
|
||||
|
||||
# Check that the adjustment move was created on the first end of month after the lock date.
|
||||
created_moves = self.env['account.move'].browse(wizard_res['domain'][0][2])
|
||||
adjustment_move = created_moves[1] # There are 2 created moves; the adjustment move is the second one.
|
||||
self.assertRecordValues(adjustment_move, [{'date': fields.Date.to_date('2019-03-31')}])
|
||||
|
||||
@@ -3,9 +3,9 @@ from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools.misc import format_date, formatLang
|
||||
from odoo.tools.float_utils import float_repr
|
||||
from odoo.tools import groupby
|
||||
|
||||
from collections import defaultdict
|
||||
from itertools import groupby
|
||||
from markupsafe import Markup, escape
|
||||
import json
|
||||
|
||||
@@ -43,6 +43,7 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
compute="_compute_revenue_accrual_account",
|
||||
inverse="_inverse_revenue_accrual_account",
|
||||
)
|
||||
lock_date_message = fields.Char(string="Lock Date Message", compute="_compute_lock_date_message")
|
||||
|
||||
# change account
|
||||
destination_account_id = fields.Many2one(string="To", comodel_name='account.account', help="Account to transfer to.")
|
||||
@@ -101,6 +102,17 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
for record in self:
|
||||
record.account_type = 'income' if sum(record.move_line_ids.mapped('balance')) < 0 else 'expense'
|
||||
|
||||
@api.depends('action', 'move_line_ids')
|
||||
def _compute_lock_date_message(self):
|
||||
for record in self:
|
||||
record.lock_date_message = False
|
||||
if record.action == 'change_period':
|
||||
for aml in record.move_line_ids:
|
||||
lock_date_message = aml.move_id._get_lock_date_message(aml.date, aml.move_id._affect_tax_report())
|
||||
if lock_date_message:
|
||||
record.lock_date_message = lock_date_message
|
||||
break
|
||||
|
||||
@api.depends('destination_account_id')
|
||||
def _compute_display_currency_helper(self):
|
||||
for record in self:
|
||||
@@ -112,11 +124,6 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
if wizard.move_line_ids.move_id._get_violated_lock_dates(wizard.date, False):
|
||||
raise ValidationError(_("The date selected is protected by a lock date"))
|
||||
|
||||
if wizard.action == 'change_period':
|
||||
for move in wizard.move_line_ids.move_id:
|
||||
if move._get_violated_lock_dates(move.date, False):
|
||||
raise ValidationError(_("The date of some related entries is protected by a lock date"))
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super().default_get(fields)
|
||||
@@ -208,6 +215,12 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
}]
|
||||
|
||||
def _get_move_dict_vals_change_period(self):
|
||||
reference_move = self.env['account.move'].new({'journal_id': self.journal_id.id})
|
||||
|
||||
def get_lock_safe_date(aml):
|
||||
# Use a reference move in the correct journal because _get_accounting_date depends on the journal sequence.
|
||||
return reference_move._get_accounting_date(aml.date, aml.move_id._affect_tax_report())
|
||||
|
||||
# set the change_period account on the selected journal items
|
||||
accrual_account = self.revenue_accrual_account if self.account_type == 'income' else self.expense_accrual_account
|
||||
|
||||
@@ -220,7 +233,7 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
'journal_id': self.journal_id.id,
|
||||
}}
|
||||
# complete the account.move data
|
||||
for date, grouped_lines in groupby(self.move_line_ids, lambda m: m.move_id.date):
|
||||
for date, grouped_lines in groupby(self.move_line_ids, get_lock_safe_date):
|
||||
grouped_lines = list(grouped_lines)
|
||||
amount = sum(l.balance for l in grouped_lines)
|
||||
move_data[date] = {
|
||||
@@ -261,7 +274,7 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
'analytic_distribution': aml.analytic_distribution,
|
||||
}),
|
||||
]
|
||||
move_data[aml.move_id.date]['line_ids'] += [
|
||||
move_data[get_lock_safe_date(aml)]['line_ids'] += [
|
||||
(0, 0, {
|
||||
'name': aml.name or '',
|
||||
'debit': reported_credit,
|
||||
@@ -344,7 +357,7 @@ class AutomaticEntryWizard(models.TransientModel):
|
||||
accrual_move_offsets = defaultdict(int)
|
||||
for move in self.move_line_ids.move_id:
|
||||
amount = sum((self.move_line_ids._origin & move.line_ids).mapped('balance'))
|
||||
accrual_move = created_moves[1:].filtered(lambda m: m.date == move.date)
|
||||
accrual_move = created_moves[1:].filtered(lambda m: m.date == m._get_accounting_date(move.date, move._affect_tax_report()))
|
||||
|
||||
if accrual_account.reconcile and accrual_move.state == 'posted' and destination_move.state == 'posted':
|
||||
destination_move_lines = destination_move.mapped('line_ids').filtered(lambda line: line.account_id == accrual_account)[destination_move_offset:destination_move_offset+2]
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
The selected destination account is set to use a specific currency. Every entry transferred to it will be converted into this currency, causing
|
||||
the loss of any pre-existing foreign currency amount.
|
||||
</div>
|
||||
<div attrs="{'invisible': [('lock_date_message', '=', False)]}" class="alert alert-warning text-center" role="status">
|
||||
<field name="lock_date_message" nolabel="1"/>
|
||||
</div>
|
||||
<group>
|
||||
<group attrs="{'invisible': [('action', '!=', 'change_period')]}">
|
||||
<field name="date" string="Recognition Date"/>
|
||||
|
||||
Reference in New Issue
Block a user