【问题标题】:How to optimize invoice validation如何优化发票验证
【发布时间】:2020-10-22 14:28:14
【问题描述】:

我正在研究使用大量数据的数据库。一张发票可能有 7482 种不同的物品。验证发票需要花费大量时间,需要 26 分钟才能验证包含 7482 篇文章的发票。我发现需要时间完成的方法,它是“odoo\addons\account\models\account_invoice.py”中的“action_move_create”。

@api.multi
def action_move_create(self):
    """ Creates invoice related analytics and financial move lines """
    account_move = self.env['account.move']

    for inv in self:
        if not inv.journal_id.sequence_id:
            raise UserError(_('Please define sequence on the journal related to this invoice.'))
        if not inv.invoice_line_ids.filtered(lambda line: line.account_id):
            raise UserError(_('Please add at least one invoice line.'))
        if inv.move_id:
            continue


        if not inv.date_invoice:
            inv.write({'date_invoice': fields.Date.context_today(self)})
        if not inv.date_due:
            inv.write({'date_due': inv.date_invoice})
        company_currency = inv.company_id.currency_id

        # create move lines (one per invoice line + eventual taxes and analytic lines)
        iml = inv.invoice_line_move_line_get()
        iml += inv.tax_line_move_line_get()

        diff_currency = inv.currency_id != company_currency
        # create one move line for the total and possibly adjust the other lines amount
        total, total_currency, iml = inv.compute_invoice_totals(company_currency, iml)

        name = inv.name or ''
        if inv.payment_term_id:
            totlines = inv.payment_term_id.with_context(currency_id=company_currency.id).compute(total, inv.date_invoice)[0]
            res_amount_currency = total_currency
            for i, t in enumerate(totlines):
                if inv.currency_id != company_currency:
                    amount_currency = company_currency._convert(t[1], inv.currency_id, inv.company_id, inv._get_currency_rate_date() or fields.Date.today())
                else:
                    amount_currency = False

                # last line: add the diff
                res_amount_currency -= amount_currency or 0
                if i + 1 == len(totlines):
                    amount_currency += res_amount_currency

                iml.append({
                    'type': 'dest',
                    'name': name,
                    'price': t[1],
                    'account_id': inv.account_id.id,
                    'date_maturity': t[0],
                    'amount_currency': diff_currency and amount_currency,
                    'currency_id': diff_currency and inv.currency_id.id,
                    'invoice_id': inv.id
                })
        else:
            iml.append({
                'type': 'dest',
                'name': name,
                'price': total,
                'account_id': inv.account_id.id,
                'date_maturity': inv.date_due,
                'amount_currency': diff_currency and total_currency,
                'currency_id': diff_currency and inv.currency_id.id,
                'invoice_id': inv.id
            })
        part = self.env['res.partner']._find_accounting_partner(inv.partner_id)
        line = [(0, 0, self.line_get_convert(l, part.id)) for l in iml]
        line = inv.group_lines(iml, line)

        line = inv.finalize_invoice_move_lines(line)

        date = inv.date or inv.date_invoice
        move_vals = {
            'ref': inv.reference,
            'line_ids': line,
            'journal_id': inv.journal_id.id,
            'date': date,
            'narration': inv.comment,
        }
        move = account_move.create(move_vals)
        # Pass invoice in method post: used if you want to get the same
        # account move reference when creating the same invoice after a cancelled one:
        move.post(invoice = inv)
        # make the invoice point to that move
        vals = {
            'move_id': move.id,
            'date': date,
            'move_name': move.name,
        }
        inv.write(vals)
    return True

您能提出一些解决方案吗?

我们假设硬件能够有效地正确运行 odoo。

【问题讨论】:

  • 问得好,而且这种方法确实存在一些瓶颈。我认为在 Odoo 的 Github 中有一些关于它的问题,但我还没有找到它。
  • @CZoellner 感谢您提供线索。让我在github上搜索。如果您有任何建议,请不要犹豫
  • 第一次检查时,我看到 _amount_compute()_compute_matched_percentage() 两者都会被调用 2800 万次。
  • 你可以尝试一下,但之后必须重新计算它们。帐户模块中的任何地方都有一个示例。只需搜索with self.env.norecompute():
  • @CZoellner,正在按预期计算计算字段,但未触发相关字段,我将它们添加到“vals”中。

标签: python postgresql odoo


【解决方案1】:

我使用原始 sql 查询对其进行了优化。我在 account.invoice 模型中制作了这些代码:
第一个是_mock_create_move_line的定义(在action_move_create中调用)。

    def _mock_create_move_line(self, model, values, move):
        bad_names = ["analytic_line_ids", "tax_ids", "analytic_tag_ids"]
        other_fields = [
            "currency_id", "debit", "credit", "balance",
            "debit_cash_basis", "credit_cash_basis", "balance_cash_basis",
            "company_currency_id", "amount_residual",
            "amount_residual_currency", "tax_base_amount", "reconciled",
            "company_id", "counterpart"
        ]
        cr = self.env.cr
        quote = '"{}"'.format
        columns = []
        columns1 = []
        for i, v in enumerate(values):
            v = model._add_missing_default_values(v)
            account_id = self.env['account.account'].browse(v['account_id'])
            # compulsory columns and some stored related columns
            # related fields are not triggered, krrrrr
            v.update({
                'move_id': move.id,
                'date_maturity': move.date,
                'company_id': account_id.company_id.id,
                'date': move.date,
                'journal_id': move.journal_id.id,
                'user_type_id': account_id.user_type_id.id,
                'create_uid': self.env.uid,
                'create_date': fields.Datetime.now()
            })
            ######
            temp_column = []
            for name, val in sorted(v.items()):
                if name in bad_names:
                    continue
                field = model._fields[name]
                if field.column_type:
                    col_val = field.convert_to_column(val, model, v)
                    temp_column.append(col_val)
                    if not i:
                        columns1.append((name, field.column_format, col_val))
            columns.append(tuple(temp_column))

        model.check_access_rule('create')

        try:
            query = "INSERT INTO {} ({}) VALUES {} RETURNING id".format(
                quote(model._table),
                ", ".join(quote(name) for name, fmt, val in columns1),
                ", ".join('%s' for fmt in columns),
            )
            cr.execute(query, columns)
            ids = cr.fetchall()
            # clear the model cache to take account of the new insertion
            # if not executed, relationnal field will not be updated
            model.invalidate_cache()

            account_move_line_ids = model.browse(ids)

            account_move_line_ids.modified(other_fields)

            account_move_line_ids.recompute()


            # update parent_path
            account_move_line_ids._parent_store_create()


        except Exception as e:
            _logger.info(e)
            cr.rollback()

        return

第二个是覆盖原生方法action_move_create。我做了一些修改,如果上下文中有'raw_sql',则调用_mock_create_move_line。

@api.multi
def action_move_create(self):
    """ Creates invoice related analytics and financial move lines """
    # TODO : make choice between ORM or raw sql according to the context
    account_move = self.env['account.move']

    for inv in self:
        if not inv.journal_id.sequence_id:
            raise UserError(_('Please define sequence on the journal related to this invoice.'))
        if not inv.invoice_line_ids.filtered(lambda line: line.account_id):
            raise UserError(_('Please add at least one invoice line.'))
        if inv.move_id:
            continue

        if not inv.date_invoice:
            inv.write({'date_invoice': fields.Date.context_today(self)})
        if not inv.date_due:
            inv.write({'date_due': inv.date_invoice})
        company_currency = inv.company_id.currency_id

        # create move lines (one per invoice line + eventual taxes and analytic lines)
        iml = inv.invoice_line_move_line_get()
        iml += inv.tax_line_move_line_get()

        diff_currency = inv.currency_id != company_currency
        # create one move line for the total and possibly adjust the other lines amount
        total, total_currency, iml = inv.compute_invoice_totals(company_currency, iml)

        name = inv.name or ''
        if inv.payment_term_id:
            totlines = \
            inv.payment_term_id.with_context(currency_id=company_currency.id).compute(total, inv.date_invoice)[0]
            res_amount_currency = total_currency
            for i, t in enumerate(totlines):
                if inv.currency_id != company_currency:
                    amount_currency = company_currency._convert(t[1], inv.currency_id, inv.company_id,
                                                                inv._get_currency_rate_date() or fields.Date.today())
                else:
                    amount_currency = False

                # last line: add the diff
                res_amount_currency -= amount_currency or 0
                if i + 1 == len(totlines):
                    amount_currency += res_amount_currency

                iml.append({
                    'type': 'dest',
                    'name': name,
                    'price': t[1],
                    'account_id': inv.account_id.id,
                    'date_maturity': t[0],
                    'amount_currency': diff_currency and amount_currency,
                    'currency_id': diff_currency and inv.currency_id.id,
                    'invoice_id': inv.id
                })
        else:
            iml.append({
                'type': 'dest',
                'name': name,
                'price': total,
                'account_id': inv.account_id.id,
                'date_maturity': inv.date_due,
                'amount_currency': diff_currency and total_currency,
                'currency_id': diff_currency and inv.currency_id.id,
                'invoice_id': inv.id
            })
        part = self.env['res.partner']._find_accounting_partner(inv.partner_id)
        line = [(0, 0, self.line_get_convert(l, part.id)) for l in iml]
        line = inv.group_lines(iml, line)

        line = inv.finalize_invoice_move_lines(line)

        date = inv.date or inv.date_invoice

        if self.env.context.get('raw_sql', None):
            move_vals = {
                'ref': inv.reference,
                'journal_id': inv.journal_id.id,
                'date': date,
                'narration': inv.comment,
            }
            # remove (0, 0, ...)
            # override the group_lines method to avoid looping on next instruction
            new_lines = [nl[2] for nl in line]
            # TODO do not call compute here, add with ...norecompute()
            move = account_move.create(move_vals)
            move.env.cr.commit()

            self._mock_create_move_line(self.env['account.move.line'], new_lines, move)
            # Pass invoice in method post: used if you want to get the same
            # account move reference when creating the same invoice after a cancelled one:
            # compute move, it is not triggered automatically bc raw sql insertion
            # is it correct to call it like this ? find better way
            move._amount_compute()
            move._compute_partner_id()
            move._compute_matched_percentage()
        else:
            # make default behavior
            move_vals = {
                'ref': inv.reference,
                'line_ids': line,
                'journal_id': inv.journal_id.id,
                'date': date,
                'narration': inv.comment,
            }
            move = account_move.create(move_vals)

        move.post(invoice=inv)
        # make the invoice point to that move
        vals = {
            'move_id': move.id,
            'date': date,
            'move_name': move.name,
        }
        inv.write(vals)
    return True

现在,在 invoice.move.line 中插入大约 7000 条记录的执行时间不到 1 分钟

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    • 1970-01-01
    • 2013-04-20
    • 1970-01-01
    相关资源
    最近更新 更多