[FIX] account, sale: create invoice lines in batch

This patch changes the create of `account.invoice.line` to use
`model_create_multi`, which allows the batch creation of multiple
invoice lines while only triggering the recompute of all related fields
once.

It also changes the generation of invoices from a SO to take advantage
of the create multi of `account.invoice.line`.

For a SO of 2000 lines:
    Pre-patch:
        ~417s to generate an Invoice
    Post-patch:
        ~30s to generate an Invoice

closes odoo/odoo#31975

Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
This commit is contained in:
Adrian Torres
2019-03-20 14:34:17 +00:00
parent 7dfcea0655
commit f4b7dab00e
2 changed files with 36 additions and 11 deletions
+6 -6
View File
@@ -1865,12 +1865,12 @@ class AccountInvoiceLine(models.Model):
}
return data
@api.model
def create(self, vals):
if vals.get('display_type', self.default_get(['display_type'])['display_type']):
vals.update(price_unit=0, account_id=False, quantity=0)
return super(AccountInvoiceLine, self).create(vals)
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get('display_type', self.default_get(['display_type'])['display_type']):
vals.update(price_unit=0, account_id=False, quantity=0)
return super(AccountInvoiceLine, self).create(vals_list)
@api.multi
def write(self, values):
+30 -5
View File
@@ -522,6 +522,8 @@ class SaleOrder(models.Model):
# We only want to create sections that have at least one invoiceable line
pending_section = None
# Create lines in batch to avoid performance problems
line_vals_list = []
for line in order.order_line:
if line.display_type == 'line_section':
pending_section = line
@@ -543,14 +545,21 @@ class SaleOrder(models.Model):
if line.qty_to_invoice > 0 or (line.qty_to_invoice < 0 and final):
if pending_section:
pending_section.invoice_line_create(invoices[group_key].id, pending_section.qty_to_invoice)
line_vals_list.extend(pending_section.invoice_line_create_vals(
invoices[group_key].id,
pending_section.qty_to_invoice
))
pending_section = None
line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice)
line_vals_list.extend(line.invoice_line_create_vals(
invoices[group_key].id, line.qty_to_invoice
))
if references.get(invoices.get(group_key)):
if order not in references[invoices[group_key]]:
references[invoices[group_key]] |= order
self.env['account.invoice.line'].create(line_vals_list)
for group_key in invoices:
invoices[group_key].write({'name': ', '.join(invoices_name[group_key]),
'origin': ', '.join(invoices_origin[group_key])})
@@ -1363,18 +1372,34 @@ class SaleOrderLine(models.Model):
@api.multi
def invoice_line_create(self, invoice_id, qty):
""" Create an invoice line. The quantity to invoice can be positive (invoice) or negative (refund).
.. deprecated:: 12.0
Replaced by :func:`invoice_line_create_vals` which can be used for creating
`account.invoice.line` records in batch
:param invoice_id: integer
:param qty: float quantity to invoice
:returns recordset of account.invoice.line created
"""
invoice_lines = self.env['account.invoice.line']
return self.env['account.invoice.line'].create(
self.invoice_line_create_vals(invoice_id, qty))
def invoice_line_create_vals(self, invoice_id, qty):
""" Create an invoice line. The quantity to invoice can be positive (invoice) or negative
(refund).
:param invoice_id: integer
:param qty: float quantity to invoice
:returns list of dict containing creation values for account.invoice.line records
"""
vals_list = []
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
for line in self:
if not float_is_zero(qty, precision_digits=precision) or not line.product_id:
vals = line._prepare_invoice_line(qty=qty)
vals.update({'invoice_id': invoice_id, 'sale_line_ids': [(6, 0, [line.id])]})
invoice_lines |= self.env['account.invoice.line'].create(vals)
return invoice_lines
vals_list.append(vals)
return vals_list
@api.multi
def _prepare_procurement_values(self, group_id=False):