One2many field on_change function can't change its own value? - odoo

I have these two fields.
'name' : fields.char('Name'),
'addresses' : fields.one2many('res.partner.address', 'partner','Addresses'),
This function:
def addresses_change(self, cr, uid, ids, name, addresses, context=None):
value = {}
new_addresses = []
address_pool = self.pool.get('res.partner.address')
for address in address_pool.browse(cr, uid, addresses[0][2], context=context):
new_addresses.append((1,address.id,{'street':'wall street','zip':'7777','partner': ids[0],'active':True}))
value.update(name='whatever')
value.update(addresses=new_addresses)
return {'value':value}
And these view fields:
<field name="name" on_change="addresses_change(name,addresses)"/>
<field name="addresses" on_change="addresses_change(name,addresses)"/>
Now when I change name, both name and addresses are updated. But when I change addresses its own value isn't updated but the name is updated. So this bizarre behavior affects only one2many fields. Why is this?
And how do I add on_change event to one2many fields that can update its own value?
EDIT: I found out that this is might be a limitation in odoo, have they fixed this issue? Link to the issue

Apply the following patch on the lastest version of models.py (the version commited on Tue Aug 4 15:22:33 2015 +0200):
--- a/openerp/models.py
+++ b/openerp/models.py
## -5897,9 +5897,9 ## class BaseModel(object):
# At the moment, the client does not support updates on a *2many field
# while this one is modified by the user.
- if field_name and not isinstance(field_name, list) and \
- self._fields[field_name].type in ('one2many', 'many2many'):
- result['value'].pop(field_name, None)
+ ## if field_name and not isinstance(field_name, list) and \
+ ## self._fields[field_name].type in ('one2many', 'many2many'):
+ ## result['value'].pop(field_name, None)
return result
In other words, just comment the lines 5900 to 5902 of the openerp/models.py file.
Of course, there is a big disadvantage with this solution - you need to apply the patch every time the models.py file is updated in the Odoo distribution you use.
There is a considerable risk too - they say that the Web client is not dealing well with one2many and many2many fields updated in onchange event. I did not discover any problem with that right now but I'll continue to test my development installation of Odoo...

I created a Pull Request to odoo version 8
https://github.com/odoo/odoo/issues/2693
with the changes mentioned in the issues and here
if field_name and not isinstance(field_name, list) and \
- self._fields[field_name].type in ('one2many', 'many2many'):
+ if field_name and not isinstance(field_name, list):

Related

Odoo 8 - how to get field values of current active id or current model

How to access the fields of current form view if it is not yet save or in editing mode?
I tried getting the current active id but I cant get it.
In my .py
#api.one
def _check_butcher(self):
prod_id = self.env.context.get('active_id')
# logging.info("TEST \n TEST %s" % prod_id.can_butcher)
logging.info("TEST \n TEST %s" % prod_id)
return prod_id
In the terminal, it is printed None
I dont work with version 8 so my answer propably won't be most accurate but i think you should look for CRUD in this Documentation https://www.odoo.com/documentation/8.0/ for "default_get()"

Accesing oneToMany data from odoo model (odoo 12)

I have a list of tasks related to a project in a one-to-many relationship one project have many tasks, reading the official documentation the field access should be as easy as iterating on a list and access the field by name, this is the code I have done so far:
def _roles_assigned(self):
for rec in self:
total = len(rec.task_ids)
for e in rec.tasks
logging.info(e.status) #<-- this should print status
I print the result and get 'project.task(X,)' where I guess X is the Id of the task, how can I access the task properties?
Note: the field is a selection field
try something like this:
def _roles_assigned(self):
for tasks in self.task_ids:
logging.info(tasks.status)
Since the field is a selection I had to access the dictionary
#api.multi
#api.depends('task_ids')
def _roles_assigned(self):
for rec in self:
total = len(rec.tasks)
assigned = 0
for e in rec.tasks:
selected = dict(e._fields['status'].selection).get(e.status) #<-- getting selected value from a selection field

How can I give a user the permission to create a field but not to change it?

I wanna a user to create a record but later dun give it the right to change the value of that field. should I do it By overriding create and write methods? is it possible to write such code:
field1: fields.float(string='Field',write=['base.GROUP_ID']),
This may work create a status field this field is a compute field when it's true the field1 will be read only. Because i'm on my phone i'm not going to writr the hole code just try to understand the idea
status = field.Boolean(compute='compute_status')
def compute_status(self):
for rec in self:
# first check of the use belong to the group that have full acces
if self.env.user.has_group('group_id') :
rec.status = False
# then check if the record is saved in databse
# unsaved records There id is instance of NewId it's a dummy class used for this
elif instanceOf(NewId ,rec.id) :
rec.status = False # here all users can fill the field when the record is not created yet but cannot edit
else :
rec.status = True # if record is saved and user is not in group_id make field readonly or invisible as you want
Now create your field and use status property to make it readonly when status field is True .
As you can see my answer is algorithme more than a code sorry for sysntax errors
I think the better way to do this is to create a group to which the user will belong, then set in the ir.model.access a rule, with the rights you want, for that particular group.
Ask if you need more help.
EDIT
You can define a view, that inherit from the original one, but is accessible only for the user group, like:
<field name="groups_id" eval="[(6, 0, [ref(' < your group > ')])]"/>
and there you redefine the field making it readonly. That's it.

Odoo: get type of field by name

in odoo you can get value of field by it's str name:
exm:
name = getattr(self, 'name')
what i want now is to know the type of field name is it :
fields.Char, fields.Many2one, fields.Many2many .....
so what i need is something like this
gettype(self, 'user_id')
is there a way to now what is the type of field in odoo?
You can search from ir.model.fields model.
ir_model_obj=self.env['ir.model.fields']
ir_model_field=ir_model_obj.search([('model','=',model),('name','=',field)])
field_type=ir_model_field.ttype
if field_type=='many2one':
print "do operation"
This may help you.
Odoo provides this information in the _fields attribute, I think It's better because every thing happens In the Python side no need for contacting the database, especially In my case my model have more than 30 fields :
for name, field in self._fields.iteritems():
if not isinstance(field, (fields.Many2one, fields.Many2many, fields.One2many)):
# logic go here
If you you want to verify just one fields:
if not isinstance(self._fields[field_name], (fields.Many2one, ...)): # do something

How to update one2many in customers from accounting invoice

I have created an one2many to customer form. So, when I am validating (button validate) an invoice I am trying to pass some values in that one2many. I have tried many ways and followed odoo forums, but I am having trouble to do that. Using following code:
My one2many field is 'customer_status' in 'res.partner' :
#api.multi
#api.model
def invoice_validate(self):
customer_obj=self.env['res.partner']
customer_id=customer_obj.search([('id','=',self.partner_id.id)])
customer_line=customer_obj.browse(customer_id)
dd = {'policy_number': self.policy_no,}
customer_stat_add = customer_obj.write([customer_line.id],{
'customer_status': [(0, 0, dd)]
})
state_change = self.write({'state': 'open'})
return state_change, customer_stat_add
It gives me this error:
ValueError: "invoice_validate() takes exactly 2 arguments (1 given)" while evaluating
u'invoice_validate()'
Thanks.
buttons w/ new api need #api.multi and if you want to work on a single object you can use self.ensure_one();
#api.multi
def foo(self):
self.ensure_one()
print self.my_field
Also, you don't need to browse object as you already get browse objects w/ new api.
Bear in mind that if that feature is an odoo base feature you must call super().method_name to not break anything ;)