Reputation: 560
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
Reputation: 14746
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