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

3

5

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
Canton answered 24/6, 2017 at 20:34 Comment(6)
Did you put any custom code like a compute field or a constraints.Alis
I have custom constraints. I have commented them to check if they were the source of the problem, but I get the same error. Did you have a problem like this? Or are you using your intuition?Canton
how many databases are running with the same database user ? It seems like there is also possibilities of browser session issue. it will process in other database in which you logged in last time and details are in cache. So it's advisable to test it in private window from the database selector page.Post
I've already tried in a private window. And I am using only one database and with the admin user to do testsCanton
I updated my question with the code of _get_lot_idCanton
I found the solution. Finally it was a computed field as @Cherif Odoo said :) it was not a decorator, although. Thank you to everybody. I have written my own answerCanton
A
9

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.

Alis answered 25/6, 2017 at 22:32 Comment(4)
Yes, I know how to use @api.one and @api.multi. The error here is different, I think it has to do with cache corruption or something similar. I create stock moves sending only one ID. If I create one record it works well, but if I create many of them, the lots that I have created in the method _get_lot_id come back from their ashes. Anyway I will take a look to all the methods involved againCanton
@Canton yes from your reputation i can tell that you know the difference but it's for other developers too. check your code see where you use self.get_some_value directly.Alis
I updated my question with the code of _get_lot_idCanton
The problem in my case was in a compute method, exactly the same case as @Canton explains in his answer, but the reason is the one you mention here. Thank you!Nygaard
P
1

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

Parian answered 24/6, 2017 at 21:33 Comment(9)
Thanks for your time. The lots are well created, the problem is when I insert the lot id in the stock.move module. I updated my answer with the stackCanton
See if the if the connector module is installed. If not, restart the server. If the problem is still not resolved upgrade the base module.Parian
I have installed and uninstalled the module. I updated all modules with --update=all with same resultsCanton
What do you mean by syncronization problem?Parian
I have checked that using a pause, so I don't think that's the problem. I thought that Odoo was running more than one query at the same time.Canton
Try something else please, ps -ef | grep odoo and kill all the instances of the server you find. Verify that the server is not running and then start the server with -d your_db_name -u allParian
There is only one instance running and I have already update all the modules, but the same error appearsCanton
And if I first create the stock moves without the restrict_lot_id and after that I modified the lod with the method write I get the same error but with all the lots that I have created. I am a very bit lost here ValueError Expected singleton: stock.production.lot(12830, 12831, 12832, 12833, 12834, 12836, 12838, 12839, 12841, 12846, 12848, 12849, 12851, 12852, 12854, 12855, 12857, 12859, 12861, 12864, 12866, 12867, 12868, 12870, 12871, 12872, 12873, 12875, 12877, 12878, 12885, 12889, 12892, 12894, 12897, 12898)Canton
I updated my question with the code of _get_lot_idCanton
C
1

Finally I found the mistake. The error was in a 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 the self with lot like this

@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 didn't find the error earlier because the stack trace was not very useful. I found it after commenting methods, fields, constraints, creates...

Canton answered 27/6, 2017 at 10:59 Comment(2)
Mmm, but it is not exactly what you said because I have used for lot in self from the beginning. Anyway I will accept your answer because I want you to be happy :)Canton
This answer saved me, Odoo traceback is pathetic in this kind of error. Thank you for sharing!Nygaard

© 2022 - 2024 — McMap. All rights reserved.