4e15e63732
When calling `_next()` in a sequence, it issues calls to `search()`, especially if it is a no-gap or date-range-based sequence (which is common in this use case). When doing a search, Odoo triggers recomputations. Thus, when doing both a write and a call to `_next()` in the same loop, Odoo had to flush to DB too often, causing a bottleneck. Now, the process is more optimized: 1. Cache all new entry numbers. 2. Write them all. 3. Mark them all as modified at once, to batch-trigger recomputations. To reduce the amount of recomputations, tracking is disabled for the entry number. After all, before renumbering there's already a warning telling you that you shouldn't renumber if you already published those entry numbers to your fiscal authority. Another pseudo-improvement is that the info log is shorter. Enable debug logging to log the list of IDs changed. A test was failing because it was relying on the fact that computations were not getting as lazy as they should. Manual flushes are added to imitate a user doing different invoice creations. @moduon MT-3082
74 lines
2.6 KiB
Python
74 lines
2.6 KiB
Python
# Copyright 2022 Moduon
|
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
|
import logging
|
|
|
|
from odoo import api, fields, models
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
ADDON = "account_journal_general_sequence"
|
|
|
|
|
|
class AccountMove(models.Model):
|
|
_inherit = "account.move"
|
|
|
|
_sql_constraints = [
|
|
(
|
|
"entry_number_unique",
|
|
"UNIQUE(entry_number, journal_id)",
|
|
"Entry number must be unique per journal.",
|
|
),
|
|
]
|
|
|
|
entry_number = fields.Char(
|
|
index=True,
|
|
readonly=True,
|
|
store=True,
|
|
compute="_compute_entry_number",
|
|
help="Automatic numbering, based on journal configuration.",
|
|
)
|
|
|
|
@api.depends("state")
|
|
def _compute_entry_number(self):
|
|
"""Assign an entry number when posting."""
|
|
# Skip if installing module, for performance reasons
|
|
if self.env.context.get("module") == ADDON:
|
|
module = self.env["ir.module.module"].search([("name", "=", ADDON)])
|
|
if module.state == "to install":
|
|
_logger.info(
|
|
"Skipping entry number generation at install for %s.",
|
|
self,
|
|
)
|
|
return
|
|
canceled = self.filtered_domain(
|
|
[("state", "=", "cancel"), ("entry_number", "!=", False)]
|
|
)
|
|
canceled.entry_number = False
|
|
if canceled:
|
|
no_gap_seqs = canceled.mapped(
|
|
"journal_id.entry_number_sequence_id"
|
|
).filtered_domain([("implementation", "=", "no_gap")])
|
|
if no_gap_seqs:
|
|
_logger.warning(
|
|
"Emptied entry_number for %r after cancellation. "
|
|
"This created gaps on %r.",
|
|
canceled,
|
|
no_gap_seqs,
|
|
)
|
|
chosen = self.filtered_domain(
|
|
[("state", "=", "posted"), ("entry_number", "=", False)]
|
|
)
|
|
# Cache all the new numbers to avoid wasting recomputations, caused by
|
|
# searches done by _next() in the loop below
|
|
chosen_map = {}
|
|
for move in chosen.sorted(lambda one: (one.date, one.name, one.id)):
|
|
chosen_map[move.id] = move.journal_id.entry_number_sequence_id._next(
|
|
move.date
|
|
)
|
|
# Write all the new numbers in the chosen moves
|
|
for move_id, new_number in chosen_map.items():
|
|
self.browse(move_id).entry_number = new_number
|
|
if chosen:
|
|
_logger.info("Added entry_number to %d account moves", len(chosen))
|
|
_logger.debug("Added entry_number to %r", chosen)
|