Accessing a field, model by group, rules and condition

 

Theory (rules and csv)

About rules

Get from this thanks to these guys for their work

Record rules are conditions that records must satisfy for an operation (create, read, write or delete) to be allowed. Example of a condition: User can update Task that assigned to him.

Group field defines for which group rule is applied. If Group is not specified, then rule is global and applied for all users.

Domain field defines conditions for records.

Boolean fields (read, write, create, delete) of ir.rule mean Apply this rule for this kind of operation. They do not mean restrict access for this kind of operation.

Checking access algorithm

To check either user has access for example to read a record, system do as following:

  • Check access according to ir.model.access records. If it doesn’t pass, then user doesn’t get access
  • Find and check global rules for the model and for read operation
    • if the record doesn’t satisfy (doesn’t fit to domain) for at least one of the global rules, then user doesn’t get access
  • Find and check non-global rules for the model, which perm_read equal True and groups that intersect with current user groups.
    • if there are no such rules, then user get access
    • if the record satisfy (fit to domain) for at least one of the non-global rules, then user get access
    • if the record doesn’t satisfy for all non-global rules, then user doesn’t get access

Real task, case

I recently encountered a problem that I could not solve for a long time, although I already thought that such problems did not exist for me in odoo :)

So we have sale.order and sale.order.line in it.

And we need to prevent some users who belong to a certain group from changing and deleting sale.order.line after the compute field in sale.order takes a certain value. But if this prohibition should only apply to those sale.order.line that were created before the moment when the compute field crossed a certain threshold. And new sale.order.line users can create, delete, modify. And there is a group of users, like admins, to whom these restrictions do not apply. so my 3 steps before i made a working solution

Implementation in code

1. variant XML attributes

<record id="view_order_form_cust_ref_readonly" model="ir.ui.view">
    <field name="name">sale.order.form.readonly.cust</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_order_form"/>
    <field name="groups_id" eval="[(6, 0, [ref('base.group_user') ])]"/>
    <field name="arch" type="xml">
        <field name='client_order_ref' position="attributes">
            <attribute name="attrs">
                {'readonly':[('state','not in',['draft','sent'])]}
            </attribute>
        </field>
    </field>

</record> 

Let's analyze it in detail. This code determines for all users with the base.group_user group to change the attribute of the client_order_ref field to readonly by condition. Thus, we can, as it were, prohibit a certain group from recalling a field or, on the contrary, allow it, we can also change the attribute in the tree element and thereby prohibit deleting one2many rows for the group.

But what if we need to prohibit the deletion of certain lines for a certain group of users, but allow some lines to be deleted, then such a solution will not help.

2. variant fields_view_get

    @api.model
    def fields_view_get(selfview_id=Noneview_type='form'toolbar=Falsesubmenu=False):
        """
        Переопределяем вью, если пользователь не в группе которой разрешенно редактировать и удалять
        и при этом есть какая то сумма оплаченная клиенту, то выключаем возможность удаления и редактирования
        детализации
        """
        res = super(mta_saleorderself).fields_view_get(view_id=view_idview_type=view_typetoolbar=toolbarsubmenu=submenu)
        if view_type == 'form':
            _logger.warning("!!!!"*10)
            _logger.warning(self._context)
            if not self.user_has_groups('mta_base.mta_sale_order_line_editor'):
                active_id = self._context.get('params'and self._context.get('params').get('id'or self._context.get('active_id'False)
                if active_id:
                    if self.env['sale.order'].browse(active_id).mt_detail_balance_supp > 0:
                        doc = etree.fromstring(res['fields']['order_line']['views']['tree']['arch'])
                        # выключаем режим редактировани и удаления
                        for tree in doc.xpath("//tree"):
                            tree.set('delete''0')
                            tree.set('edit''0')
                            tree.set('create''1')
                            # tree.set('editable', 'top')
                            # tree.set('editable', 'bottom')
                        # все поля реадонли, потому что режим выключенного редактирования не работает (баг в текущем инстансе)
                        for field in doc.xpath("//field"):
                            if field.get('modifiers'):
                                modifiers = json.loads(field.get('modifiers'))
                                modifiers['readonly'] = True
                                field.set('modifiers'json.dumps(modifiers))
                        res['fields']['order_line']['views']['tree']['arch'] = etree.tostring(docencoding='unicode')

                        doc = etree.fromstring(res['fields']['details_ids']['views']['tree']['arch'])
                        # выключаем режим редактировани и удаления
                        for tree in doc.xpath("//tree"):
                            tree.set('delete''0')
                            tree.set('edit''0')
                            tree.set('create''1')
                            # tree.set('editable', 'top')
                        # все поля реадонли, потому что режим выключенного редактирования не работает (баг в текущем инстансе)
                        for field in doc.xpath("//field"):
                            if field.get('modifiers'):
                                modifiers = json.loads(field.get('modifiers'))
                                modifiers['readonly'] = True
                                field.set('modifiers'json.dumps(modifiers))
                        res['fields']['details_ids']['views']['tree']['arch'] = etree.tostring(docencoding='unicode')
        return res

We can override the fields view_get function which returns the xml view for the form, but it will also only allow dynamically changing attributes and will not allow deleting one2many fields in some new lines

3. variant (work) write, unlink,create function override

And in the end we come to the option that is less attractive from the user's point of view, but the most flexible. We can add logic in python to allow deleting only those records in one2many that meet our conditions. The only difference is that the user will see an error after clicking the save button, which is not very convenient, but it closes our task.

Odoo windows services and rename folder
services and nssm