[FIX] account: fix accounting dates
Fix the accounting date in a few scenarios, such as exchange difference or CABA moves reversal. Also avoid updating the currency rate when reversing exchange difference moves. closes odoo/odoo#98543 X-original-commit: 2b8df0c111a785a9e51dc2f528ac5b00cb0eadb9 Signed-off-by: Laurent Smet <las@odoo.com> Signed-off-by: Nicolas Viseur <vin@odoo.com>
This commit is contained in:
@@ -27,11 +27,11 @@ class AccountFullReconcile(models.Model):
|
||||
res = super().unlink()
|
||||
|
||||
# Reverse all exchange moves at once.
|
||||
today = fields.Date.context_today(self)
|
||||
default_values_list = [{
|
||||
'date': today,
|
||||
'ref': _('Reversal of: %s') % move.name,
|
||||
} for move in moves_to_reverse]
|
||||
moves_to_reverse._reverse_moves(default_values_list, cancel=True)
|
||||
if moves_to_reverse:
|
||||
default_values_list = [{
|
||||
'date': move._get_accounting_date(move.date, move._affect_tax_report()),
|
||||
'ref': _('Reversal of: %s') % move.name,
|
||||
} for move in moves_to_reverse]
|
||||
moves_to_reverse._reverse_moves(default_values_list, cancel=True)
|
||||
|
||||
return res
|
||||
|
||||
@@ -2008,13 +2008,14 @@ class AccountMove(models.Model):
|
||||
|
||||
def copy_data(self, default=None):
|
||||
data_list = super().copy_data(default)
|
||||
for data in data_list:
|
||||
data['line_ids'] = [
|
||||
(command, _id, line_vals)
|
||||
for command, _id, line_vals in data['line_ids']
|
||||
if command == 0
|
||||
and line_vals.get('display_type') not in ('payment_term', 'tax', 'rounding')
|
||||
]
|
||||
for move, data in zip(self, data_list):
|
||||
if move.move_type in ('out_invoice', 'in_invoice'):
|
||||
data['line_ids'] = [
|
||||
(command, _id, line_vals)
|
||||
for command, _id, line_vals in data['line_ids']
|
||||
if command == Command.CREATE
|
||||
and line_vals.get('display_type') not in ('payment_term', 'tax', 'rounding')
|
||||
]
|
||||
if not self.journal_id.active and 'journal_id' in data_list:
|
||||
del default['journal_id']
|
||||
return data_list
|
||||
@@ -3112,10 +3113,12 @@ class AccountMove(models.Model):
|
||||
return action
|
||||
|
||||
def action_post(self):
|
||||
if self.payment_id:
|
||||
self.payment_id.action_post()
|
||||
else:
|
||||
self._post(soft=False)
|
||||
moves_with_payments = self.filtered('payment_id')
|
||||
other_moves = self - moves_with_payments
|
||||
if moves_with_payments:
|
||||
moves_with_payments.payment_id.action_post()
|
||||
if other_moves:
|
||||
other_moves._post(soft=False)
|
||||
return False
|
||||
|
||||
def js_assign_outstanding_line(self, line_id):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import ast
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from datetime import date
|
||||
from datetime import date, timedelta
|
||||
from functools import lru_cache
|
||||
|
||||
from odoo import api, fields, models, Command, _
|
||||
@@ -1828,7 +1828,7 @@ class AccountMoveLine(models.Model):
|
||||
|
||||
move_vals = {
|
||||
'move_type': 'entry',
|
||||
'date': max(exchange_date or date.min, company._get_user_fiscal_lock_date()),
|
||||
'date': max(exchange_date or date.min, company._get_user_fiscal_lock_date() + timedelta(days=1)),
|
||||
'journal_id': journal.id,
|
||||
'line_ids': [],
|
||||
}
|
||||
|
||||
@@ -110,12 +110,12 @@ class AccountPartialReconcile(models.Model):
|
||||
full_to_unlink.unlink()
|
||||
|
||||
# Reverse CABA entries.
|
||||
today = fields.Date.context_today(self)
|
||||
default_values_list = [{
|
||||
'date': move.date if move.date > (move.company_id.period_lock_date or date.min) else today,
|
||||
'ref': _('Reversal of: %s') % move.name,
|
||||
} for move in moves_to_reverse]
|
||||
moves_to_reverse._reverse_moves(default_values_list, cancel=True)
|
||||
if moves_to_reverse:
|
||||
default_values_list = [{
|
||||
'date': move._get_accounting_date(move.date, move._affect_tax_report()),
|
||||
'ref': _('Reversal of: %s') % move.name,
|
||||
} for move in moves_to_reverse]
|
||||
moves_to_reverse._reverse_moves(default_values_list, cancel=True)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from . import test_account_move_out_refund
|
||||
from . import test_account_move_in_invoice
|
||||
from . import test_account_move_in_refund
|
||||
from . import test_account_move_entry
|
||||
from . import test_account_move_date_algorithm
|
||||
from . import test_invoice_tax_totals
|
||||
from . import test_account_journal
|
||||
from . import test_account_account
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import fields, Command
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
import freezegun
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestAccountMoveDateAlgorithm(AccountTestInvoicingCommon):
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# HELPERS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def _create_invoice(self, move_type, date, **kwargs):
|
||||
return self.env['account.move'].create({
|
||||
'invoice_date': date,
|
||||
'partner_id': self.partner_a.id,
|
||||
**kwargs,
|
||||
'move_type': move_type,
|
||||
'date': date,
|
||||
'invoice_line_ids': [
|
||||
Command.create({
|
||||
'product_id': self.product_a.id,
|
||||
'price_unit': 1200.0,
|
||||
'tax_ids': [],
|
||||
**line_kwargs,
|
||||
})
|
||||
for line_kwargs in kwargs.get('invoice_line_ids', [{}])
|
||||
],
|
||||
})
|
||||
|
||||
def _create_payment(self, date, **kwargs):
|
||||
return self.env['account.payment'].create({
|
||||
'partner_id': self.partner_a.id,
|
||||
'payment_type': 'inbound',
|
||||
'partner_type': 'customer',
|
||||
**kwargs,
|
||||
'date': date,
|
||||
})
|
||||
|
||||
def _set_lock_date(self, lock_date):
|
||||
self.env.company.fiscalyear_lock_date = fields.Date.from_string(lock_date)
|
||||
|
||||
def _reverse_invoice(self, invoice):
|
||||
move_reversal = self.env['account.move.reversal']\
|
||||
.with_context(active_model="account.move", active_ids=invoice.ids)\
|
||||
.create({
|
||||
'journal_id': invoice.journal_id.id,
|
||||
'reason': "no reason",
|
||||
'refund_method': 'cancel',
|
||||
})
|
||||
reversal = move_reversal.reverse_moves()
|
||||
return self.env['account.move'].browse(reversal['res_id'])
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# TESTS
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@freezegun.freeze_time('2017-01-12')
|
||||
def test_out_invoice_date_with_lock_date(self):
|
||||
self._set_lock_date('2016-12-31')
|
||||
move = self._create_invoice('out_invoice', '2016-01-01')
|
||||
move.action_post()
|
||||
|
||||
self.assertRecordValues(move, [{
|
||||
'invoice_date': fields.Date.from_string('2016-01-01'),
|
||||
'date': fields.Date.from_string('2017-01-12'),
|
||||
}])
|
||||
|
||||
@freezegun.freeze_time('2017-01-12')
|
||||
def test_out_invoice_reverse_date_with_lock_date(self):
|
||||
move = self._create_invoice('out_invoice', '2016-01-01')
|
||||
move.action_post()
|
||||
self._set_lock_date('2016-12-31')
|
||||
reverse_move = self._reverse_invoice(move)
|
||||
|
||||
self.assertRecordValues(reverse_move, [{
|
||||
'invoice_date': fields.Date.from_string('2017-01-12'),
|
||||
'date': fields.Date.from_string('2017-01-12'),
|
||||
}])
|
||||
|
||||
@freezegun.freeze_time('2017-01-12')
|
||||
def test_out_refund_date_with_lock_date(self):
|
||||
self._set_lock_date('2016-12-31')
|
||||
move = self._create_invoice('out_refund', '2016-01-01')
|
||||
move.action_post()
|
||||
|
||||
self.assertRecordValues(move, [{
|
||||
'invoice_date': fields.Date.from_string('2016-01-01'),
|
||||
'date': fields.Date.from_string('2017-01-12'),
|
||||
}])
|
||||
|
||||
@freezegun.freeze_time('2017-01-12')
|
||||
def test_out_refund_reverse_date_with_lock_date(self):
|
||||
move = self._create_invoice('out_refund', '2016-01-01')
|
||||
move.action_post()
|
||||
self._set_lock_date('2016-12-31')
|
||||
reverse_move = self._reverse_invoice(move)
|
||||
|
||||
self.assertRecordValues(reverse_move, [{'date': fields.Date.from_string('2017-01-12')}])
|
||||
|
||||
@freezegun.freeze_time('2017-01-12')
|
||||
def test_in_invoice_date_with_lock_date(self):
|
||||
self._set_lock_date('2016-12-31')
|
||||
move = self._create_invoice('in_invoice', '2016-01-01')
|
||||
move.action_post()
|
||||
|
||||
self.assertRecordValues(move, [{
|
||||
'invoice_date': fields.Date.from_string('2016-01-01'),
|
||||
'date': fields.Date.from_string('2017-01-12'),
|
||||
}])
|
||||
|
||||
@freezegun.freeze_time('2017-01-12')
|
||||
def test_in_invoice_reverse_date_with_lock_date(self):
|
||||
move = self._create_invoice('in_invoice', '2016-01-01')
|
||||
move.action_post()
|
||||
self._set_lock_date('2016-12-31')
|
||||
reverse_move = self._reverse_invoice(move)
|
||||
|
||||
self.assertRecordValues(reverse_move, [{
|
||||
'invoice_date': fields.Date.from_string('2017-01-12'),
|
||||
'date': fields.Date.from_string('2017-01-12'),
|
||||
}])
|
||||
|
||||
@freezegun.freeze_time('2017-01-12')
|
||||
def test_in_refund_date_with_lock_date(self):
|
||||
self._set_lock_date('2016-12-31')
|
||||
move = self._create_invoice('in_refund', '2016-01-01')
|
||||
move.action_post()
|
||||
|
||||
self.assertRecordValues(move, [{
|
||||
'invoice_date': fields.Date.from_string('2016-01-01'),
|
||||
'date': fields.Date.from_string('2017-01-12'),
|
||||
}])
|
||||
|
||||
@freezegun.freeze_time('2017-01-12')
|
||||
def test_in_refund_reverse_date_with_lock_date(self):
|
||||
move = self._create_invoice('in_refund', '2016-01-01')
|
||||
move.action_post()
|
||||
self._set_lock_date('2016-12-31')
|
||||
reverse_move = self._reverse_invoice(move)
|
||||
|
||||
self.assertRecordValues(reverse_move, [{'date': fields.Date.from_string('2017-01-12')}])
|
||||
|
||||
@freezegun.freeze_time('2017-02-12')
|
||||
def test_reconcile_with_lock_date(self):
|
||||
invoice = self._create_invoice('out_invoice', '2016-01-01', currency_id=self.currency_data['currency'].id)
|
||||
refund = self._create_invoice('out_refund', '2017-01-01', currency_id=self.currency_data['currency'].id)
|
||||
(invoice + refund).action_post()
|
||||
self._set_lock_date('2017-01-31')
|
||||
|
||||
res = (invoice + refund).line_ids\
|
||||
.filtered(lambda x: x.account_id.account_type == 'asset_receivable')\
|
||||
.reconcile()
|
||||
exchange_move = res['partials'].exchange_move_id
|
||||
|
||||
self.assertRecordValues(exchange_move, [{
|
||||
'date': fields.Date.from_string('2017-02-01'),
|
||||
'amount_total_signed': 200.0,
|
||||
}])
|
||||
|
||||
@freezegun.freeze_time('2017-02-12')
|
||||
def test_unreconcile_with_lock_date(self):
|
||||
invoice = self._create_invoice('out_invoice', '2016-01-01', currency_id=self.currency_data['currency'].id)
|
||||
refund = self._create_invoice('out_refund', '2017-01-01', currency_id=self.currency_data['currency'].id)
|
||||
(invoice + refund).action_post()
|
||||
|
||||
res = (invoice + refund).line_ids\
|
||||
.filtered(lambda x: x.account_id.account_type == 'asset_receivable')\
|
||||
.reconcile()
|
||||
exchange_move = res['partials'].exchange_move_id
|
||||
|
||||
self._set_lock_date('2017-01-31')
|
||||
(invoice + refund).line_ids.remove_move_reconcile()
|
||||
|
||||
reverse_exchange_move = exchange_move.line_ids.matched_credit_ids.credit_move_id.move_id
|
||||
self.assertRecordValues(reverse_exchange_move, [{
|
||||
'date': fields.Date.from_string('2017-02-12'),
|
||||
'amount_total_signed': 200.0,
|
||||
}])
|
||||
|
||||
def test_caba_with_lock_date(self):
|
||||
tax_waiting_account = self.env['account.account'].create({
|
||||
'name': 'TAX_WAIT',
|
||||
'code': 'TWAIT',
|
||||
'account_type': 'liability_current',
|
||||
'reconcile': True,
|
||||
})
|
||||
tax = self.env['account.tax'].create({
|
||||
'name': 'cash basis 10%',
|
||||
'type_tax_use': 'sale',
|
||||
'amount': 10,
|
||||
'tax_exigibility': 'on_payment',
|
||||
'cash_basis_transition_account_id': tax_waiting_account.id,
|
||||
})
|
||||
|
||||
invoice = self._create_invoice(
|
||||
'out_invoice', '2016-01-01',
|
||||
currency_id=self.currency_data['currency'].id,
|
||||
invoice_line_ids=[{'tax_ids': [Command.set(tax.ids)]}],
|
||||
)
|
||||
payment = self._create_payment('2016-02-01', amount=invoice.amount_total)
|
||||
(invoice + payment.move_id).action_post()
|
||||
|
||||
self._set_lock_date('2017-01-03')
|
||||
|
||||
with freezegun.freeze_time('2017-01-12'):
|
||||
(invoice + payment.move_id).line_ids\
|
||||
.filtered(lambda x: x.account_id.account_type == 'asset_receivable')\
|
||||
.reconcile()
|
||||
|
||||
caba_move = self.env['account.move'].search([('tax_cash_basis_origin_move_id', '=', invoice.id)])
|
||||
|
||||
self.assertRecordValues(caba_move, [{
|
||||
'date': fields.Date.from_string('2017-01-12'),
|
||||
'amount_total_signed': 440.0,
|
||||
}])
|
||||
|
||||
self._set_lock_date('2017-02-01')
|
||||
|
||||
with freezegun.freeze_time('2017-03-12'):
|
||||
(invoice + payment.move_id).line_ids.remove_move_reconcile()
|
||||
|
||||
reverse_exchange_move = self.env['account.move'].search([('tax_cash_basis_origin_move_id', '=', invoice.id)]) - caba_move
|
||||
|
||||
self.assertRecordValues(reverse_exchange_move, [{
|
||||
'date': fields.Date.from_string('2017-02-28'),
|
||||
'amount_total_signed': 440.0,
|
||||
}])
|
||||
Reference in New Issue
Block a user