Search code examples
pythonxmlpython-2.7odoo-8odoo

How to allow an user of a group to modify specific parts of a form in Odoo?


I've created a new group named accountant. If an user of this group opens the res.partner form for example, he must be able to read all, but only modify some specific fields (the ones inside the tab Accountancy, for example).

So I set the permissions create, write, unlink, read to 0, 1, 0, 1 in the res.partner model for the accountant group.

The problem: if I'm an user of the accountant group and I go to the form of res.partner, I will see the Edit button, if I click on it, I will be able to modify any field I want (and I should not, only the ones inside the tab).

So I thought to duplicate the menuitem (put the attribute groups="accountant" to the copy) and the form (put all fields readonly except for the content of the tab).

The problem: if I'm an user of a group over accountant group (with accountant in its implied_ids list), I will see both menuitems (the one which takes to the normal form and the one which takes to the duplicated form with the readonly fields).

Is it possible to create a menuitem which opens a specific set of views depending on the group of the user who is clicking on the mentioned menuitem? Any ideas of how can I succesfully implement this?


Solution

  • I was googling a lot and there are many people who ask this same question in Odoo forums, but nobody gives them an answer.

    Finally, I found this workaround, which in my case worked perfectly: the method field_view_get, which is in every single model, gets the XML view just before returning it to be shown. This means that you can manipulate the view from Python code in whatever way you want.

    You only must know how to use a library like lxml to modify the string variable with the XML code. I put my example:

    RES.PARTNER model (Here we must use etree from lxml library)

    @api.model
    def fields_view_get(self, view_id=None, view_type='form', toolbar=False,
                        submenu=False):
        res = super(res_partner, self).fields_view_get(
            view_id=view_id, view_type=view_type, toolbar=toolbar,
            submenu=submenu)
    
        checking_obj = self.env['my.own.checkings']
        doc = etree.XML(res['arch'])
    
        if checking_obj.belongs_to_accountant_group():
            doc.set('create', 'false')
            doc.set('delete', 'false')
            doc.set('edit', 'true')
    
        if view_type == 'form':
            accounting_pages = doc.xpath("//page[@name='accounting']")
            accounting_page = accounting_pages[0] if accounting_pages \
                else False
    
            if accounting_page is not False:
                if checking_obj.belongs_to_accountant_group():
                    all_fields = doc.xpath("//field")
                    for field in all_fields:
                        if accounting_page not in field.iterancestors():
                            checking_obj.set_modifiers(field,
                                                       {'readonly': True, })
    
        res['arch'] = etree.tostring(doc)
        return res
    

    AUXILIAR CUSTOMIZED model (named MY.OWN.CHECKINGS) (Here we must use json library)

    def update_json_data(self, json_data=False, update_data={}):
        ''' It updates JSON data. It gets JSON data, converts it to a Python
        dictionary, updates this, and converts the dictionary to JSON data
        again. '''
        dict_data = json.loads(json_data) if json_data else {}
        dict_data.update(update_data)
        return json.dumps(dict_data, ensure_ascii=False)
    
    def set_modifiers(self, element=False, modifiers_upd={}):
        ''' It updates the JSON modifiers with the specified data to indicate
        if a XML tag is readonly or invisible or not. '''
        if element is not False:  # Do not write only if element:
            modifiers = element.get('modifiers') or {}
            modifiers_json = self.update_json_data(
                modifiers, modifiers_upd)
            element.set('modifiers', modifiers_json)
    
    def is_accountant(self):
        return self.env.ref(
            'my_module.group_accountant').id in \
            self.env.user.groups_id.mapped('id')
    

    This way you can make readonly some fields depending on the group of the current user.