无法使用关闭的游标

Unable to use a closed cursor

在对 res.partner 模型进行一些操作后,我在第二次调用 make_po 方法时遇到此异常。如果我停止服务器并重新启动它,操作将继续正确执行以下是我对 make_po 方法和堆栈跟踪的覆盖:

class procurement_order(osv.osv):
    _inherit = 'procurement.order'

    counter = 0
    global_po_id = None
    line_counter = 0
    first_line = None
    def make_po(self, cr, uid, ids, context=None):
        _logger = logging.getLogger(__name__)
        """ Resolve the purchase from procurement, which may result in a new PO creation, a new PO line creation or a quantity change on existing PO line.
        Note that some operations (as the PO creation) are made as SUPERUSER because the current user may not have rights to do it (mto product launched by a sale for example)
        @return: dictionary giving for each procurement its related resolving PO line.
        OVERRIDE: If we already have a purchase order on a draft state with the same product, do not append the lines.
        Instead create a new Purchase Order
        """
        company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
        po_obj = self.pool.get('purchase.order')
        seq_obj = self.pool.get('ir.sequence')
        sales_orders = self.pool.get('sale.order')
        po_line = self.pool.get('purchase.order.line')

        # Get the procurement ID ("key" == Procurement ID, "value" == PO line)
        procurement_purchase_order = super(procurement_order, self).make_po(cr, uid, ids, context=context)
        key, value = procurement_purchase_order.popitem()
        procurement_purchase_order[key] = value

        # Search for the equivalent purchase.order.line
        p_o_line_returned_id = po_line.search(cr, uid, [('id', '=', value)])
        p_o_line_returned = po_line.browse(cr, uid, p_o_line_returned_id, context=context)

        # get purchase order from its line
        purchase_order_from_line_ID = po_obj.search(cr, uid, [('id', '=', p_o_line_returned.order_id.id)])
        purchase_order_from_line = po_obj.browse(cr, uid, purchase_order_from_line_ID, context=context)

        # Get me the current procurement
        current_procurement_ID = self.search(cr, uid, [('id', '=', key)])
        current_procurement = self.browse(cr, uid, current_procurement_ID, context=context)

        # Get me the line ids of the lines that are related to purchase_order_from_line_ID
        lines_IDS = po_line.search(cr, uid, [('order_id', '=', purchase_order_from_line_ID[0])])
        _logger.warning('lines_IDS' + str(lines_IDS))

        # Check the source of all the lines in thelines_IDS
        for line in po_line.browse(cr, uid, lines_IDS, context=context):
            _logger.warning('First line value ' + str(procurement_order.first_line))
            _logger.warning('')
            if line.procurement_ids != 0:
                if procurement_order.line_counter == 0:
                    procurement_order.first_line = line 
                    procurement_order.line_counter += 1

                    # Compare the first line procurement.group_id with the current procurement.group_id
                if procurement_order.first_line.procurement_ids and procurement_order.first_line.procurement_ids[0].group_id == current_procurement[0].group_id:
                    _logger.warning('GROUP IDS ARE EQUAL')
                else:
                    _logger.warning('GROUP IDS ARE NOT EQUAL')

                    # if we have inequality between the group ids then that means that the current "line" was created by a different SO
                    to_remove = []
                    to_remove.append(line.id)

                    # from the current_procurement.group_id get the PO.line and remove it
                    temp_group_id = current_procurement[0].group_id.id
                    _logger.warning(str(temp_group_id))
                    temp_line_for_removal_ID = po_line.search(cr, uid, [('id', '=', temp_group_id)])
                    po_line.unlink(cr, uid, temp_line_for_removal_ID, context=context)


                    _logger.warning('Removed from Purchase Order ' + str(purchase_order_from_line_ID[0]) + ' Lines ' + str(to_remove))
                    pass_ids = []
                    # for all the procrements that belong to my SO
                    # for procurement in self.browse(cr, uid, ids, context=context):
                    partner = self._get_product_supplier(cr, uid, current_procurement, context=context)
                    if not partner:
                        self.message_post(cr, uid, [current_procurement.id], _('There is no supplier associated to product %s') % (current_procurement.product_id.name))
                        procurement_purchase_order[current_procurement.id] = False
                    else:
                        schedule_date = self._get_purchase_schedule_date(cr, uid, current_procurement, company, context=context)
                        purchase_date = self._get_purchase_order_date(cr, uid, current_procurement, company, schedule_date, context=context) 
                        line_vals = self._get_po_line_values_from_proc(cr, uid, current_procurement, partner, company, schedule_date, context=context)  # get the lines from here
                        name = seq_obj.get(cr, uid, 'purchase.order') or _('PO: %s') % current_procurement.name
                        po_vals = {
                        'name': name,
                        'origin': current_procurement.origin,
                        'partner_id': partner.id,
                        'location_id': current_procurement.location_id.id,
                        'picking_type_id': current_procurement.rule_id.picking_type_id.id,
                        'pricelist_id': partner.property_product_pricelist_purchase.id,
                        'currency_id': partner.property_product_pricelist_purchase and partner.property_product_pricelist_purchase.currency_id.id or current_procurement.company_id.currency_id.id,
                        'date_order': purchase_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
                        'company_id': current_procurement.company_id.id,
                        'fiscal_position': po_obj.onchange_partner_id(cr, uid, None, partner.id, context=context)['value']['fiscal_position'],
                        'payment_term_id': partner.property_supplier_payment_term.id or False,
                        'dest_address_id': current_procurement.partner_dest_id.id,
                    }

                    # The first time create the PO and add a line to it
                    if procurement_order.counter == 0:
                        po_id = self.create_procurement_purchase_order(cr, uid, current_procurement, po_vals, line_vals, context=context)
                        procurement_order.global_po_id = po_id
                        po_line_id = po_obj.browse(cr, uid, po_id, context=context).order_line[0].id
                        pass_ids.append(current_procurement.id)
                        procurement_purchase_order[current_procurement.id] = po_line_id
                        self.write(cr, uid, [current_procurement.id], {'purchase_line_id': po_line_id}, context=context)
                        _logger.warning('New Purchase order created ' + str(po_id))
                    else:
                        # All the other lines from the procurements will be appended here
                        proc_order = self.pool.get('procurement.order')
                        purchase_order_created = po_obj.browse(cr, uid, procurement_order.global_po_id, context=context)  # get the Purchase Order
                        line_vals = proc_order._get_po_line_values_from_proc(cr, uid, current_procurement, partner, company, schedule_date, context=context)  # Get the line from the procurement
                        po_line_obj = self.pool.get('purchase.order.line')
                        line_vals['order_id'] = purchase_order_created[0].id
                        # if there exists a line with the same id, do not add it
                        if len(po_line_obj.search(cr, uid, [('order_id', '=', purchase_order_created[0].id)])) == 0:
                            po_line_id = po_line_obj.create(cr, uid, line_vals, context=context)
                            _logger.warning('Appending line ' + str(line_vals) + 'to existing Purchase order ' + str(purchase_order_created))
                        else:
                            _logger.warning('Line has already been appended, dismissed')
                    if pass_ids:
                        self.message_post(cr, uid, pass_ids, body=_("Draft Purchase Order created"), context=context)
                    procurement_order.counter += 1
        self.pool.get('purchase.order.line').split_lines(cr, uid, [procurement_purchase_order[ids[0]]], context=context)
        return procurement_purchase_order

堆栈跟踪:

2015-09-10 13:15:11,752 1167 ERROR odoov8 openerp.http: Exception during JSON request handling.
Traceback (most recent call last):
  File "/home/odoo/odoo/openerp/http.py", line 537, in _handle_exception
    return super(JsonRequest, self)._handle_exception(exception)
  File "/home/odoo/odoo/openerp/http.py", line 574, in dispatch
    result = self._call_function(**self.params)
  File "/home/odoo/odoo/openerp/http.py", line 310, in _call_function
    return checked_call(self.db, *args, **kwargs)
  File "/home/odoo/odoo/openerp/service/model.py", line 113, in wrapper
    return f(dbname, *args, **kwargs)
  File "/home/odoo/odoo/openerp/http.py", line 307, in checked_call
    return self.endpoint(*a, **kw)
  File "/home/odoo/odoo/openerp/http.py", line 803, in __call__
    return self.method(*args, **kw)
  File "/home/odoo/odoo/openerp/http.py", line 403, in response_wrap
    response = f(*args, **kw)
  File "/home/odoo/odoo/addons/web/controllers/main.py", line 948, in call_button
    action = self._call_kw(model, method, args, {})
  File "/home/odoo/odoo/addons/web/controllers/main.py", line 936, in _call_kw
    return getattr(request.registry.get(model), method)(request.cr, request.uid, *args, **kwargs)
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/portal_sale/portal_sale.py", line 67, in action_button_confirm
    return super(sale_order, self).action_button_confirm(cr, uid, ids, context=context)
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/sale/sale.py", line 605, in action_button_confirm
    self.signal_workflow(cr, uid, ids, 'order_confirm')
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/openerp/models.py", line 3560, in signal_workflow
    result[res_id] = workflow.trg_validate(uid, self._name, res_id, signal, cr)
  File "/home/odoo/odoo/openerp/workflow/__init__.py", line 85, in trg_validate
    return WorkflowService.new(cr, uid, res_type, res_id).validate(signal)
  File "/home/odoo/odoo/openerp/workflow/service.py", line 91, in validate
    res2 = wi.validate(signal)
  File "/home/odoo/odoo/openerp/workflow/instance.py", line 75, in validate
    wi.process(signal=signal, force_running=force_running, stack=stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 120, in process
    ok = self._split_test(activity['split_mode'], signal, stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 248, in _split_test
    self._join_test(t[0], t[1], stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 257, in _join_test
    WorkflowItem.create(self.session, self.record, activity, inst_id, stack=stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 95, in create
    workflow_item.process(stack=stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 120, in process
    ok = self._split_test(activity['split_mode'], signal, stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 248, in _split_test
    self._join_test(t[0], t[1], stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 257, in _join_test
    WorkflowItem.create(self.session, self.record, activity, inst_id, stack=stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 95, in create
    workflow_item.process(stack=stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 120, in process
    ok = self._split_test(activity['split_mode'], signal, stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 248, in _split_test
    self._join_test(t[0], t[1], stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 257, in _join_test
    WorkflowItem.create(self.session, self.record, activity, inst_id, stack=stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 95, in create
    workflow_item.process(stack=stack)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 116, in process
    if not self._execute(activity, stack):
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 162, in _execute
    returned_action = self.wkf_expr_execute(activity)
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 313, in wkf_expr_execute
    return self.wkf_expr_eval_expr(activity['action'])
  File "/home/odoo/odoo/openerp/workflow/workitem.py", line 291, in wkf_expr_eval_expr
    result = eval(line, env, nocopy=True)
  File "/home/odoo/odoo/openerp/tools/safe_eval.py", line 314, in safe_eval
    return eval(c, globals_dict, locals_dict)
  File "", line 1, in <module>
  File "/home/odoo/odoo/openerp/api.py", line 239, in wrapper
    return new_api(self, *args, **kwargs)
  File "/home/odoo/odoo/openerp/api.py", line 547, in new_api
    result = method(self._model, cr, uid, self.ids, *args, **kwargs)
  File "/home/odoo/odoo/addons/sale/sale.py", line 765, in action_ship_create
    procurement_obj.run(cr, uid, proc_ids, context=context)
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/procurement_jit_stock/procurement_jit_stock.py", line 30, in run
    res = super(procurement_order, self).run(cr, uid, ids, autocommit=autocommit, context=context)
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/stock/procurement.py", line 219, in run
    self.pool.get('stock.move').action_confirm(cr, uid, move_to_confirm_ids, context=context)
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/stock/stock.py", line 2214, in action_confirm
    self._create_procurements(cr, uid, moves, context=context)
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/procurement_jit_stock/procurement_jit_stock.py", line 43, in _create_procurements
    self.pool['procurement.order'].run(cr, uid, res, context=context)
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/procurement_jit_stock/procurement_jit_stock.py", line 30, in run
    res = super(procurement_order, self).run(cr, uid, ids, autocommit=autocommit, context=context)
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/stock/procurement.py", line 210, in run
    res = super(procurement_order, self).run(cr, uid, new_ids, autocommit=autocommit, context=context)
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/procurement/procurement.py", line 206, in run
    res = self._run(cr, uid, procurement, context=context or {})
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/purchase/purchase.py", line 1267, in _run
    return self.make_po(cr, uid, [procurement.id], context=context)[procurement.id]
  File "/home/odoo/odoo/openerp/api.py", line 241, in wrapper
    return old_api(self, *args, **kwargs)
  File "/home/odoo/odoo/addons/glints/models/purchase.py", line 154, in make_po
    if procurement_order.first_line.procurement_ids and procurement_order.first_line.procurement_ids[0].group_id == current_procurement[0].group_id:
  File "/home/odoo/odoo/openerp/fields.py", line 817, in __get__
    self.determine_value(record)
  File "/home/odoo/odoo/openerp/fields.py", line 910, in determine_value
    record._prefetch_field(self)
  File "/home/odoo/odoo/openerp/api.py", line 239, in wrapper
    return new_api(self, *args, **kwargs)
  File "/home/odoo/odoo/openerp/models.py", line 3231, in _prefetch_field
    result = records.read(list(fnames), load='_classic_write')
  File "/home/odoo/odoo/openerp/api.py", line 239, in wrapper
    return new_api(self, *args, **kwargs)
  File "/home/odoo/odoo/openerp/models.py", line 3176, in read
    self._read_from_database(stored, inherited)
  File "/home/odoo/odoo/openerp/api.py", line 239, in wrapper
    return new_api(self, *args, **kwargs)
  File "/home/odoo/odoo/openerp/models.py", line 3300, in _read_from_database
    cr.execute(query_str, [tuple(sub_ids)] + where_params)
  File "/home/odoo/odoo/openerp/sql_db.py", line 157, in wrapper
    raise psycopg2.OperationalError(msg)
OperationalError: Unable to use a closed cursor.

我认为这是因为您滥用了 Odoo ORM。首先,在 Odoo 中,您永远不会像这样声明 class 级别的属性...

counter = 0
global_po_id = None
line_counter = 0
first_line = None

然后你在这里引用它:

if procurement_order.first_line

问题是 procurement_order 的类型是 osv.osv 但这行代码应该使用浏览记录。当它试图追踪并从它认为是浏览记录的内容中懒惰地加载数据项时,它会到达需要使用游标但没有游标可供使用的地步,因此出现错误。

最好将 osv.osv class 视为只读元数据 class,它在数据库中描述了您的 table,但也让您有一些已定义静态或 class 方法。

您使用的procurement_order应该是浏览的结果

my_procurement_order = self.browse(cr, uid, ids[0], context=context)

下一个问题是您不能在 运行 时将 first_line 之类的值分配给浏览记录,它们是只读的(注意这在 Odoo 8 new API 但那是另一回事了)。

这里的一个常见做法是维护一个类似本地字典的东西,你可以参考:

first_lines = {my_procurement_order.id: a_line}