Write Access rules to group depending on a field

Hi,

For some models, we allow “admin rights” to users. For instance, creating products for opportunities.
But, if we intend to sell those products, we need to validate some details (accounting, customs, measurements) and then prevent user to update data once the product is validated. In this case, we use the nan-tic module https://github.com/NaN-tic/trytond-product_validate which add a “validate” boolean field on the template form. This field is only writeable by a specific group “product_validation”.
Once a product is validated, users having “product admin” rights should not update this product. This could only be done by user in “product_validation” group.

So i add this rule to prevent users in “product_admin” group to write in product model if field “validated” is true:

        <record model="ir.rule.group" id="rule_group_product_validated">
           <field name="name">Product Validated</field>
           <field name="model" search="[('model', '=', 'product.template')]"/>
           <field name="global_p" eval="False"/>
           <field name="default_p" eval="False"/>
           <field name="perm_read" eval="True"/>
           <field name="perm_write" eval="False"/>
           <field name="perm_create" eval="False"/>
           <field name="perm_delete" eval="False"/>
       </record>
       <record model="ir.rule" id="rule_product_validated">
           <field name="domain" eval="[('validated', '=', True)]" pyson="1"/>
           <field name="rule_group" ref="rule_group_product_validated"/>
       </record>
       <record model="ir.rule.group-res.group" id="rule_group_product_validated_group_product_admin">
           <field name="rule_group" ref="rule_group_product_validated"/>
           <field name="group" ref="product.group_product_admin"/>
       </record>

With this rule, i only see validated products and i can’t write them which is correct.
But my products which are not validated are not displayed !

So i’ve 2 groups:

  • product_admin
  • product_validation

Product_admin has the default rights to create, update, delete product but can’t “validate” a product. Once a product is validated, product_admin can no more update product.

Product_validation group has all rights on product (validate, and also updating a validated product).

Need some help to set correct rules depending of this behaviour. Thanks!

In contrary to other access rights, the Rules are removing access instead of giving (it is a kind of filter).
So your rule limit the read access for the group to only product validated.

Instead you must reverse the rules to give write access to only non-validated products.

I applied this way:

        <record model="ir.rule.group" id="rule_group_product_validated">
           <field name="name">Product Validated</field>
           <field name="model" search="[('model', '=', 'product.template')]"/>
           <field name="perm_write" eval="True"/>
       </record>
       <record model="ir.rule" id="rule_product_validated">
           <field name="domain" eval="[('validated', '=', False)]" pyson="1"/>
           <field name="rule_group" ref="rule_group_product_validated"/>
       </record>
       <record model="ir.rule.group-res.group"
           id="rule_group_product_validated_group_product_admin">
           <field name="rule_group" ref="rule_group_product_validated"/>
           <field name="group" ref="product.group_product_admin"/>
       </record>

but in this case, i don’t see anymore validated products. It’s only displaying not validated products (and i can update them).

I guess the rule is not correctly updated.
Removing a <field/> does not change its value.

Is this a bug ? How correctly update the rule ?
Or should i add other perm too ?

To ensure the value of a <field/> through update, you must always have it declared.
Removing a <field/> from the XML means that you do not care anymore of its value.

Still struggling with rules…

So i define a group product_management similar to product_admin.
Only group product_admin can write boolean field “validate” on product template.
Product_management group can not update nor delete product template having field validate = ‘True’
I define a rule to allow write/delete permission on group product_management when validated field is False.

Products with validated field = True are not visible… even for admin (But i need to see all products!)
As admin, i can’t save a template with validated=True (not allowed to write records… because of my rule).

Need some help to understand…

Here’s a part of my code:

<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
     copyright notices and license terms. -->
<tryton>
    <data>
        <record model="res.group" id="group_product_management">
            <field name="name">Product Management</field>
        </record>
        <record model="ir.model.access" id="access_product_management">
            <field name="model" search="[('model', '=', 'product.product')]"/>
            <field name="group" ref="group_product_management"/>
            <field name="perm_read" eval="True"/>
            <field name="perm_write" eval="True"/>
            <field name="perm_create" eval="True"/>
            <field name="perm_delete" eval="True"/>
        </record>
        <record model="ir.model.access" id="access_product_template_management">
            <field name="model" search="[('model', '=', 'product.template')]"/>
            <field name="group" ref="group_product_management"/>
            <field name="perm_read" eval="True"/>
            <field name="perm_write" eval="True"/>
            <field name="perm_create" eval="True"/>
            <field name="perm_delete" eval="True"/>
        </record>

        <record model="ir.model.field.access"
                id="model_field_access_product_validated">
            <field name="field"
                search="[('model.model', '=', 'product.template'), ('name', '=', 'validated')]"/>
            <field name="perm_read" eval="True"/>
            <field name="perm_write" eval="False"/>
        </record>
        <record model="ir.model.field.access"
                id="model_field_access_product_validated_group_product_admin">
            <field name="field"
                search="[('model.model', '=', 'product.template'), ('name', '=', 'validated')]"/>
            <field name="group" ref="product.group_product_admin"/>
            <field name="perm_read" eval="True"/>
            <field name="perm_write" eval="True"/>
        </record>

        <record model="ir.rule.group" id="rule_group_product_not_validated">
           <field name="name">Product Not Validated</field>
	   <field name="model" search="[('model', '=', 'product.template')]"/>
	   <field name="perm_write" eval="True"/>
	   <field name="perm_delete" eval="True"/>
       </record>
       <record model="ir.rule" id="rule_product_not_validated">
           <field name="domain" eval="[('validated', '=', False)]" pyson="1"/>
           <field name="rule_group" ref="rule_group_product_not_validated"/>
       </record>
       <record model="ir.rule.group-res.group"
           id="rule_group_product_not_validated_group_product_management">
           <field name="rule_group" ref="rule_group_product_not_validated"/>
           <field name="group" ref="group_product_management"/>
       </record>
...

There is no special rule for admin. So if you create a new group you should assign it to the admin user so he is able to perform this operation also. As the admin user is created with an xml record, you can assign the group on your xml group so this is automatically done when updating your module. You can have a look at the account module to see how it’s done.

I don’t want admin user to be part of this new group ‘product_management’. Admin is part of group “product_admin” with all the rights on products. Product_management group will be similar of group “product_admin” except i don’t want people of this group could update or delete products having “validated” field = True.
So i try to define a rule for this: Everybody can see all products, product_admin can do anything, product_management can do anything except updating or deleting validated products.

Nice question :smile: I’m always in for a challenge so I fiddled around a bit.

I’ve tried your idea on the trunk version of Tryton, so no idea if it will work with your version of Tryton. Also I made a small change to your idea, I used the salable field and made the rules in the GUI rather then in XML. I will leave that to you.

I’ve created a record rule with an empty domain.

  • model: product.template
  • only ‘read’ access
  • both ‘global’ and ‘default’ unchecked
  • domain, leave it empty or add '[]'

Just create a special group for them and add the empty rule, but also add all the rights on the product.template model on the model access tab

Create a new record rule with [["salable", "=", True"]] as domain

  • model: product.template
  • all access
  • both ‘global’ and ‘default’ unchecked

Create a new group, and add all access on the product.template model like the group above. Also add the two newly created record rules to the group.

It’s very rough but hopefully it will help you a bit.

1 Like

I think i found a solution :slight_smile:
Thanks to all! Don’t hesitate to comment if this could be improved.

I seems working with this code:

...
        <record model="ir.rule.group" id="rule_group_product_not_validated">
           <field name="name">Product Not Validated</field>
		   <field name="model" search="[('model', '=', 'product.template')]"/>
		   <field name="global_p" eval="False"/>
		   <field name="default_p" eval="False"/>
		   <field name="perm_read" eval="False"/>
		   <field name="perm_write" eval="True"/>
		   <field name="perm_delete" eval="True"/>
       </record>
       <record model="ir.rule" id="rule_product_not_validated">
           <field name="domain" eval="[('validated', '=', False)]" pyson="1"/>
           <field name="rule_group" ref="rule_group_product_not_validated"/>
       </record>
       <record model="ir.rule.group-res.group"
           id="rule_group_product_not_validated_group_product_management">
           <field name="rule_group" ref="rule_group_product_not_validated"/>
           <field name="group" ref="group_product_management"/>
       </record>

        <record model="ir.rule.group" id="rule_group_product_admin_all_perm">
           <field name="name">Product All Perm Admin</field>
           <field name="model" search="[('model', '=', 'product.template')]"/>
	       <field name="global_p" eval="False"/>
	       <field name="default_p" eval="False"/>
           <field name="perm_read" eval="True"/>
           <field name="perm_write" eval="True"/>
           <field name="perm_create" eval="True"/>
           <field name="perm_delete" eval="True"/>
        </record>
       <record model="ir.rule.group-res.group"
           id="rule_group_product_admin_all_perm_group_product_admin">
           <field name="rule_group" ref="rule_group_product_admin_all_perm"/>
           <field name="group" ref="product.group_product_admin"/>
       </record>
...

Since my last post, i met some problem with field access rules to duplicate a record. (Setting perm_write=False to a field doesn’t allow to duplicate a record). So i update my code to set field ‘readonly’ depending on field state attribute instead on field access rules.

To resume, i need to allow some users to have some rights to manage products but they are not product_admin.
So, i’ve defined a new group product_management similar to product_admin.
I’ve added a boolean field “Validated” on product template. Only group product_admin can write boolean field “validate” on product template.
Product_management group can not update nor delete product template once field validate is ‘True’.

Here’s my code:

product.py

from trytond.model import fields
from trytond.pool import PoolMeta
from trytond.pyson import Eval, Id


class Template(metaclass=PoolMeta):
    __name__ = 'product.template'

    validated = fields.Boolean('Validated',
        states={
            'readonly': ~Id('product', 'group_product_admin').in_(
                Eval('context', {}).get('groups', [])),
            })

    @classmethod
    def default_validated(cls):
        return False

    @classmethod
    def copy(cls, templates, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('validated', False)
        return super().copy(templates, default=default)

product.xml

<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
     copyright notices and license terms. -->
<tryton>
    <data>
        <record model="res.group" id="group_product_management">
            <field name="name">Product Management</field>
        </record>
        <record model="ir.model.access" id="access_product_management">
            <field name="model" search="[('model', '=', 'product.product')]"/>
            <field name="group" ref="group_product_management"/>
            <field name="perm_read" eval="True"/>
            <field name="perm_write" eval="True"/>
            <field name="perm_create" eval="True"/>
            <field name="perm_delete" eval="True"/>
        </record>
        <record model="ir.model.access" id="access_product_template_management">
            <field name="model" search="[('model', '=', 'product.template')]"/>
            <field name="group" ref="group_product_management"/>
            <field name="perm_read" eval="True"/>
            <field name="perm_write" eval="True"/>
            <field name="perm_create" eval="True"/>
            <field name="perm_delete" eval="True"/>
        </record>
        <record model="ir.ui.view" id="product_template_view_form">
            <field name="model">product.template</field>
            <field name="inherit" ref="product.template_view_form"/>
            <field name="name">product_template_form</field>
        </record>

        <record model="ir.rule.group" id="rule_group_product_not_validated">
            <field name="name">Product Not Validated</field>
            <field name="model" search="[('model', '=', 'product.template')]"/>
            <field name="global_p" eval="False"/>
            <field name="default_p" eval="False"/>
            <field name="perm_read" eval="False"/>
            <field name="perm_write" eval="True"/>
            <field name="perm_delete" eval="True"/>
        </record>
        <record model="ir.rule" id="rule_product_not_validated">
            <field name="domain" eval="[('validated', '=', False)]" pyson="1"/>
            <field name="rule_group" ref="rule_group_product_not_validated"/>
        </record>
        <record model="ir.rule.group-res.group"
            id="rule_group_product_not_validated_group_product_management">
            <field name="rule_group" ref="rule_group_product_not_validated"/>
            <field name="group" ref="group_product_management"/>
        </record>

        <record model="ir.rule.group" id="rule_group_product_all_perm_admin">
            <field name="name">Product All Perm Admin</field>
            <field name="model" search="[('model', '=', 'product.template')]"/>
            <field name="global_p" eval="False"/>
            <field name="default_p" eval="False"/>
            <field name="perm_read" eval="True"/>
            <field name="perm_write" eval="True"/>
            <field name="perm_create" eval="True"/>
            <field name="perm_delete" eval="True"/>
        </record>
        <record model="ir.rule.group-res.group"
            id="rule_group_product_all_perm_admin_group_product_admin">
            <field name="rule_group" ref="rule_group_product_all_perm_admin"/>
            <field name="group" ref="product.group_product_admin"/>
        </record>
    </data>
</tryton>

view/template_form.xml

<?xml version="1.0"?>
<!-- The COPYRIGHT file at the top level of this repository contains the full
     copyright notices and license terms. -->
<data>
    <xpath expr="/form/notebook" position="before">
        <label name="validated"/>
        <field name="validated"/>
    </xpath>
</data>

Feel free to comment…

1 Like

For the record, this works because readonly is not yet enforced on the server side Issue 4207: Enforce readonly on field - Tryton issue tracker

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.