Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions auditlog/models/auditlog_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class ThrowAwayCache:
]

def __init__(self, env):
self._env = env
self._transaction = env.transaction

def __enter__(self):
Expand All @@ -80,6 +81,12 @@ def __enter__(self):
to the cursor, so if we want to keep using the same cursor, we need to
patch out these properties.
"""
# Flush any pending updates and recomputations first. If the cache is
# set aside while recomputations of stored computed fields are still
# pending, they end up being consumed after the swap/read/restore
# cycle without ever being persisted, leaving NULL columns in the
# database (see OCA/server-tools#3635).
self._env.flush_all()
for attribute in self.transaction_attributes:
instance = getattr(self._transaction, attribute)
setattr(
Expand Down
1 change: 1 addition & 0 deletions test_auditlog/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from . import test_account_move_reverse
from . import test_product_tax_multicompany
from . import test_account_reentrancy
from . import test_payment_residual
57 changes: 57 additions & 0 deletions test_auditlog/tests/test_payment_residual.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from odoo.tests import tagged

from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.addons.auditlog.tests.common import AuditLogRuleCommon


@tagged("post_install", "-at_install")
class TestPaymentResidual(AccountTestInvoicingCommon, AuditLogRuleCommon):
def setUp(self):
super().setUp()
self.rule = self.env["auditlog.rule"].create(
{
"name": __name__,
"model_id": self.env.ref("account.model_account_move").id,
"log_read": True,
"log_create": True,
"log_write": True,
"log_unlink": True,
"log_type": "full",
}
)
self.rule.set_to_confirmed()

def test_register_payment_computes_residual(self):
"""Payment lines keep their stored computed values with a full rule.

With a full-log rule on account.move, the swapped-cache reads of the
log diff must not cause the pending recomputations of the payment
lines' stored computed fields to be lost, or those columns end up
NULL in the database (issue #3635: the payment is then never offered
as an outstanding credit on the invoice).
"""
invoice = self.init_invoice("out_invoice", products=self.product_a, post=True)
wizard = (
self.env["account.payment.register"]
.with_context(active_model="account.move", active_ids=invoice.ids)
.create({"journal_id": self.company_data["default_journal_bank"].id})
)
payments = wizard._create_payments()
self.env.flush_all()
self.env.cr.execute(
"""
SELECT id, amount_residual, amount_residual_currency, reconciled
FROM account_move_line
WHERE move_id = %s
""",
(payments.move_id.id,),
)
rows = self.env.cr.fetchall()
self.assertTrue(rows)
for line_id, residual, residual_currency, reconciled in rows:
self.assertIsNotNone(residual, f"line {line_id}: amount_residual is NULL")
self.assertIsNotNone(
residual_currency,
f"line {line_id}: amount_residual_currency is NULL",
)
self.assertIsNotNone(reconciled, f"line {line_id}: reconciled is NULL")
Loading