Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get the error "Expected singleton" in spite of sending only one id?

I added some fields to the model stock.move and I importing a CSV file in order to create some records. I go over the all the rows and I create the records one by one as you can see here:

lot_id = self._get_lot_id(
    n, row, product_id, picking_type_id,
    box_quantity, product_uom_qty
)

move = {
    'auto_lot_name': False,
    'box_quantity': 2,
    'client_order_ref': '581002',
    'date': datetime.datetime(2017, 6, 24, 19, 55, 52, 372648),
    'invoice_state': '2binvoiced',
    'location_dest_id': 9,
    'location_id': 12,
    'name': 'Product name',
    'partner_id': 487,
    'partner_shipping_id': 488,
    'picking_type_id': 2,
    'price_unit': 4.0,
    'pricelist_id': 1,
    'product_code': u'36033',
    'product_id': 3,
    'product_uom': 3,
    'product_uom_qty': 6.0,
    'restrict_lot_id': 12222,   # lot_id
    'tax_id': [(4, 67)]
}

result = self.env['stock.move'].create(move)

I create the lot if needed in the method _get_lot_id. If the lot is already created I return the id. This is working well

It is very simple and works well many times, but sometimes I get the following error despite that I am filling the field restrict_lot_id with only one id as you can see in the dictionary. It looks like it is appending the lot id of the previous loop. How is that possible? Is my database broken?

File "/[ ... ]/import_moves/models/stock_picking_import_wizard.py", line 129, in _generate_moves_from_csv
    result = self.env['stock.move'].create(move)
  File "/[ ... ]/openerp/api.py", line 266, in wrapper
    return new_api(self, *args, **kwargs)
  File "/[ ... ]/openerp/api.py", line 508, in new_api
    result = method(self._model, cr, uid, *args, **old_kwargs)
  File "/[ ... ]/stock/stock.py", line 1993, in create
    res = super(stock_move, self).create(cr, uid, vals, context=context)
  File "/[ ... ]/openerp/api.py", line 268, in wrapper
    return old_api(self, *args, **kwargs)
  File "/[ ... ]/openerp/api.py", line 372, in old_api
    result = method(recs, *args, **kwargs)
  File "/[ ... ]/connector/producer.py", line 48, in create     ##> strange because this module is not installed
    record_id = create_original(self, vals)
  File "/[ ... ]/openerp/api.py", line 266, in wrapper
    return new_api(self, *args, **kwargs)
  File "/[ ... ]/openerp/models.py", line 4126, in create
    record = self.browse(self._create(old_vals))
  File "/[ ... ]/openerp/api.py", line 266, in wrapper
    return new_api(self, *args, **kwargs)
  File "/[ ... ]/openerp/api.py", line 508, in new_api
    result = method(self._model, cr, uid, *args, **old_kwargs)
  File "/[ ... ]/openerp/models.py", line 4323, in _create
    recs._validate_fields(vals)
  File "/[ ... ]/openerp/api.py", line 266, in wrapper
    return new_api(self, *args, **kwargs)
  File "/[ ... ]/openerp/models.py", line 1285, in _validate_fields
    raise ValidationError("Error while validating constraint\n\n%s" % tools.ustr(e))
ValidationError: ('ValidateError', u'Error while validating constraint\n\nValueError\nExpected singleton: stock.production.lot(12286, 12287)')

I have also verified that the id is arriving well inside the original create function in the stock module. It does not make any sense to me. What is happening?

I have just checked that if I always create the lot it is working well. So something has to be wrong with the method _get_lot_id that I show here

def _get_lot_id(self, n, row, product_id,
                picking_type_id, box_quantity, product_uom_qty):
    lot_id_name = row.get('lot_id_name', False)
    if lot_id_name != '':
        if picking_type_id == STOCK_PICKING_TYPE_OUT:
            lot_ids = self.env['stock.production.lot'].search([
                ('product_id', '=', product_id.id),
                ('name', '=', lot_id_name)
            ])
            if len(lot_ids) == 0:
                try:
                    lot_id = self.env['stock.production.lot'].create({
                        'name': lot_id_name,
                        'product_id': product_id.id,
                    })                
                except Exception:
                    raise Warning(_('The lot could not be created. '
                                    '\nROW: %s') % n)
                return lot_id.ensure_one().id
            if len(lot_ids) == 1:
                return lot_ids[0].id
            else:
                raise Warning(_('ERROR\nThere is more than one lot with the same name for that product.'
                                '\nROW: %s') % n)                
        elif picking_type_id == STOCK_PICKING_TYPE_IN:
            lot_ids = self.env['stock.production.lot'].search([
                ('product_id', '=', product_id.id),
                ('name', '=', lot_id_name)
            ])
            if len(lot_ids) == 1:
                return lot_ids[0].id
            try:
                lot_id = self.env['stock.production.lot'].create({
                    'name': lot_id_name,
                    'product_id': product_id.id,
                })
                return lot_id.id
            except Exception:
                raise Warning(_('The lot could not be created. '
                                '\nROW: %s') % n)
    else:
        if picking_type_id == STOCK_PICKING_TYPE_OUT:
            raise Warning(_('The lot is required for outgoing moves. '
                            '\nROW: %s') % n)
        elif picking_type_id == STOCK_PICKING_TYPE_IN:
            # set "auto_lot_name = True"  >> this is set by default, so the lot is automatically created
            return False
like image 221
ChesuCR Avatar asked Jun 24 '17 20:06

ChesuCR


2 Answers

I think the problem is in one of your code.

Let me explain why this error can happen in Odoo the self in the method is a recordSet, means it can contain a one or more record. When you use decorator @api.multi , depends, or constraints here self can contain more than one record so to avoid this kind of errors make sure you loop through self.

     for rec in self:
               # your code here
               # always access to rec fields not self here

If you don't loop when you do self.some_field this works fine when the recordset have only one record but when you have more this is confusing what record you want to get the value of some_field from and here you get this error.

But when you use decorator @api.one here will call the method for each record , singleton error never happen with api.one because self always contains one recorrd.

like image 184
Charif DZ Avatar answered Oct 02 '22 03:10

Charif DZ


Probably the create method has been overridden by some custom (or not) module which modifies the vals passed to the create function.

A way to find out what is happening would be to go to the definition of create method of stock.production.lot model (addons/stock/stock.py) and either raise an Exception or import traceback;traceback.print_stack() to see the methods that are called. After that you can see the one which changes your values.

If that is not the case, you have to share more code for us to see what is going on and how do you create the move dictionary

like image 29
George Daramouskas Avatar answered Oct 02 '22 05:10

George Daramouskas