diff --git a/contract/README.rst b/contract/README.rst index 267baaef..cc397553 100644 --- a/contract/README.rst +++ b/contract/README.rst @@ -16,6 +16,25 @@ Configuration To view discount field set *Discount on lines* in user access rights. +You might find that generating all pending invoices at once takes too much +time and produces some performance problems, mostly in cases where you +generate a lot of invoices in little time (i.e. when invoicing thousands +of contracts yearly, and you get to January 1st of the next year). To avoid +this bottleneck, the trick is to **increase the cron frequence and decrease +the contracts batch size**. The counterpart is that some invoices could not +be generated in the exact day you expected. To configure that: + +#. Go to *Settings > Activate the developer mode*. +#. Go to *Settings > Technical > Automation > Scheduled Actions > + Generate Recurring Invoices from Contracts > Edit > Information*. +#. Set a lower interval. For example, change *Interval Unit* to *Hours*. +#. Go to the *Technical Data* tab, and add a batch size in *Arguments*. + For example, it should look like ``(20,)``. +#. Save. + +That's it! From now on, only 20 invoices per hour will be generated. +That should take only a few seconds each hour, and shouln't block other users. + Usage ===== diff --git a/contract/models/account_analytic_account.py b/contract/models/account_analytic_account.py index df26b7b9..3eabbbf5 100644 --- a/contract/models/account_analytic_account.py +++ b/contract/models/account_analytic_account.py @@ -255,12 +255,16 @@ class AccountAnalyticAccount(models.Model): @api.multi def _create_invoice(self): self.ensure_one() - invoice_vals = self._prepare_invoice() - invoice = self.env['account.invoice'].create(invoice_vals) + # Re-read contract with correct company + _self = self.with_context(self.get_invoice_context()) + invoice_vals = _self._prepare_invoice() + invoice = _self.env['account.invoice'].create(invoice_vals) + # Lines are read from an env where expensive values are precomputed for line in self.recurring_invoice_line_ids: - invoice_line_vals = self._prepare_invoice_line(line, invoice.id) - self.env['account.invoice.line'].create(invoice_line_vals) - invoice.compute_taxes() + invoice_line_vals = _self._prepare_invoice_line(line, invoice.id) + _self.env['account.invoice.line'].create(invoice_line_vals) + # Update next invoice date for current contract + _self.recurring_next_date = _self.env.context['next_date'] return invoice @api.multi @@ -287,6 +291,7 @@ class AccountAnalyticAccount(models.Model): relativedelta(days=1)) date_to = date_start ctx.update({ + 'mail_notrack': True, 'next_date': next_date, 'date_format': date_format, 'date_from': date_from, @@ -317,18 +322,21 @@ class AccountAnalyticAccount(models.Model): :return: invoices created """ - invoices = self.env['account.invoice'] - for contract in self: - if limit and len(invoices) >= limit: - break - if not contract.check_dates_valid(): - continue - # Re-read contract with correct company - ctx = contract.get_invoice_context() - invoices |= contract.with_context(ctx)._create_invoice() - contract.write({ - 'recurring_next_date': fields.Date.to_string(ctx['next_date']) - }) + _self = self.with_context(prefetch_fields=False) + invoices = _self.env['account.invoice'] + # Precompute expensive computed fields in batch + recurring_lines = _self.mapped("recurring_invoice_line_ids") + recurring_lines._fields["price_unit"].determine_value(recurring_lines) + # Create invoices + with _self.env.norecompute(): + for contract in _self: + if limit and len(invoices) >= limit: + break + if not contract.check_dates_valid(): + continue + invoices |= contract._create_invoice() + invoices.compute_taxes() + _self.recompute() return invoices @api.model