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

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.

Related

How can I find out the difference between when user changes fields and when field fills automatically from record on the table in onchange function?

I have a many2one field as A and one2many field as A_details that A_details has filter base on A,
A = fields.Many2one(comodel_name="headertable")
A_details = fields.One2many(comodel_name="detailtable")
and in the xml I pass the A value with context in A_detail to filter it
<field name="A_detail" context="{'parent_id': A,}"/>
now I want to delete A_detail's records when user changes the A value, so I use onchange decorator on A field like this:
#api.onchange('A')
def _delete_selected_records(self):
for rec in self.A_details:
self.A_details = [(3, rec.id, 0)]
this function workes correctly to create mode but the problem occurs when I open the record from tree view and while A field is getting value from model Onchanged decorator call the _delete_selected_records function and it delete all the A_detail's records, That's why I want to check in this function that if user change the A, delete the A_detail's records else if system sets value to field do nothing... how can I handle this???
You should add a check if A is deleted or not meaning if A is False then do X:
#api.onchange('A')
def _delete_selected_records(self):
for rec in self:
if rec.A is False:
rec.A_details = [(3, rec.id, 0)] # You can you .unlink() btw
else:
continue
This way even if the function is triggered by let's say a user accidentally putting cursor on the field in the view or something you won't risk deleting anything unless the correct condition is met.

How to update/ delete a key from a context in Odoo 10?

Hi i am trying to delete a key value from the context for res.partner form view.
I opening the partner form view using controller function and trying to set phone number as default and its working fine. But when i try to create a new customer by clicking on the create button the phone number again auto-filled. In order to avoid this behaviour, in default_get function, i copied the context into another variable, removed the key value from the context using del context['cc_mobile']. And reassigned to self.env.context. But when i try to create a new customer, the deleted key value comes in the context again.
Controller.py
#http.route('/open_customer/<string:val>', type="http",method=['POST','GET'],website=False, auth="public")
def open_case_window(self,**kw):
mobile_no = kw.get('val')
action = request.env.ref('base.action_partner_form').sudo()
mobile_flag = 0
partner = 'res.partner'
partner_model = request.env[partner]
regex = re.match( '^(?:\01|02|03|04|06|07|09)\d*$', mobile_no)
if regex:
mobile_flag = 0
partner_id = partner_model.search([('phone', '=', mobile_no)]).id
else:
mobile_flag = 1
partner_id = partner_model.search([('mobile','=',mobile_no)]).id
if partner_id:
return werkzeug.utils.redirect('/web#id='+str(partner_id)+'&view_type=form&model='+partner)
else:
context = dict(action._context)
if mobile_flag == 0:
context.update({'cc_phone': mobile_no})
else:
context.update({'cc_mobile': mobile_no})
context.pop('lang')
url = werkzeug.utils.redirect('/web?debug=#view_type=form&model='+str(partner)+'&action=%s'%(action.id))
return url
ResPartner.py
#api.model
def default_get(self, fields):
context = self.env.context.copy()
print'default_get context',context
res = super(Partner, self).default_get(fields)
if 'cc_mobile' in context:
res.update({'mobile':context.get('cc_mobile')})
if 'cc_phone' in context:
res.update({'phone':context.get('cc_phone')})
if context.get('cc_mobile'):
del context['cc_mobile']
if context.get('cc_phone'):
del context['cc_phone']
self.env.context = context
print'self.env.context after',self.env.context
action = self.env.ref('base.action_partner_form').sudo()
action.env.context = self.env.context
return res
You cannot remove a key of action context from python side, because it's in the client side. when ever you call the server like search in many2one field, create a record in fly you will see this context comeback again every time (The way Odoo work).
What you need is something that will be used for one time, I think you need some kind of persistence for example:
dummy model that contains user_id, model_name, value, active fields so in the controller you create a record for default value for that specific user.
get that value by overriding default_get by searching with user_id and model_name field and hide that value or delete it.
this way when yo hit create button or create contact in fly when you search for the value it will be gone so it will not be used a second time.
This a simple Idea and easy to implement, you need to handle some cases to prevent user from saving two default value if some interruption happens should not be hard.
Edit
After second thought to prevent any error when you create a record just pass it's ID in the context with a special key, then use That Id to retrieve it, use it then delete it. easier, safer and no need for search.

How to fill a field by date now from a click of a button

I want to fill a field by date now from a click of a button of the action
class sale_inheritd(models.Model):
_inherit = 'sale.order'
#api.multi
def action_sale_temporary(self):
for order in self:
self.env['project.project'].search([('project_id', '=', 'related_project_id')]).write({'temporary_reception_date':datetime.date.today()})
order.write({'state': 'temporary'})
What is the problem with this function?
I didn't understand the domain that you have passed to search method but if you want to fill a field with type Date:
.write({'temporary_reception_date':fields.Date.today()})
and for Datetime field:
.write({'temporary_reception_date':fields.Datetime.now()})
Note: and don't use self to access a field inside the loop exm: self.related_project_id use order.related_project_id instead or you most likely will have Singleton Error
EDITS: as #CZoellner said it's better to fields.Date.context_today(self) because that will prevent problems with user timezones
From your question it is not clear what are you trying to accomplish.
Your search method you search for project_id[looks like many2one field] equals to a string.
If it is many2one field then pass id to get correct result.
Also make sure self.env['project.project'].search([('project_id', '=', 'related_project_id')]) returns a single record only else it will lead to singleton error.
If there is only one record returned by search method then there is no issue in writing to the record like you did.

Odoo 10 getting the id of the selected value of many2one

I have this many2one field that I want to get the ID of the selected value
service = fields.Many2one('mainservices.mtvehlog', string='Select Service(s)')
so onchange i want to know the id and do something after knowing (I want to make other fields invisible)
What if the many2one is multi selection? How do you get the ID's that have been selected?
#api.depends('service')
def _compute_hide(self):
source_obj=self.pool.get('mainservices.mtvehlog').browse(self.service)
if source_obj == '1':
self.services_selected = source_obj
self.hide = True
else:
self.services_selected = source_obj
self.hide = False
kindly help
In compute methods using depends decorator always loop throuth self first.
for rec in self:
# and this is how you get the id value
#you don't have to browse you have direct acces to properties of the selected value
if rec.service.id == somthing :
# and you will not get except singlton error
you question is not clear what you want to do with the id value exactly.

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