MtC
MtC

Reputation: 31

How to fix ValueError: Expected singleton sale.order(41,...)

I've written a custom odoo module, and when I install it, it throws a singleton error. When I comment out everything inside _compute_margin(self), and just return 0.0, the module installs fine. When I then change the _compute_margin function back to original, and update the module, it updates fine.

What change(s) do I need to make in order to make the module install fine with the contents of the _compute_margin function?

class MyCompanyintercompanyMargin(models.Model):
    _name = 'my_companyintercompany.margin'
    name = fields.Char()
    x_marginsplitmodel = fields.Char()
    x_marginsplitdescription = fields.Char()

class ResPartner(models.Model):
    _name = 'res.partner'
    _inherit = 'res.partner'

    x_my_companyintercompany = fields.Boolean()
    x_my_companyintercompany_marginsplit = fields.Many2one(
        'my_companyintercompany.margin',
        string='Margin Split Model'
    )

    class SaleOrder(models.Model):
    _name = 'sale.order'
    _inherit = 'sale.order'

    x_endcustomer = fields.Many2one(
            comodel_name='res.partner',
            string='Customer'
    )

    x_my_companyintercompany_marginsplit = fields.Many2one(string='Margin Split', related="partner_id.x_my_companyintercompany_marginsplit")
    x_my_companyintercompany_marginsplitid = fields.Char(related="x_my_companyintercompany_marginsplit.name", string="Margin Split")

    x_prsmarginpercentage = fields.Float(string="Marge %")

    @api.depends('order_line.margin')
    def _compute_margin(self):
        amount_untaxed = self.amount_untaxed
        if self.x_my_companyintercompany_marginsplit:
            try:
                if self.x_my_companyintercompany_marginsplitid == "Total -2,5%":
                    totalordercost = 0.0
                    for line in self.order_line:
                        totalordercost += line.purchase_price * line.product_uom_qty
                    intercompanymargin = amount_untaxed * 0.025
                    self.x_prsmargin = amount_untaxed - totalordercost - intercompanymargin
                elif self.x_my_companyintercompany_marginsplitid == "Marge 50/50":
                    self.x_prsmargin = self.margin / 2
                else:
                    self.x_prsmargin = self.margin
            except:
                raise "Exception!"
        else:
            self.x_prsmargin = self.margin
        if amount_untaxed > 0.0:
              self.x_prsmarginpercentage = self.x_prsmargin / amount_untaxed * 100
        else:
               self.x_prsmarginpercentage = 0.0
    x_prsmargin = fields.Monetary(compute='_compute_margin', store='true')

Upvotes: 2

Views: 3318

Answers (2)

CZoellner
CZoellner

Reputation: 14778

The compute method will be called with multiple records, which are provided in self. But you try to get attributes directly from self, which only works on singletons (record sets with exactly one record).

So you have two options to fix the problem:

  1. Rewrite your method as for-loop on self. You should also decorate the method with @api.multi. Simple example:
@api.multi
@api.depends('field1', 'field2', 'fieldn')
def _compute_my_field(self):
    for record in self:
        record.my_field = 4711
  1. Just decorate your compute method with @api.one to tell Odoo to loop on it for every record in self. Odoo will loop on every record in self and will gather the method return values in a list which will be the return value in the end of the loop. You can use self in a way you are using already: as a singleton.

Upvotes: 2

Travis Waelbroeck
Travis Waelbroeck

Reputation: 2135

You need to decorate your compute method with @api.multi.

Basically, when Odoo tries to initialize your new field in the database, it is going to compute for many records at once. The way you have your method set up currently can only support a single record at a time, which is why you get the Expected singleton message.

Try with following:

# First, include the @api.multi decorator
@api.multi
@api.depends('order_line.margin')
def _compute_margin(self):
    # Second, use a for loop to loop over self
    # because it's possible for self to be multiple records
    for order in self:
        # Finally, use your same method logic in the loop...

        # Except that you must assign the result **per record**
        # in the loop (with `order` in this example instead of `self`)
        order.x_prsmarginpercentage = ...

See more details in the Odoo ORM Documentation

Upvotes: 2

Related Questions