Arsalan Sherwani
Arsalan Sherwani

Reputation: 560

ValueError Expected singleton , Odoo8

I have been working on a module in Odoo8 on Ubuntu 14.04. I have come up to a strange issue when saving a form record based on some One2many fields. The error says

ValueError

Expected singleton: hr.employee.pay.change(84, 85) 

My Python code is as below

class hr_employee_pay_change(models.Model):

    _name='hr.employee.pay.change'
    hr_payroll_change_ids = fields.Many2one("employee.salary.change", "Employee", ondelete="cascade")

    @api.onchange('emp_basic', 'emp_allowance')
    @api.depends('emp_basic', 'emp_allowance')
    def _current_total(self):
        self.emp_current_total = self.emp_basic + self.emp_allowance



    @api.onchange('emp_propose_allowance', 'emp_propose_basic')
    @api.depends('emp_propose_allowance', 'emp_propose_basic')
    def _proposed_total(self):
        data_val={}
        self.emp_propose_total = self.emp_propose_basic + self.emp_propose_allowance
        cr=self._cr
        uid=self._uid
        ids=self._ids  
        val=int(self.employee_name)
        if val:
           cr.execute("select max_salary,min_salary from hr_job where id in (select job_id from hr_employee where id='"+str(val)+"')")
           res=cr.fetchall()
           for data_val in res:
               max_sal=data_val[0]
               min_sal=data_val[1]
           if not min_sal < self.emp_propose_total < max_sal: 
              self.emp_propose_basic = 0.0
              self.emp_propose_allowance = 0.0
              return {'warning':{'title':'warning','message':'Out of Range, Proposed Total must be in between "'+str(max_sal)+'"to"'+str(min_sal)+'"'}}
        else:
           cr.execute("select wage from hr_contract where employee_id=0")    




    @api.onchange('employee_name')
    @api.depends('employee_name')
    def get_data(self):
        data={}
        cr=self._cr
        uid=self._uid
        ids=self._ids     
        value=int(self.employee_name)        
        if(self.employee_name):  
           cr.execute("select wage,allowance from hr_contract where employee_id ='"+str(value)+"'")
           res=cr.fetchall()
           for data in res:
               self.emp_basic=data[0]
               self.emp_allowance = data[1]

        else:     
           cr.execute("select wage,allowance from hr_contract where employee_id=0")   



    employee_name = fields.Many2one('hr.employee', 'Employee Name', required=True )
    zeo_number = fields.Char(related='employee_name.zeo_number', string='ZEO Number', readonly=True )
    emp_basic = fields.Float('Basic Salary',  compute='get_data',readonly=True, store=True )
    emp_allowance = fields.Float('Allowance', compute='get_data',readonly=True, store=True )
    emp_current_total = fields.Float('Totals', compute='_current_total', store=True, track_visibility='always')
    emp_propose_basic = fields.Float('Proposed Basic')
    emp_propose_allowance = fields.Float('Proposed Allowance')
    emp_propose_total = fields.Float('Proposed Totals', compute='_proposed_total', store=True, track_visibility='always')

I am unable to get this issue . I tried to remove the 'readonly=True' property of the field and the issue gets fixed but I need to have them as readonly . Hopes for suggestion

Upvotes: 3

Views: 14355

Answers (1)

Expected Singleton:

Class methods required single invoking object (Single Browsable Record) to invoke the method and suppose it will call by multiple invoking objects (Browsable Recordsets) then method is not able to identify for which object it should process, therefore it will raise an error Expected Singleton.

New API decorator is used to define method calling pattern whether methods allows only single object or multiple objects to invoke this method.

@api.one

This decorator loops automatically on Records of RecordSet for you. Self is redefined as current record

Note: Caution: the returned value is put in a list. This is not always supported by the web client, e.g. on button action methods. In that case, you should use @api.multi to decorate your method, and probably call self.ensure_one() in the method definition.

@api.multi

Self will be the current RecordSet without iteration. It is the default behavior (multiple browsable objects). Methods which returns non premitive type data(list, dictionary, function) must be decorated with @api.multi

@api.model

This decorator will convert old API calls to decorated function to new API signature. It allows to be polite when migrating code. Self does not contain any record/recordset in methods which are decorated by this decorator.

So simply call like this

self.env['model_name'].method_name(arguments)

You should try following,

class hr_employee_pay_change(models.Model):
    _name='hr.employee.pay.change'
    hr_payroll_change_ids = fields.Many2one("employee.salary.change", "Employee", ondelete="cascade")

    @api.onchange('emp_basic', 'emp_allowance')
    @api.depends('emp_basic', 'emp_allowance')
    def _current_total(self):
        for rec in self:
            rec.emp_current_total = rec.emp_basic + rec.emp_allowance

    @api.onchange('emp_propose_allowance', 'emp_propose_basic')
    @api.depends('emp_propose_allowance', 'emp_propose_basic')
    def _proposed_total(self):
        for rec in self:
            data_val={}
            rec.emp_propose_total = rec.emp_propose_basic + rec.emp_propose_allowance
            cr=self._cr
            uid=self._uid
            ids=self._ids  
            val=int(rec.employee_name)
            if val:
               cr.execute("select max_salary,min_salary from hr_job where id in (select job_id from hr_employee where id='"+str(val)+"')")
               res=cr.fetchall()
               for data_val in res:
                   max_sal=data_val[0]
                   min_sal=data_val[1]
               if not min_sal < self.emp_propose_total < max_sal: 
                  self.emp_propose_basic = 0.0
                  self.emp_propose_allowance = 0.0
                  return {'warning':{'title':'warning','message':'Out of Range, Proposed Total must be in between "'+str(max_sal)+'"to"'+str(min_sal)+'"'}}
            else:
               cr.execute("select wage from hr_contract where employee_id=0")    

    @api.onchange('employee_name')
    @api.depends('employee_name')
    def get_data(self):
        for rec in self:
            data={}
            cr=self._cr
            uid=rec._uid
            ids=rec._ids     
            value=int(rec.employee_name)        
            if(rec.employee_name):  
               cr.execute("select wage,allowance from hr_contract where employee_id ='"+str(value)+"'")
               res=cr.fetchall()
               for data in res:
                   rec.emp_basic=data[0]
                   rec.emp_allowance = data[1]

            else:     
               cr.execute("select wage,allowance from hr_contract where employee_id=0")   

Upvotes: 7

Related Questions