3
0
Fork 0

Merge PR #2502 into 14.0

Signed-off-by pedrobaeza
14.0
OCA-git-bot 2023-06-23 08:44:00 +00:00
commit bd93dfb4f2
15 changed files with 359 additions and 0 deletions

View File

@ -0,0 +1 @@
../../../../web_field_required_invisible_manager

View File

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

View File

@ -0,0 +1 @@
from . import models

View File

@ -0,0 +1,15 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
{
"name": "Web Field Required Invisible Manager",
"category": "Web",
"version": "14.0.1.0.0",
"license": "AGPL-3",
"author": "Ilyas, ooops404, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/web",
"maintainers": ["ilyasProgrammer"],
"depends": ["base"],
"data": [
"security/ir.model.access.csv",
"views/views.xml",
],
}

View File

@ -0,0 +1,2 @@
from . import custom_field_restriction
from . import models

View File

@ -0,0 +1,58 @@
# Copyright 2020 ooops404
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
from odoo import api, fields, models
class CustomFieldRestriction(models.Model):
_name = "custom.field.restriction"
_description = "Make field invisible or required"
field_id = fields.Many2one(
"ir.model.fields",
ondelete="cascade",
required=True,
string="Field",
)
field_name = fields.Char(
related="field_id.name",
store=True,
string="Field Name",
)
required_model_id = fields.Many2one(
"ir.model",
ondelete="cascade",
string="required_model_id",
index=True,
)
invisible_model_id = fields.Many2one(
"ir.model",
ondelete="cascade",
string="invisible_model_id",
index=True,
)
model_name = fields.Char(
compute="_compute_model_name",
store=True,
string="Model Name",
index=True,
)
condition_domain = fields.Char()
group_ids = fields.Many2many("res.groups", required=True)
required = fields.Boolean()
default_required = fields.Boolean(related="field_id.required")
field_invisible = fields.Boolean()
@api.onchange("field_id")
def onchange_field_id(self):
self.required = self.field_id.required
@api.depends("required_model_id", "invisible_model_id")
def _compute_model_name(self):
for rec in self:
if rec.required_model_id:
rec.model_name = rec.required_model_id.model
elif rec.invisible_model_id:
rec.model_name = rec.invisible_model_id.model

View File

@ -0,0 +1,79 @@
# Copyright 2020 ooops404
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
import json as simplejson
from lxml import etree
from odoo import api, fields, models
from odoo.tools.safe_eval import safe_eval
class IrModel(models.Model):
_inherit = "ir.model"
custom_required_restriction_ids = fields.One2many(
"custom.field.restriction",
"required_model_id",
)
custom_invisible_restriction_ids = fields.One2many(
"custom.field.restriction",
"invisible_model_id",
)
class Base(models.AbstractModel):
_inherit = "base"
@api.model
def fields_view_get(
self, view_id=None, view_type=False, toolbar=False, submenu=False
):
res = super().fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
)
if view_type not in ["form", "tree", "kanban"]:
return res
# TODO speed up somehow
restrictions = self.env["custom.field.restriction"].search(
[
"|",
("model_name", "=", self._name),
("group_ids", "in", self.env.user.groups_id.ids),
]
)
if not restrictions:
return res
doc = etree.XML(res["arch"])
for node in doc.xpath("//field"):
name = node.attrib.get("name")
restrictions_filtered = restrictions.filtered(
lambda x: x.field_id.name == name
)
for r in restrictions_filtered:
if (
view_type == "form"
and self.env.context.get("params")
and self.env.context["params"].get("id")
):
rec_id = self.env[r.model_name].browse(
self.env.context["params"]["id"]
)
if r.condition_domain:
filtered_rec_id = rec_id.filtered_domain(
safe_eval(r.condition_domain)
)
if not filtered_rec_id:
continue
if r.required:
node.set("required", "1")
modifiers = simplejson.loads(node.get("modifiers"))
modifiers["required"] = True
node.set("modifiers", simplejson.dumps(modifiers))
res["arch"] = etree.tostring(doc)
if r.field_invisible:
node.set("invisible", "1")
modifiers = simplejson.loads(node.get("modifiers"))
modifiers["invisible"] = True
node.set("modifiers", simplejson.dumps(modifiers))
res["arch"] = etree.tostring(doc)
return res

View File

@ -0,0 +1,3 @@
* `Ooops404 <https://www.ooops404.com>`__:
* Ilyas <irazor147@gmail.com>

View File

@ -0,0 +1,3 @@
This module allows to set a field required or invisible for users belonging to a specific group.
The field can be required or invisible in any case, or according to specific conditions.

View File

@ -0,0 +1,7 @@
Go to Settings > Technical > Models
Select a model > in tab "Custom required fields" or "Custom invisible fields" add a line
Select a field, add one or more group and enable flag "Required" or "Invisible"
If needed, set a condition for which the field should be required or invisible for users of those groups.

View File

@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
cfr1,cfr1,model_custom_field_restriction,base.group_system,1,1,1,1
cfr2,cfr2,model_custom_field_restriction,base.group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 cfr1 cfr1 model_custom_field_restriction base.group_system 1 1 1 1
3 cfr2 cfr2 model_custom_field_restriction base.group_user 1 0 0 0

View File

@ -0,0 +1,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_web_field_required_invisible_manager

View File

@ -0,0 +1,68 @@
# Copyright 2023 ooops404
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.tests import common
class TestFieldRequiredIvisibleManager(common.SavepointCase):
@classmethod
def setUpClass(cls):
super(TestFieldRequiredIvisibleManager, cls).setUpClass()
cls.partner_name_field_id = cls.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "name")]
)
cls.partner_id_field_id = cls.env["ir.model.fields"].search(
[("model", "=", "res.partner"), ("name", "=", "id")]
)
cls.partner_model_id = cls.partner_name_field_id.model_id
cls.partner_title_model_id = cls.env["ir.model"].search(
[("name", "=", "res.partner.title")]
)
cls.partner_title_name_field_id = cls.env["ir.model.fields"].search(
[("model", "=", "res.partner.title"), ("name", "=", "name")]
)
cls.partner_title_shortcut_field_id = cls.env["ir.model.fields"].search(
[("model", "=", "res.partner.title"), ("name", "=", "shortcut")]
)
cls.invisible_title_rec_id = cls.env["custom.field.restriction"].create(
{
"invisible_model_id": cls.partner_title_model_id.id,
"field_id": cls.partner_title_shortcut_field_id.id,
"group_ids": [(6, 0, cls.env.ref("base.group_user").ids)],
"field_invisible": True,
}
)
cls.invisible_rec_id = cls.env["custom.field.restriction"].create(
{
"invisible_model_id": cls.partner_model_id.id,
"field_id": cls.partner_id_field_id.id,
"group_ids": [(6, 0, cls.env.ref("base.group_user").ids)],
"field_invisible": True,
"condition_domain": "[('id', '<', 0)]",
}
)
cls.required_rec_id = cls.env["custom.field.restriction"].create(
{
"required_model_id": cls.partner_model_id.id,
"field_id": cls.partner_id_field_id.id,
"group_ids": [(6, 0, cls.env.ref("base.group_user").ids)],
"required": True,
"condition_domain": "[('id', '>', 0)]",
}
)
cls.deco_addict = cls.env.ref("base.res_partner_2")
cls.partner_view = cls.env.ref("base.view_partner_simple_form")
def test_all_web_field_required_invisible_manager(self):
# onchange_field_id()
self.assertFalse(self.invisible_title_rec_id.required)
self.invisible_title_rec_id.field_id = self.partner_title_name_field_id
self.invisible_title_rec_id.onchange_field_id()
self.assertTrue(self.invisible_title_rec_id.required)
# _compute_model_name()
self.invisible_rec_id._compute_model_name()
self.assertEqual(self.invisible_rec_id.model_name, "res.partner")
self.required_rec_id._compute_model_name()
self.assertEqual(self.required_rec_id.model_name, "res.partner")
# fields_view_get()
self.deco_addict.fields_view_get(view_id=self.partner_view.id, view_type="form")

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_model_form_inherit" model="ir.ui.view">
<field name="name">view.model.form.inherit</field>
<field name="model">ir.model</field>
<field name="inherit_id" ref="base.view_model_form" />
<field name="arch" type="xml">
<xpath expr="//notebook/page[@name='fields']" position="after">
<page
string="Custom Required Fields"
name="custom_required_restriction"
>
<field
name="custom_required_restriction_ids"
nolabel="1"
context="{'default_required_model_id': model}"
>
<tree string="Fields">
<field name="required_model_id" invisible="1" />
<field name="invisible_model_id" invisible="1" />
<field name="model_name" invisible="1" />
<field
name="field_id"
context="{'search_by_technical_name': True, 'display_technical_name': True}"
domain="[('model_id.model', '=', model_name)]"
options="{'create': False, 'create_edit': False}"
/>
<field name="default_required" invisible="1" />
<field
name="required"
attrs="{'readonly':[('default_required', '=', True)]}"
/>
<field name="condition_domain" />
<field name="group_ids" widget="many2many_tags" />
</tree>
</field>
</page>
<page
string="Custom Invisible Fields"
name="custom_invisible_restriction"
>
<field
name="custom_invisible_restriction_ids"
nolabel="1"
context="{'default_invisible_model_id': model}"
>
<tree string="Fields">
<field name="required_model_id" invisible="1" />
<field name="invisible_model_id" invisible="1" />
<field name="model_name" invisible="1" />
<field
name="field_id"
context="{'search_by_technical_name': True, 'display_technical_name': True}"
domain="[('model_id.model', '=', model_name)]"
options="{'create': False, 'create_edit': False}"
/>
<field name="default_required" invisible="1" />
<field name="field_invisible" />
<field name="condition_domain" />
<field name="group_ids" widget="many2many_tags" />
</tree>
</field>
</page>
</xpath>
</field>
</record>
<record id="view_custom_field_restriction_form" model="ir.ui.view">
<field name="name">custom.field.restriction.form.view</field>
<field name="model">custom.field.restriction</field>
<field name="arch" type="xml">
<form>
<sheet>
<group><group>
<field name="required_model_id" invisible="1" />
<field name="invisible_model_id" invisible="1" />
<field name="model_name" invisible="1" />
<field
name="field_id"
context="{'search_by_technical_name': True, 'display_technical_name': True}"
domain="[('model_id.model', '=', model_name)]"
options="{'create': False, 'create_edit': False}"
/>
<field name="default_required" invisible="1" />
<field
name="required"
attrs="{'readonly':[('default_required', '=', True)],
'invisible':[('required_model_id', '=', False)]}"
/>
<field
name="field_invisible"
attrs="{'readonly':[('default_required', '=', True)],
'invisible':[('invisible_model_id', '=', False)]}"
/>
<field
name="condition_domain"
widget="domain"
options="{'model': 'model_name', 'in_dialog': True}"
/>
<field name="group_ids" widget="many2many_tags" />
</group></group>
</sheet>
</form>
</field>
</record>
</odoo>