Why am I getting the error "Expected singleton" despite submitting only one ID?

I added some fields to the model stock.move

and I imported a CSV file to create some records. I go through all the lines and create entries 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 a lot as needed in a method _get_lot_id

. If the batch has already been created, I return the ID. It works well

It is very simple and works many times, but sometimes I get the following error even though I restrict_lot_id

only fill the field with one ID, as you can see in the dictionary. It looks like it is adding the batch ID of the previous cycle. How is this possible? Is my database crashed?

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 confirmed that the ID arrives well inside the original function create

in the module stock

. It doesn't make any sense to me. What's happening?

I just checked that if I always create a lot it works well. So, something must be wrong with the method _get_lot_id

I am showing 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

      

+4


source to share


3 answers


I think the problem is in one of your code.

Let me explain why this error can happen in Odoo. self

in a method is recordSet

, which means that it can contain one or more records. When you use a decorator @api.multi

, depends

or constraints

there self

may contain more than one record, so as to avoid such errors, make sure that you loop

- 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

it works fine when recordset

you only have one record, but when you have more it confuses which record you want to get the value from some_field

and here you get this error.

But when you use a decorator @api.one

, a method will be called here for every record, a single error never occurs with api.one

, because self always contains one record.

+6


source


The method create

was probably overridden by some custom (or not) module that modifies the vals passed to the create function.

To find out what is happening, you need to go to the definition of the create

model method stock.production.lot

(addons / stock / stock.py) and either raise the exception, or import traceback;traceback.print_stack()

to see the methods that are called. After that, you will see the one that changes your values.



If not, you need to share more code for us to find out what's going on and how you create the dictionary move

+1


source


I finally found the bug. The error was in the computed field as @Cherif Odoo said. I had this method

@api.multi
@api.depends('incoming_moves', 'incoming_moves.box_quantity')
def _compute_in_box_vqty(self):
    for lot in self:
        lot.in_box_vqty = sum(
            move.box_quantity for move in self.incoming_moves)

      

And I had to replace self

with the lot

following

@api.multi
@api.depends('incoming_moves', 'incoming_moves.box_quantity')
def _compute_in_box_vqty(self):
    for lot in self:
        lot.in_box_vqty = sum(
            move.box_quantity for move in lot.incoming_moves)

      

I haven't found the error before because the stack trace was not very helpful. I found it after comments on methods, fields, constraints, creates ...

+1


source







All Articles