2022-05-23 11:23:58 +02:00
|
|
|
# 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__)
|
|
|
|
|
2022-06-03 13:39:52 +02:00
|
|
|
ADDON = "account_journal_general_sequence"
|
|
|
|
|
2022-05-23 11:23:58 +02:00
|
|
|
|
|
|
|
class AccountMove(models.Model):
|
|
|
|
_inherit = "account.move"
|
|
|
|
|
|
|
|
_sql_constraints = [
|
|
|
|
(
|
|
|
|
"entry_number_unique",
|
2023-06-23 13:33:22 +02:00
|
|
|
"UNIQUE(entry_number, journal_id)",
|
|
|
|
"Entry number must be unique per journal.",
|
2022-05-23 11:23:58 +02:00
|
|
|
),
|
|
|
|
]
|
|
|
|
|
|
|
|
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."""
|
2022-06-03 13:39:52 +02:00
|
|
|
# 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
|
2022-05-23 11:23:58 +02:00
|
|
|
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)]
|
|
|
|
)
|
[FIX] account_journal_general_sequence: optimize recomputes when renumbering
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
2023-06-26 12:26:55 +02:00
|
|
|
# Cache all the new numbers to avoid wasting recomputations, caused by
|
|
|
|
# searches done by _next() in the loop below
|
|
|
|
chosen_map = {}
|
[FIX] account_journal_general_sequence: provide defaults for sorting
Under some weird scenarios where some modules are involved, several invoices are posted at the same time, and no invoice exists, we could get to the case when `one.date` or `one.name` were `False`.
Here I provide a default value to avoid comparing `str` to `bool` in those cases.
Tried a lot but couldn't reproduce the test case in code, sorry 😕. However, the traceback was clear:
```
File "/opt/odoo/auto/addons/account_journal_general_sequence/models/account_move.py", line 64, in _compute_entry_number
for move in chosen.sorted(lambda one: (one.date, one.name, one.id)):
File "/opt/odoo/custom/src/odoo/odoo/models.py", line 5583, in sorted
ids = tuple(item.id for item in sorted(self, key=key, reverse=reverse))
TypeError: '<' not supported between instances of 'str' and 'bool'
```
@moduon MT-5559
2024-03-25 11:51:45 +01:00
|
|
|
for move in chosen.sorted(
|
|
|
|
lambda one: (one.date or "", one.name or "", one.id or 0)
|
|
|
|
):
|
[FIX] account_journal_general_sequence: optimize recomputes when renumbering
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
2023-06-26 12:26:55 +02:00
|
|
|
chosen_map[move.id] = move.journal_id.entry_number_sequence_id._next(
|
2022-05-23 11:23:58 +02:00
|
|
|
move.date
|
|
|
|
)
|
[FIX] account_journal_general_sequence: optimize recomputes when renumbering
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
2023-06-26 12:26:55 +02:00
|
|
|
# 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
|
2022-05-23 11:23:58 +02:00
|
|
|
if chosen:
|
[FIX] account_journal_general_sequence: optimize recomputes when renumbering
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
2023-06-26 12:26:55 +02:00
|
|
|
_logger.info("Added entry_number to %d account moves", len(chosen))
|
|
|
|
_logger.debug("Added entry_number to %r", chosen)
|