diff --git a/trytond/trytond/backend/postgresql/init.sql b/trytond/trytond/backend/postgresql/init.sql
index c53645d147..6a190733eb 100644
--- a/trytond/trytond/backend/postgresql/init.sql
+++ b/trytond/trytond/backend/postgresql/init.sql
@@ -7,7 +7,7 @@ CREATE TABLE ir_configuration (
);
CREATE TABLE ir_model (
- id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL
+ id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 3) NOT NULL
CONSTRAINT ir_model_id_positive CHECK(id >= 0),
name VARCHAR NOT NULL,
string VARCHAR,
@@ -18,6 +18,9 @@ CREATE TABLE ir_model (
ALTER TABLE ir_model ADD CONSTRAINT ir_model_name_unique UNIQUE (name);
+INSERT INTO ir_model (id, name, module) VALUES (1, 'res.user', 'res');
+INSERT INTO ir_model (id, name, module) VALUES (2, 'res.group', 'res');
+
CREATE TABLE ir_model_field (
id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL
CONSTRAINT ir_model_field_id_positive CHECK(id >= 0),
@@ -35,6 +38,23 @@ CREATE TABLE ir_model_field (
ALTER TABLE ir_model_field ADD CONSTRAINT ir_model_field_name_model_uniq UNIQUE (name, model);
+CREATE TABLE ir_model_data (
+ id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 3) NOT NULL
+ CONSTRAINT ir_model_id_positive CHECK(id >= 0),
+ db_id INTEGER,
+ fs_id VARCHAR,
+ model VARCHAR NOT NULL,
+ module VARCHAR NOT NULL,
+ noupdate BOOL,
+ fs_values TEXT,
+ "values" TEXT,
+ PRIMARY KEY(id),
+ FOREIGN KEY (model) REFERENCES ir_model(name) ON DELETE CASCADE
+);
+
+INSERT INTO ir_model_data (id, db_id, fs_id, model, module, noupdate, fs_values, "values") VALUES (1, 1, 'user_admin', 'res.user', 'res', False, '[["login","admin"],["name","Administrator"]]', '[["login","admin"],["name","Administrator"]]');
+INSERT INTO ir_model_data (id, db_id, fs_id, model, module, noupdate, fs_values, "values") VALUES (2, 1, 'group_admin', 'res.group', 'res', False, '[["name","Administrator"]]', '[["name","Administrator"]]');
+
CREATE TABLE ir_ui_view (
id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL
CONSTRAINT ir_ui_view_id_positive CHECK(id >= 0),
@@ -83,7 +103,7 @@ CREATE TABLE ir_lang (
);
CREATE TABLE res_user (
- id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL
+ id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 2) NOT NULL
CONSTRAINT res_user_id_positive CHECK(id >= 0),
name VARCHAR,
active BOOLEAN NOT NULL,
@@ -95,14 +115,17 @@ CREATE TABLE res_user (
ALTER TABLE res_user ADD CONSTRAINT res_user_login_key UNIQUE (login);
INSERT INTO res_user (id, login, password, name, active) VALUES (0, 'root', NULL, 'Root', False);
+INSERT INTO res_user (id, login, password, name, active) VALUES (1, 'admin', NULL, 'Administrator', True);
CREATE TABLE res_group (
- id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL
+ id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 2) NOT NULL
CONSTRAINT res_group_id_positive CHECK(id >= 0),
name VARCHAR NOT NULL,
PRIMARY KEY(id)
);
+INSERT INTO res_group (id, name) VALUES (1, 'Administration');
+
CREATE TABLE "res_user-res_group" (
id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL
CONSTRAINT "res_user-res_group_id_positive" CHECK(id >= 0),
diff --git a/trytond/trytond/backend/sqlite/init.sql b/trytond/trytond/backend/sqlite/init.sql
index a7a2451f45..b6f2695cf0 100644
--- a/trytond/trytond/backend/sqlite/init.sql
+++ b/trytond/trytond/backend/sqlite/init.sql
@@ -14,6 +14,9 @@ CREATE TABLE ir_model (
CREATE UNIQUE INDEX ir_model_name_unique ON ir_model (name);
+INSERT INTO ir_model (id, name, module) VALUES (1, 'res.user', 'res');
+INSERT INTO ir_model (id, name, module) VALUES (2, 'res.group', 'res');
+
CREATE TABLE ir_model_field (
id INTEGER PRIMARY KEY AUTOINCREMENT,
model VARCHAR NOT NULL,
@@ -26,6 +29,19 @@ CREATE TABLE ir_model_field (
"access" BOOLEAN
);
+CREATE TABLE ir_model_data (
+ id INTEGER PRIMARY KEy AUTOINCREMENT,
+ db_id INTEGER,
+ fs_id VARCHAR,
+ model VARCHAR NOT NULL,
+ module VARCHAR NOT NULL,
+ noupdate BOOL,
+ fs_values TEXT,
+ "values" TEXT
+);
+
+INSERT INTO ir_model_data (id, db_id, fs_id, model, module, noupdate, fs_values, "values") VALUES (1, 1, 'user_admin', 'res.user', 'res', False, '[["login","admin"],["name","Administrator"]]', '[["login","admin"],["name","Administrator"]]');
+INSERT INTO ir_model_data (id, db_id, fs_id, model, module, noupdate, fs_values, "values") VALUES (2, 1, 'group_admin', 'res.group', 'res', False, '[["name","Administrator"]]', '[["name","Administrator"]]');
CREATE TABLE ir_ui_view (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -79,12 +95,15 @@ CREATE TABLE res_user (
CREATE UNIQUE INDEX res_user_login_key ON res_user (login);
INSERT INTO res_user (id, login, password, name, active) VALUES (0, 'root', NULL, 'Root', 0);
+INSERT INTO res_user (id, login, password, name, active) VALUES (1, 'admin', NULL, 'Administrator', 1);
CREATE TABLE res_group (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR
);
+INSERT INTO res_group (id, name) VALUES (1, 'Administration');
+
CREATE TABLE "res_user-res_group" (
id INTEGER PRIMARY KEY AUTOINCREMENT,
"user" INTEGER NOT NULL,
diff --git a/trytond/trytond/ir/__init__.py b/trytond/trytond/ir/__init__.py
index b8ddd055b1..cf104fb2b5 100644
--- a/trytond/trytond/ir/__init__.py
+++ b/trytond/trytond/ir/__init__.py
@@ -23,9 +23,11 @@ def register():
translation.TranslationExportStart,
translation.TranslationExportResult,
sequence.SequenceType,
+ sequence.SequenceTypeGroup,
sequence.Sequence,
sequence.SequenceStrict,
ui.menu.UIMenu,
+ ui.menu.UIMenuGroup,
ui.menu.UIMenuFavorite,
ui.view.View,
ui.view.ShowViewStart,
@@ -35,6 +37,7 @@ def register():
ui.view.ViewSearch,
ui.icon.Icon,
action.Action,
+ action.ActionGroup,
action.ActionKeyword,
action.ActionReport,
action.ActionActWindow,
@@ -46,6 +49,7 @@ def register():
model.ModelAccess,
model.ModelFieldAccess,
model.ModelButton,
+ model.ModelButtonGroup,
model.ModelButtonRule,
model.ModelButtonClick,
model.ModelButtonReset,
@@ -63,7 +67,10 @@ def register():
lang.LangConfigStart,
export.Export,
export.ExportLine,
+ export.ExportGroup,
+ export.ExportWriteGroup,
rule.RuleGroup,
+ rule.RuleGroup_Group,
rule.Rule,
module.Module,
module.ModuleDependency,
diff --git a/trytond/trytond/ir/action.py b/trytond/trytond/ir/action.py
index 1401b31e9c..5c55312d23 100644
--- a/trytond/trytond/ir/action.py
+++ b/trytond/trytond/ir/action.py
@@ -66,6 +66,9 @@ class Action(DeactivableMixin, ModelSQL, ModelView):
keywords = fields.One2Many('ir.action.keyword', 'action',
'Keywords')
icon = fields.Many2One('ir.ui.icon', 'Icon')
+ groups = fields.Many2Many(
+ 'ir.action-res.group', 'action', 'group', "Groups")
+ _groups_cache = Cache('ir.action.get_groups', context=False)
@classmethod
def __setup__(cls):
@@ -148,6 +151,47 @@ class Action(DeactivableMixin, ModelSQL, ModelView):
return self.get_action_values(self.type, [action_id])[0]
+class ActionGroup(ModelSQL):
+ "Action - Group"
+ __name__ = 'ir.action-res.group'
+ action = fields.Many2One(
+ 'ir.action', "Action", ondelete='CASCADE', required=True)
+ group = fields.Many2One(
+ 'res.group', "Group", ondelete='CASCADE', required=True)
+
+ @classmethod
+ def create(cls, vlist):
+ pool = Pool()
+ Action = pool.get('ir.action')
+ vlist = [x.copy() for x in vlist]
+ for vals in vlist:
+ if vals.get('action'):
+ vals['action'] = Action.get_action_id(vals['action'])
+ Action._groups_cache.clear()
+ return super().create(vlist)
+
+ @classmethod
+ def write(cls, records, values, *args):
+ pool = Pool()
+ Action = pool.get('ir.action')
+ actions = iter((records, values) + args)
+ args = []
+ for records, values in zip(actions, actions):
+ if values.get('action'):
+ values = values.copy()
+ values['action'] = Action.get_action_id(values['action'])
+ args.extend((records, values))
+ Action._groups_cache.clear()
+ super().write(*args)
+
+ @classmethod
+ def delete(cls, records):
+ pool = Pool()
+ Action = pool.get('ir.action')
+ Action._groups_cache.clear()
+ super().delete(records)
+
+
class ActionKeyword(ModelSQL, ModelView):
__name__ = 'ir.action.keyword'
keyword = fields.Selection([
@@ -160,6 +204,10 @@ class ActionKeyword(ModelSQL, ModelView):
model = fields.Reference('Model', selection='models_get')
action = fields.Many2One('ir.action', 'Action',
ondelete='CASCADE')
+ groups = fields.Function(
+ fields.One2Many('res.group', None, "Groups"),
+ 'get_groups', searcher='search_groups')
+
_get_keyword_cache = Cache(
'ir_action_keyword.get_keyword', context=False)
@@ -219,6 +267,13 @@ class ActionKeyword(ModelSQL, ModelView):
Model = pool.get('ir.model')
return [(None, '')] + Model.get_name_items(ModelView)
+ def get_groups(self, name):
+ return [g.id for g in self.action.groups]
+
+ @classmethod
+ def search_groups(cls, name, clause):
+ return [('action.' + clause[0],) + tuple(clause[1:])]
+
@classmethod
def delete(cls, keywords):
ModelView._view_toolbar_get_cache.clear()
@@ -380,6 +435,27 @@ class ActionMixin(ModelSQL):
def search_action(cls, name, clause):
return [('action.' + clause[0],) + tuple(clause[1:])]
+ @classmethod
+ @without_check_access
+ def get_groups(cls, name, action_id=None):
+ pool = Pool()
+ Action = pool.get('ir.action')
+
+ key = (name, action_id)
+ groups = Action._groups_cache.get(key)
+ if groups is not None:
+ return set(groups)
+
+ domain = [
+ (cls._action_name, '=', name),
+ ]
+ if action_id:
+ domain.append(('id', '=', action_id))
+ actions = cls.search(domain)
+ groups = {g.id for a in actions for g in a.groups}
+ Action._groups_cache.set(key, list(groups))
+ return groups
+
@classmethod
def create(cls, vlist):
pool = Pool()
diff --git a/trytond/trytond/ir/action.xml b/trytond/trytond/ir/action.xml
index 81bec8d062..1207b5777f 100644
--- a/trytond/trytond/ir/action.xml
+++ b/trytond/trytond/ir/action.xml
@@ -41,6 +41,36 @@ this repository contains the full copyright notices and license terms. -->
sequence="10"
id="menu_act_action"/>
+
+ ir.action
+
+
+
+
+
+
+ ir.action
+
+
+
+
+
+
+
+
+ User in groups
+ ir.action
+
+
+
+
+
+
+
+
+
+
+
ir.action.keyword
tree
diff --git a/trytond/trytond/ir/cron.xml b/trytond/trytond/ir/cron.xml
index b85d9dbe5e..764a84a215 100644
--- a/trytond/trytond/ir/cron.xml
+++ b/trytond/trytond/ir/cron.xml
@@ -47,6 +47,26 @@ this repository contains the full copyright notices and license terms. -->
run_once
Run Once
+
+
+
+
+
+
+ ir.cron
+
+
+
+
+
+
+ ir.cron
+
+
+
+
+
+
ir.cron.log
diff --git a/trytond/trytond/ir/email.xml b/trytond/trytond/ir/email.xml
index f2ea815b25..46c3963c38 100644
--- a/trytond/trytond/ir/email.xml
+++ b/trytond/trytond/ir/email.xml
@@ -95,5 +95,21 @@ this repository contains the full copyright notices and license terms. -->
action="act_email_template_form"
sequence="50"
id="menu_email_template_form"/>
+
+
+ ir.email.template
+
+
+
+
+
+
+ ir.email.template
+
+
+
+
+
+
diff --git a/trytond/trytond/ir/error.xml b/trytond/trytond/ir/error.xml
index 57835fdf21..ba291a3347 100644
--- a/trytond/trytond/ir/error.xml
+++ b/trytond/trytond/ir/error.xml
@@ -73,6 +73,22 @@ this repository contains the full copyright notices and license terms. -->
solve
Solve
+
+
+ ir.error
+
+
+
+
+
+
+ ir.error
+
+
+
+
+
+
diff --git a/trytond/trytond/ir/export.py b/trytond/trytond/ir/export.py
index 6a14b4de50..d39c331d76 100644
--- a/trytond/trytond/ir/export.py
+++ b/trytond/trytond/ir/export.py
@@ -3,6 +3,7 @@
"Exports"
from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import Pool
+from trytond.pyson import Eval
from trytond.rpc import RPC
from trytond.transaction import Transaction
@@ -30,6 +31,19 @@ class Export(_ClearCache, ModelSQL, ModelView):
resource = fields.Char('Resource')
user = fields.Many2One(
'res.user', "User", required=True, ondelete='CASCADE')
+ groups = fields.Many2Many(
+ 'ir.export-res.group', 'export', 'group', "Groups",
+ help="The user groups that can use the export.")
+ write_groups = fields.Many2Many(
+ 'ir.export-write-res.group', 'export', 'group',
+ "Modification Groups",
+ domain=[
+ ('id', 'in', Eval('groups', [])),
+ ],
+ states={
+ 'invisible': ~Eval('groups'),
+ },
+ help="The user groups that can modify the export.")
header = fields.Boolean(
"Header",
help="Check to include field names on the export.")
@@ -139,3 +153,20 @@ class ExportLine(_ClearCache, ModelSQL, ModelView):
def __setup__(cls):
super().__setup__()
cls.__access__.add('export')
+
+
+class ExportGroup(ModelSQL):
+ "Export Group"
+ __name__ = 'ir.export-res.group'
+
+ export = fields.Many2One(
+ 'ir.export', "Export", required=True, ondelete='CASCADE')
+ group = fields.Many2One(
+ 'res.group', "Group", required=True, ondelete='CASCADE')
+
+
+class ExportWriteGroup(ExportGroup):
+ "Export Modification Group"
+ __name__ = 'ir.export-write-res.group'
+ __string__ = None
+ _table = None # Needed to reset Export_Group._table
diff --git a/trytond/trytond/ir/export.xml b/trytond/trytond/ir/export.xml
index 1cb5fdeb6b..26c7880c1a 100644
--- a/trytond/trytond/ir/export.xml
+++ b/trytond/trytond/ir/export.xml
@@ -36,6 +36,22 @@ this repository contains the full copyright notices and license terms. -->
sequence="50"
id="menu_export_form"/>
+
+ ir.export
+
+
+
+
+
+
+ ir.export
+
+
+
+
+
+
+
ir.export.line
form
diff --git a/trytond/trytond/ir/lang.xml b/trytond/trytond/ir/lang.xml
index e8fcc01631..dd55f8608c 100644
--- a/trytond/trytond/ir/lang.xml
+++ b/trytond/trytond/ir/lang.xml
@@ -606,6 +606,22 @@ this repository contains the full copyright notices and license terms. -->
Are you sure you want to remove languages' translations?
+
+ ir.lang
+
+
+
+
+
+
+ ir.lang
+
+
+
+
+
+
+
ir.lang.config.start
form
@@ -617,6 +633,10 @@ this repository contains the full copyright notices and license terms. -->
ir.lang.config
+
+
+
+
diff --git a/trytond/trytond/ir/message.xml b/trytond/trytond/ir/message.xml
index 180db966be..4353dd0bdc 100644
--- a/trytond/trytond/ir/message.xml
+++ b/trytond/trytond/ir/message.xml
@@ -32,6 +32,22 @@ this repository contains the full copyright notices and license terms. -->
action="act_message_form"
sequence="20"
id="menu_message_form"/>
+
+
+ ir.message
+
+
+
+
+
+
+ ir.message
+
+
+
+
+
+
diff --git a/trytond/trytond/ir/model.py b/trytond/trytond/ir/model.py
index 520ec6b292..512c752123 100644
--- a/trytond/trytond/ir/model.py
+++ b/trytond/trytond/ir/model.py
@@ -982,6 +982,9 @@ class ModelButton(
('id', '!=', Eval('id', -1)),
])
_reset_cache = Cache('ir.model.button.reset')
+ groups = fields.Many2Many(
+ 'ir.model.button-res.group', 'button', 'group', "Groups")
+ _groups_cache = Cache('ir.model.button.groups')
_view_attributes_cache = Cache(
'ir.model.button.view_attributes', context=False)
@@ -1029,6 +1032,7 @@ class ModelButton(
result = super(ModelButton, cls).create(vlist)
cls._rules_cache.clear()
cls._reset_cache.clear()
+ cls._groups_cache.clear()
cls._view_attributes_cache.clear()
return result
@@ -1037,6 +1041,7 @@ class ModelButton(
super(ModelButton, cls).write(buttons, values, *args)
cls._rules_cache.clear()
cls._reset_cache.clear()
+ cls._groups_cache.clear()
cls._view_attributes_cache.clear()
@classmethod
@@ -1044,6 +1049,7 @@ class ModelButton(
super(ModelButton, cls).delete(buttons)
cls._rules_cache.clear()
cls._reset_cache.clear()
+ cls._groups_cache.clear()
cls._view_attributes_cache.clear()
@classmethod
@@ -1097,6 +1103,27 @@ class ModelButton(
cls._reset_cache.set(key, reset)
return reset
+ @classmethod
+ def get_groups(cls, model, name):
+ '''
+ Return a set of group ids for the named button on the model.
+ '''
+ key = (model, name)
+ groups = cls._groups_cache.get(key)
+ if groups is not None:
+ return groups
+ buttons = cls.search([
+ ('model.name', '=', model),
+ ('name', '=', name),
+ ])
+ if not buttons:
+ groups = set()
+ else:
+ button, = buttons
+ groups = set(g.id for g in button.groups)
+ cls._groups_cache.set(key, groups)
+ return groups
+
@classmethod
def get_view_attributes(cls, model, name):
"Return the view attributes of the named button of the model"
@@ -1121,6 +1148,37 @@ class ModelButton(
return attributes
+class ModelButtonGroup(DeactivableMixin, ModelSQL):
+ "Model Button - Group"
+ __name__ = 'ir.model.button-res.group'
+ button = fields.Many2One(
+ 'ir.model.button', "Button", ondelete='CASCADE', required=True)
+ group = fields.Many2One(
+ 'res.group', "Group", ondelete='CASCADE', required=True)
+
+ @classmethod
+ def create(cls, vlist):
+ pool = Pool()
+ result = super().create(vlist)
+ # Restart the cache for get_groups
+ pool.get('ir.model.button')._groups_cache.clear()
+ return result
+
+ @classmethod
+ def write(cls, records, values, *args):
+ pool = Pool()
+ super().write(records, values, *args)
+ # Restart the cache for get_groups
+ pool.get('ir.model.button')._groups_cache.clear()
+
+ @classmethod
+ def delete(cls, records):
+ pool = Pool()
+ super().delete(records)
+ # Restart the cache for get_groups
+ pool.get('ir.model.button')._groups_cache.clear()
+
+
class ModelButtonRule(ModelSQL, ModelView):
__name__ = 'ir.model.button.rule'
button = fields.Many2One(
@@ -1131,6 +1189,7 @@ class ModelButtonRule(ModelSQL, ModelView):
"Condition",
help='A PYSON statement evaluated with the record represented by '
'"self"\nIt activate the rule if true.')
+ group = fields.Many2One('res.group', "Group", ondelete='CASCADE')
@classmethod
def __setup__(cls):
@@ -1205,6 +1264,7 @@ class ModelButtonClick(DeactivableMixin, ModelSQL, ModelView):
button = fields.Many2One(
'ir.model.button', "Button", required=True, ondelete='CASCADE')
record_id = fields.Integer("Record ID", required=True)
+ user = fields.Many2One('res.user', "User", ondelete='CASCADE')
@classmethod
def __setup__(cls):
diff --git a/trytond/trytond/ir/model.xml b/trytond/trytond/ir/model.xml
index f604dba4e4..ebb496e59d 100644
--- a/trytond/trytond/ir/model.xml
+++ b/trytond/trytond/ir/model.xml
@@ -39,6 +39,23 @@ this repository contains the full copyright notices and license terms. -->
action="act_model_form"
sequence="10"
id="menu_model_form"/>
+
+
+ ir.model
+
+
+
+
+
+
+ ir.model
+
+
+
+
+
+
+
ir.model.field
form
@@ -304,5 +321,13 @@ this repository contains the full copyright notices and license terms. -->
action="act_model_log_form"
sequence="50"
id="menu_model_log_form"/>
+
+
+ ir.model.log
+
+
+
+
+
diff --git a/trytond/trytond/ir/module.xml b/trytond/trytond/ir/module.xml
index 1a2be9e195..d4bb1a84e4 100644
--- a/trytond/trytond/ir/module.xml
+++ b/trytond/trytond/ir/module.xml
@@ -47,31 +47,76 @@ this repository contains the full copyright notices and license terms. -->
activate
Mark for Activation
+
+
+
+
+
ir.module
activate_cancel
Cancel Activation
+
+
+
+
+
ir.module
deactivate
Mark for Deactivation (beta)
+
+
+
+
+
ir.module
deactivate_cancel
Cancel Deactivation
+
+
+
+
+
ir.module
upgrade
Mark for Upgrade
+
+
+
+
+
ir.module
upgrade_cancel
Cancel Upgrade
+
+
+
+
+
+
+ ir.module
+
+
+
+
+
+
+ ir.module
+
+
+
+
+
+
ir.module.dependency
@@ -140,6 +185,11 @@ this repository contains the full copyright notices and license terms. -->
Perform Pending Activation/Upgrade
ir.module.activate_upgrade
+
+
+
+
+
form_action
diff --git a/trytond/trytond/ir/rule.py b/trytond/trytond/ir/rule.py
index 57a517cbc9..a47d6c81be 100644
--- a/trytond/trytond/ir/rule.py
+++ b/trytond/trytond/ir/rule.py
@@ -66,6 +66,8 @@ class RuleGroup(
()),
],
help="Add this rule to all users by default.")
+ groups = fields.Many2Many(
+ 'ir.rule.group-res.group', 'rule_group', 'group', "Groups")
rules = fields.One2Many('ir.rule', 'rule_group', 'Tests',
help="The rule is satisfied if at least one test is True.\n"
"If there is no test defined, "
@@ -161,6 +163,15 @@ class RuleGroup(
Pool().get('ir.rule')._domain_get_cache.clear()
+class RuleGroup_Group(ModelSQL):
+ "Rule Group - Group"
+ __name__ = 'ir.rule.group-res.group'
+ rule_group = fields.Many2One(
+ 'ir.rule.group', "Rule Group", ondelete='CASCADE', required=True)
+ group = fields.Many2One(
+ 'res.group', "Group", ondelete='CASCADE', required=True)
+
+
class Rule(ModelSQL, ModelView):
__name__ = 'ir.rule'
rule_group = fields.Many2One('ir.rule.group', 'Group',
diff --git a/trytond/trytond/ir/rule.xml b/trytond/trytond/ir/rule.xml
index 415d253f31..f2828981b2 100644
--- a/trytond/trytond/ir/rule.xml
+++ b/trytond/trytond/ir/rule.xml
@@ -34,6 +34,23 @@ this repository contains the full copyright notices and license terms. -->
action="act_rule_group_form"
sequence="30"
id="menu_rule_group_form"/>
+
+
+ ir.rule.group
+
+
+
+
+
+
+ ir.rule.group
+
+
+
+
+
+
+
ir.rule
form
diff --git a/trytond/trytond/ir/sequence.py b/trytond/trytond/ir/sequence.py
index 8fe43cf999..b91a13e08f 100644
--- a/trytond/trytond/ir/sequence.py
+++ b/trytond/trytond/ir/sequence.py
@@ -35,6 +35,9 @@ class SequenceType(ModelSQL, ModelView):
__name__ = 'ir.sequence.type'
name = fields.Char('Sequence Name', required=True, translate=True)
+ groups = fields.Many2Many(
+ 'ir.sequence.type-res.group', 'sequence_type', 'group', "Groups",
+ help="Groups allowed to edit the sequences of this type.")
@classmethod
def __register__(cls, module):
@@ -46,6 +49,15 @@ class SequenceType(ModelSQL, ModelView):
table_h.not_null_action('code', action='remove')
+class SequenceTypeGroup(ModelSQL):
+ 'Sequence Type - Group'
+ __name__ = 'ir.sequence.type-res.group'
+ sequence_type = fields.Many2One(
+ 'ir.sequence.type', "Sequence Type", ondelete='CASCADE', required=True)
+ group = fields.Many2One(
+ 'res.group', "Group", ondelete='CASCADE', required=True)
+
+
class Sequence(DeactivableMixin, ModelSQL, ModelView):
__name__ = 'ir.sequence'
diff --git a/trytond/trytond/ir/sequence.xml b/trytond/trytond/ir/sequence.xml
index e75f747fc8..3d02e48faf 100644
--- a/trytond/trytond/ir/sequence.xml
+++ b/trytond/trytond/ir/sequence.xml
@@ -43,6 +43,17 @@ this repository contains the full copyright notices and license terms. -->
sequence="10"
id="menu_sequence_form"/>
+
+ User in groups
+ ir.sequence
+
+
+
+
+
+
+
+
ir.sequence.strict
@@ -78,6 +89,17 @@ this repository contains the full copyright notices and license terms. -->
id="menu_sequence_strict_form"
icon="tryton-list"/>
+
+ User in groups
+ ir.sequence.strict
+
+
+
+
+
+
+
+
ir.sequence.type
form
@@ -111,5 +133,21 @@ this repository contains the full copyright notices and license terms. -->
action="act_sequence_type_form"
sequence="20"
id="menu_ir_sequence_type"/>
+
+
+ ir.sequence.type
+
+
+
+
+
+
+ ir.sequence.type
+
+
+
+
+
+
diff --git a/trytond/trytond/ir/translation.xml b/trytond/trytond/ir/translation.xml
index 756df84992..5a405b94ba 100644
--- a/trytond/trytond/ir/translation.xml
+++ b/trytond/trytond/ir/translation.xml
@@ -50,6 +50,22 @@ this repository contains the full copyright notices and license terms. -->
sequence="20"
id="menu_translation_form"/>
+
+ ir.translation
+
+
+
+
+
+
+ ir.translation
+
+
+
+
+
+
+
Translations
ir.translation.report
@@ -131,6 +147,10 @@ this repository contains the full copyright notices and license terms. -->
Synchronize Translations
ir.translation.update
+
+
+
+
form_action
@@ -165,6 +185,10 @@ this repository contains the full copyright notices and license terms. -->
Export Translations
ir.translation.export
+
+
+
+
+
+
+
+
+
+
+ ir.ui.view
+
+
+
+
+
+
+ ir.ui.view
+
+
+
+
+
+
ir.ui.view_tree_width
@@ -158,6 +178,22 @@ this repository contains the full copyright notices and license terms. -->
+
+ ir.ui.view_tree_state
+
+
+
+
+
+
+ ir.ui.view_tree_state
+
+
+
+
+
+
+
ir.ui.view_search
form
@@ -191,5 +227,21 @@ this repository contains the full copyright notices and license terms. -->
ir.model,-1
+
+
+ ir.ui.view_search
+
+
+
+
+
+
+ ir.ui.view_search
+
+
+
+
+
+
diff --git a/trytond/trytond/ir/view/export_form.xml b/trytond/trytond/ir/view/export_form.xml
index e7fd6cbcdd..8f78405da2 100644
--- a/trytond/trytond/ir/view/export_form.xml
+++ b/trytond/trytond/ir/view/export_form.xml
@@ -15,5 +15,9 @@ this repository contains the full copyright notices and license terms. -->
+
+
+
+
diff --git a/trytond/trytond/ir/view/export_list.xml b/trytond/trytond/ir/view/export_list.xml
index 981ca548c8..608c232281 100644
--- a/trytond/trytond/ir/view/export_list.xml
+++ b/trytond/trytond/ir/view/export_list.xml
@@ -5,4 +5,6 @@ this repository contains the full copyright notices and license terms. -->
+
+
diff --git a/trytond/trytond/ir/view/sequence_type_form.xml b/trytond/trytond/ir/view/sequence_type_form.xml
index 3e61cab53d..a2b4f1e64e 100644
--- a/trytond/trytond/ir/view/sequence_type_form.xml
+++ b/trytond/trytond/ir/view/sequence_type_form.xml
@@ -4,4 +4,6 @@ this repository contains the full copyright notices and license terms. -->
diff --git a/trytond/trytond/res/__init__.py b/trytond/trytond/res/__init__.py
index 8bf3da1a56..2dd3cc46dc 100644
--- a/trytond/trytond/res/__init__.py
+++ b/trytond/trytond/res/__init__.py
@@ -18,27 +18,7 @@ def register():
user.Warning_,
user.UserApplication,
user.UserConfigStart,
- ir.UIMenu,
- ir.UIMenuGroup,
- ir.ActionGroup,
- ir.Action,
- ir.ActionReport,
- ir.ActionActWindow,
- ir.ActionWizard,
- ir.ActionURL,
- ir.ActionKeyword,
- ir.ModelButton,
- ir.ModelButtonGroup,
- ir.ModelButtonRule,
- ir.ModelButtonClick,
- ir.RuleGroup,
- ir.RuleGroupGroup,
ir.Rule,
- ir.SequenceType,
- ir.SequenceTypeGroup,
- ir.Export,
- ir.Export_Group,
- ir.Export_Write_Group,
module='res', type_='model')
Pool.register(
user.UserConfig,
diff --git a/trytond/trytond/res/ir.py b/trytond/trytond/res/ir.py
index b3081adacf..a32353d75d 100644
--- a/trytond/trytond/res/ir.py
+++ b/trytond/trytond/res/ir.py
@@ -1,228 +1,8 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-from trytond.cache import Cache
-from trytond.model import DeactivableMixin, ModelSQL, fields
-from trytond.pool import Pool, PoolMeta
-from trytond.pyson import Eval
-from trytond.transaction import Transaction, without_check_access
-
-
-class UIMenu(metaclass=PoolMeta):
- __name__ = 'ir.ui.menu'
-
- groups = fields.Many2Many(
- 'ir.ui.menu-res.group', 'menu', 'group', "Groups")
-
-
-class UIMenuGroup(ModelSQL):
- __name__ = 'ir.ui.menu-res.group'
- menu = fields.Many2One('ir.ui.menu', 'Menu', ondelete='CASCADE',
- required=True)
- group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
- required=True)
-
-
-class ActionGroup(ModelSQL):
- __name__ = 'ir.action-res.group'
- action = fields.Many2One('ir.action', 'Action', ondelete='CASCADE',
- required=True)
- group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
- required=True)
-
- @classmethod
- def create(cls, vlist):
- pool = Pool()
- Action = pool.get('ir.action')
- vlist = [x.copy() for x in vlist]
- for vals in vlist:
- if vals.get('action'):
- vals['action'] = Action.get_action_id(vals['action'])
- Action._groups_cache.clear()
- return super().create(vlist)
-
- @classmethod
- def write(cls, records, values, *args):
- pool = Pool()
- Action = pool.get('ir.action')
- actions = iter((records, values) + args)
- args = []
- for records, values in zip(actions, actions):
- if values.get('action'):
- values = values.copy()
- values['action'] = Action.get_action_id(values['action'])
- args.extend((records, values))
- Action._groups_cache.clear()
- super().write(*args)
-
- @classmethod
- def delete(cls, records):
- pool = Pool()
- Action = pool.get('ir.action')
- Action._groups_cache.clear()
- super().delete(records)
-
-
-class Action(metaclass=PoolMeta):
- __name__ = 'ir.action'
-
- groups = fields.Many2Many(
- 'ir.action-res.group', 'action', 'group', "Groups")
- _groups_cache = Cache('ir.action.get_groups', context=False)
-
-
-class ActionMixin(metaclass=PoolMeta):
-
- @classmethod
- @without_check_access
- def get_groups(cls, name, action_id=None):
- pool = Pool()
- Action = pool.get('ir.action')
-
- key = (name, action_id)
- groups = Action._groups_cache.get(key)
- if groups is not None:
- return set(groups)
-
- domain = [
- (cls._action_name, '=', name),
- ]
- if action_id:
- domain.append(('id', '=', action_id))
- actions = cls.search(domain)
- groups = {g.id for a in actions for g in a.groups}
- Action._groups_cache.set(key, list(groups))
- return groups
-
-
-class ActionReport(ActionMixin):
- __name__ = 'ir.action.report'
-
-
-class ActionActWindow(ActionMixin):
- __name__ = 'ir.action.act_window'
-
-
-class ActionWizard(ActionMixin):
- __name__ = 'ir.action.wizard'
-
-
-class ActionURL(ActionMixin):
- __name__ = 'ir.action.url'
-
-
-class ActionKeyword(metaclass=PoolMeta):
- __name__ = 'ir.action.keyword'
-
- groups = fields.Function(fields.One2Many('res.group', None, 'Groups'),
- 'get_groups', searcher='search_groups')
-
- def get_groups(self, name):
- return [g.id for g in self.action.groups]
-
- @classmethod
- def search_groups(cls, name, clause):
- return [('action.' + clause[0],) + tuple(clause[1:])]
-
-
-class ModelButton(metaclass=PoolMeta):
- __name__ = 'ir.model.button'
-
- groups = fields.Many2Many(
- 'ir.model.button-res.group', 'button', 'group', "Groups")
- _groups_cache = Cache('ir.model.button.groups')
-
- @classmethod
- def create(cls, vlist):
- result = super().create(vlist)
- cls._groups_cache.clear()
- return result
-
- @classmethod
- def write(cls, buttons, values, *args):
- super().write(buttons, values, *args)
- cls._groups_cache.clear()
-
- @classmethod
- def delete(cls, buttons):
- super().delete(buttons)
- cls._groups_cache.clear()
-
- @classmethod
- def get_groups(cls, model, name):
- '''
- Return a set of group ids for the named button on the model.
- '''
- key = (model, name)
- groups = cls._groups_cache.get(key)
- if groups is not None:
- return groups
- buttons = cls.search([
- ('model.name', '=', model),
- ('name', '=', name),
- ])
- if not buttons:
- groups = set()
- else:
- button, = buttons
- groups = set(g.id for g in button.groups)
- cls._groups_cache.set(key, groups)
- return groups
-
-
-class ModelButtonGroup(DeactivableMixin, ModelSQL):
- __name__ = 'ir.model.button-res.group'
- button = fields.Many2One('ir.model.button', 'Button',
- ondelete='CASCADE', required=True)
- group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
- required=True)
-
- @classmethod
- def create(cls, vlist):
- pool = Pool()
- result = super(ModelButtonGroup, cls).create(vlist)
- # Restart the cache for get_groups
- pool.get('ir.model.button')._groups_cache.clear()
- return result
-
- @classmethod
- def write(cls, records, values, *args):
- pool = Pool()
- super(ModelButtonGroup, cls).write(records, values, *args)
- # Restart the cache for get_groups
- pool.get('ir.model.button')._groups_cache.clear()
-
- @classmethod
- def delete(cls, records):
- pool = Pool()
- super(ModelButtonGroup, cls).delete(records)
- # Restart the cache for get_groups
- pool.get('ir.model.button')._groups_cache.clear()
-
-
-class ModelButtonRule(metaclass=PoolMeta):
- __name__ = 'ir.model.button.rule'
- group = fields.Many2One('res.group', "Group", ondelete='CASCADE')
-
-
-class ModelButtonClick(metaclass=PoolMeta):
- __name__ = 'ir.model.button.click'
- user = fields.Many2One('res.user', "User", ondelete='CASCADE')
-
-
-class RuleGroup(metaclass=PoolMeta):
- __name__ = 'ir.rule.group'
-
- groups = fields.Many2Many(
- 'ir.rule.group-res.group', 'rule_group', 'group', "Groups")
-
-
-class RuleGroupGroup(ModelSQL):
- __name__ = 'ir.rule.group-res.group'
- rule_group = fields.Many2One('ir.rule.group', 'Rule Group',
- ondelete='CASCADE', required=True)
- group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
- required=True)
+from trytond.pool import PoolMeta
+from trytond.transaction import Transaction
class Rule(metaclass=PoolMeta):
@@ -241,51 +21,3 @@ class Rule(metaclass=PoolMeta):
if model_names & {'res.user.warning', 'res.user.application'}:
key = (*key, Transaction().user)
return key
-
-
-class SequenceType(metaclass=PoolMeta):
- __name__ = 'ir.sequence.type'
- groups = fields.Many2Many('ir.sequence.type-res.group', 'sequence_type',
- 'group', 'User Groups',
- help='Groups allowed to edit the sequences of this type.')
-
-
-class SequenceTypeGroup(ModelSQL):
- __name__ = 'ir.sequence.type-res.group'
- sequence_type = fields.Many2One('ir.sequence.type', 'Sequence Type',
- ondelete='CASCADE', required=True)
- group = fields.Many2One('res.group', 'User Groups',
- ondelete='CASCADE', required=True)
-
-
-class Export(metaclass=PoolMeta):
- __name__ = 'ir.export'
-
- groups = fields.Many2Many(
- 'ir.export-res.group', 'export', 'group', "Groups",
- help="The user groups that can use the export.")
- write_groups = fields.Many2Many(
- 'ir.export-write-res.group', 'export', 'group',
- "Modification Groups",
- domain=[
- ('id', 'in', Eval('groups', [])),
- ],
- states={
- 'invisible': ~Eval('groups'),
- },
- depends=['groups'],
- help="The user groups that can modify the export.")
-
-
-class Export_Group(ModelSQL):
- __name__ = 'ir.export-res.group'
- export = fields.Many2One(
- 'ir.export', "Export", required=True, ondelete='CASCADE')
- group = fields.Many2One(
- 'res.group', "Group", required=True, ondelete='CASCADE')
-
-
-class Export_Write_Group(Export_Group):
- __name__ = 'ir.export-write-res.group'
- __string__ = None
- _table = None # Needed to reset Export_Group._table
diff --git a/trytond/trytond/res/ir.xml b/trytond/trytond/res/ir.xml
deleted file mode 100644
index c0064f92ce..0000000000
--- a/trytond/trytond/res/ir.xml
+++ /dev/null
@@ -1,463 +0,0 @@
-
-
-
-
-
- ir.sequence.type
-
-
-
-
-
-
- ir.sequence.type
-
-
-
-
-
-
-
- ir.ui.icon
-
-
-
-
-
-
- ir.ui.icon
-
-
-
-
-
-
-
-
-
-
- ir.ui.view
-
-
-
-
-
-
- ir.ui.view
-
-
-
-
-
-
-
- ir.action
-
-
-
-
-
-
- ir.action
-
-
-
-
-
-
-
- ir.model
-
-
-
-
-
-
- ir.model
-
-
-
-
-
-
-
-
- ir.model.data
-
-
-
-
-
-
-
- ir.model.log
-
-
-
-
-
-
-
- ir.cron
-
-
-
-
-
-
- ir.cron
-
-
-
-
-
-
-
- ir.queue
-
-
-
-
-
-
- ir.queue
-
-
-
-
-
-
-
- ir.lang
-
-
-
-
-
-
- ir.lang
-
-
-
-
-
-
-
- ir.translation
-
-
-
-
-
-
- ir.translation
-
-
-
-
-
-
-
- ir.rule.group
-
-
-
-
-
-
- ir.rule.group
-
-
-
-
-
-
-
-
- ir.module
-
-
-
-
-
-
- ir.module
-
-
-
-
-
-
-
-
- ir.trigger
-
-
-
-
-
-
- ir.trigger
-
-
-
-
-
-
-
-
-
-
-
- User in groups
- ir.action
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ir.sequence.type
-
- sequence_type_form
-
-
-
- User in groups
- ir.sequence
-
-
-
-
-
-
-
-
-
- User in groups
- ir.sequence.strict
-
-
-
-
-
-
-
-
-
- ir.ui.view_tree_state
-
-
-
-
-
-
- ir.ui.view_tree_state
-
-
-
-
-
-
-
-
- ir.ui.view_search
-
-
-
-
-
-
- ir.ui.view_search
-
-
-
-
-
-
-
-
- ir.message
-
-
-
-
-
-
- ir.message
-
-
-
-
-
-
-
-
- ir.export
-
- export_form
-
-
-
- ir.export
-
- export_list
-
-
-
- ir.export
-
-
-
-
-
-
- ir.export
-
-
-
-
-
-
-
-
- ir.email.template
-
-
-
-
-
-
- ir.email.template
-
-
-
-
-
-
-
-
- ir.error
-
-
-
-
-
-
- ir.error
-
-
-
-
-
-
-
-
-
- ir.trigger|trigger_time
-
- minutes
-
-
-
diff --git a/trytond/trytond/res/tryton.cfg b/trytond/trytond/res/tryton.cfg
index 3b346d7453..2584302b6b 100644
--- a/trytond/trytond/res/tryton.cfg
+++ b/trytond/trytond/res/tryton.cfg
@@ -5,5 +5,4 @@ xml:
res.xml
group.xml
user.xml
- ir.xml
message.xml
diff --git a/trytond/trytond/res/user.py b/trytond/trytond/res/user.py
index 51cbe2836c..3227ffbb28 100644
--- a/trytond/trytond/res/user.py
+++ b/trytond/trytond/res/user.py
@@ -354,6 +354,10 @@ class User(avatar_mixin(100, 'login'), DeactivableMixin, ModelSQL, ModelView):
vals['email'] = normalize_email(vals['email'])
return vals
+ @classmethod
+ def check_xml_record(cls, records, values):
+ pass
+
@classmethod
def read(cls, ids, fields_names):
result = super(User, cls).read(ids, fields_names)
diff --git a/trytond/trytond/res/user.xml b/trytond/trytond/res/user.xml
index a71da8a23e..f9cc5d6191 100644
--- a/trytond/trytond/res/user.xml
+++ b/trytond/trytond/res/user.xml
@@ -2,14 +2,13 @@
-
+
admin
Administrator
-
-
+
diff --git a/trytond/trytond/res/view/export_form.xml b/trytond/trytond/res/view/export_form.xml
deleted file mode 100644
index 3c78d3d23e..0000000000
--- a/trytond/trytond/res/view/export_form.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/trytond/trytond/res/view/export_list.xml b/trytond/trytond/res/view/export_list.xml
deleted file mode 100644
index 5a15350b41..0000000000
--- a/trytond/trytond/res/view/export_list.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/trytond/trytond/res/view/sequence_type_form.xml b/trytond/trytond/res/view/sequence_type_form.xml
deleted file mode 100644
index 1f56effbf5..0000000000
--- a/trytond/trytond/res/view/sequence_type_form.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-