Skip to content
Closed
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
17 changes: 16 additions & 1 deletion l10n_fr_einvoicing/models/account_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,25 @@ def _compute_fr_directory_line_id(self):
fr_directory_line_id = dir_lines.id
move.fr_directory_line_id = fr_directory_line_id

# fr_directory_company_entity_type is deliberately NOT a dependency here.
#
# It is a related on company_id.partner_id.fr_directory_entity_type, so a
# single value would drive this stored field on EVERY invoice of the
# company. Setting the issuer's entity type — a one-off, done when the
# reform is switched on — then invalidates the whole history in one
# transaction: on a production-sized database (877k journal entries, 497k
# customer invoices on the one this was found on) the worker dies in
# MemoryError before writing anything.
#
# The issuer's entity type only changes at activation time, so the refresh
# is triggered explicitly and in batches by
# res.company._fr_ctc_recompute_einvoicing_required(). Same approach as
# company_fr_directory_line_id, which depends on company_id alone and comes
# with _fr_ctc_compute_invoice_company_dir_line().
@api.depends(
"fr_directory_company_entity_type",
"fr_directory_partner_entity_type",
"move_type",
"company_id",
"company_id.fr_ctc_send_out_invoice",
)
def _compute_einvoicing_required(self):
Expand Down
43 changes: 43 additions & 0 deletions l10n_fr_einvoicing/models/res_company.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,49 @@ def _fr_ctc_authorization_code_redirect(self):
}
return action

def _fr_ctc_recompute_einvoicing_required(self, batch_size=2000):
"""Refresh fr_einvoicing_required after the issuer's entity type changed.

That stored field depends on the issuer's entity type, which only
changes once — when the reform is switched on. Keeping it in the
@api.depends would recompute the company's whole history at flush time
and exhaust the worker's memory on a production database, so it is
refreshed here instead, in committed batches.

Deliberately not atomic: the computation is idempotent, so a batch
replayed after a failure yields the same result.
"""
self.ensure_one()
domain = [
("move_type", "in", ("out_invoice", "out_refund")),
("company_id", "=", self.id),
]
if self.hard_lock_date:
domain.append(("date", ">", self.hard_lock_date))
move_ids = self.env["account.move"].search(domain).ids
logger.info(
"Recomputing fr_einvoicing_required on %d invoices in company %s",
len(move_ids),
self.display_name,
)
for offset in range(0, len(move_ids), batch_size):
batch = self.env["account.move"].browse(
move_ids[offset:offset + batch_size]
)
batch._compute_einvoicing_required()
self.env.flush_all()
self.env.cr.commit()
self.env.invalidate_all()
logger.info(
"fr_einvoicing_required: %d/%d invoices processed",
min(offset + batch_size, len(move_ids)),
len(move_ids),
)
logger.info(
"Recomputation of fr_einvoicing_required in company %s finished",
self.display_name,
)

def _fr_ctc_compute_invoice_company_dir_line(self):
self.ensure_one()
domain = [
Expand Down