Merge pull request #648 from expsa/fix-dashboard

update modules
This commit is contained in:
eslam 2024-08-12 15:14:59 +03:00 committed by GitHub
commit b3959e819e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
62 changed files with 54400 additions and 828 deletions

0
odex25_base/advanced_web_domain_widget/__init__.py Normal file → Executable file
View File

6
odex25_base/advanced_web_domain_widget/__manifest__.py Normal file → Executable file
View File

@ -10,14 +10,12 @@
#################################################################################
{
'name': 'Advanced Web Domain Widget',
'version': '14.0.2.0.0',
'version': '14.0.3.0.0',
'summary': 'Set all relational fields domain by selecting its records unsing `in, not in` operator.',
'sequence': 1,
'author': 'Terabits Technolab',
'category': 'Odex25-base',
'license': 'OPL-1',
'website': 'https://www.terabits.xyz',
'website': 'https://www.terabits.xyz/apps/14.0/advanced_web_domain_widget',
'description':"""
""",

View File

View File

@ -1 +1,2 @@
from . import domain_prepare
from . import models

View File

12
odex25_base/advanced_web_domain_widget/models/models.py Normal file → Executable file
View File

@ -5,11 +5,9 @@ class BaseModel(models.AbstractModel):
_inherit = 'base'
@api.model
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, **read_kwargs):
res = super().search_read(domain, fields, offset, limit, order, **read_kwargs)
if self._context.get('web_domain_widget') and hasattr(self, 'company_id'):
for rec in res:
rec.update({'company_name': self.browse(rec.get('id')).company_id.name})
return res
def get_widget_name(self, domain=None, fields=None, offset=0, limit=None, order=None, **read_kwargs):
return self.sudo().search_read(domain, ['id', 'display_name'], offset, limit, order)
@api.model
def get_widget_count(self, args):
return self.sudo().search_count(args)

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -12,7 +12,7 @@ var config = require('web.config');
var core = require('web.core');
var Domain = require('web.Domain');
var DomainSelector = require('advanced_web_domain_widget.TerabitsDomainSelector');
var DomainSelectorDialog = require('advanced_web_domain_widget.DomainSelectorDialog');
var {DomainSelectorDialog, DomainSelectorDialog2} = require('advanced_web_domain_widget.DomainSelectorDialog');
var view_dialogs = require('web.view_dialogs');
require("web.zoomodoo");
@ -439,8 +439,61 @@ var TerabitsFieldDomain = AbstractField.extend({
},
});
var TerabitsFieldDomain2 = TerabitsFieldDomain.extend({
_replaceContent: function () {
if (this._$content) {
this._$content.remove();
}
this._$content = $(qweb.render("TerabitsFieldDomain.content2", {
hasModel: !!this._domainModel,
isValid: !!this._isValidForModel,
nbRecords: this.record.specialData[this.name].nbRecords || 0,
inDialogEdit: this.inDialog && this.mode === "edit",
isDateFilter: this.value[0] && this.value[0]?.length>0? this.value[0][1] == "date_filter": false
}));
this._$content.appendTo(this.$el);
},
_render: function () {
// If there is no model, only change the non-domain-selector content
if (!this._domainModel) {
this._replaceContent();
return Promise.resolve();
}
// Convert char value to array value
var value = this.value || "[]";
// Create the domain selector or change the value of the current one...
var def;
if (!this.domainSelector) {
this.domainSelector = new DomainSelector.TerabitsDomainSelector2(this, this._domainModel, value, {
readonly: this.mode === "readonly" || this.inDialog,
filters: this.fsFilters,
debugMode: config.isDebug(),
});
def = this.domainSelector.prependTo(this.$el);
} else {
def = this.domainSelector.setDomain(value);
}
// ... then replace the other content (matched records, etc)
return def.then(this._replaceContent.bind(this));
},
_onDialogEditButtonClick: function (e) {
e.preventDefault();
new DomainSelectorDialog2(this, this._domainModel, this.value || "[]", {
readonly: this.mode === "readonly",
filters: this.fsFilters,
debugMode: config.isDebug(),
}).open();
},
})
return {
TerabitsFieldDomain: TerabitsFieldDomain,
TerabitsFieldDomain2: TerabitsFieldDomain2
};
});

View File

@ -239,7 +239,7 @@ BasicModel.include({
});
self._rpc({
model: domainModel,
method: 'search_count',
method: 'get_widget_count',
args: [newDomain],
context: context
})

View File

@ -6,6 +6,7 @@ odoo.define('advanced_web_domain_widget._terabits_field_registry', function (req
// Basic fields
fieldRegistry.add('terabits_domain', basic_fields.TerabitsFieldDomain);
fieldRegistry.add('terabits_field_domain', basic_fields.TerabitsFieldDomain2);
});

View File

@ -5,7 +5,7 @@ odoo.define("advanced_web_domain_widget.TerabitsDomainSelector", function (requi
var datepicker = require("web.datepicker");
var Domain = require("web.Domain");
var field_utils = require("web.field_utils");
var ModelFieldSelector = require("advanced_web_domain_widget.ModelFieldSelector");
var {ModelFieldSelector, ModelFieldSelector2} = require("advanced_web_domain_widget.ModelFieldSelector");
var ModelRecordSelector = require("advanced_web_domain_widget.ModelRecordSelector");
var Widget = require("web.Widget");
var _t = core._t;
@ -451,6 +451,33 @@ odoo.define("advanced_web_domain_widget.TerabitsDomainSelector", function (requi
},
});
var TerabitsDomainTree2 = TerabitsDomainTree.extend({
_addChild: function (domain, afterNode) {
var i = afterNode ? _.indexOf(this.children, afterNode) : this.children.length;
if (i < 0) return false;
this.children.splice(i + 1, 0, instantiateNode2(this, this.model, domain, this.options));
this.trigger_up("domain_changed", { child: this });
return true;
},
_addFlattenedChildren: function (domain) {
var node = instantiateNode2(this, this.model, domain, this.options);
if (node === null) {
return;
}
if (!node.children || node.operator !== this.operator) {
this.children.push(node);
return;
}
_.each(node.children, (function (child) {
child.setParent(this);
this.children.push(child);
}).bind(this));
node.destroy();
},
})
/**
* The TerabitsDomainSelector widget can be used to build prefix char domain. It is the
* TerabitsDomainTree specialization to use to have a fully working widget.
@ -611,6 +638,154 @@ odoo.define("advanced_web_domain_widget.TerabitsDomainSelector", function (requi
},
});
var TerabitsDomainSelector2 = TerabitsDomainTree2.extend({
template: "TerabitsDomainSelector",
events: _.extend({}, TerabitsDomainTree.prototype.events, {
"click .o_domain_add_first_node_button": "_onAddFirstButtonClick",
"change .o_domain_debug_input": "_onDebugInputChange",
}),
custom_events: _.extend({}, TerabitsDomainTree.prototype.custom_events, {
domain_changed: "_onDomainChange",
}),
start: function () {
var self = this;
return this._super.apply(this, arguments).then(function () {
if (self.invalidDomain) {
var msg = _t("This domain is not supported.");
self.$el.html(msg);
}
});
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* Changes the internal domain value and forces a reparsing and rerendering.
* If the internal domain value was already equal to the given one, this
* does nothing.
*
* @param {string} domain
* @returns {Promise} resolved when the rerendering is finished
*/
setDomain: function (domain) {
if (domain === Domain.prototype.arrayToString(this.getDomain())) {
return Promise.resolve();
}
var parsedDomain = this._parseDomain(domain);
if (parsedDomain) {
return this._redraw(parsedDomain);
}
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @see TerabitsDomainTree._initialize
*/
_initialize: function (domain) {
// Check if the domain starts with implicit "&" operators and make them
// explicit. As the TerabitsDomainSelector is a specialization of a TerabitsDomainTree,
// it is waiting for a tree and not a leaf. So [] and [A] will be made
// explicit with ["&"], ["&", A] so that tree parsing is made correctly.
// Note: the domain is considered to be a valid one
if (domain.length > 1) {
Domain.prototype.normalizeArray(domain);
} else {
domain = ["&"].concat(domain);
}
return this._super(domain);
},
/**
* @see TerabitsDomainTree._postRender
* Warns the user if the domain is not valid after rendering.
*/
_postRender: function () {
this._super.apply(this, arguments);
// Display technical domain if in debug mode
this.$debugInput = this.$(".o_domain_debug_input");
if (this.$debugInput.length) {
this.$debugInput.val(Domain.prototype.arrayToString(this.getDomain()));
}
// Warn the user if the domain is not valid after rendering
if (!this._isValid) {
this.do_warn(false, _t("Domain not supported"));
}
},
/**
* This method is ugly but achieves the right behavior without flickering.
*
* @param {Array|string} domain
* @returns {Promise}
*/
_redraw: function (domain) {
var oldChildren = this.children.slice();
this._initialize(domain || this.getDomain());
return this._renderChildrenTo($("<div/>")).then((function () {
_.each(oldChildren, function (child) { child.destroy(); });
this.renderElement();
this._postRender();
_.each(this.children, (function (child) {
if(child.$el){
child.$el.appendTo(this.$childrenContainer);
}
}).bind(this));
}).bind(this));
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* Called when the "add a filter" button is clicked -> adds a first domain
* node
*/
_onAddFirstButtonClick: function () {
this._addChild(this.options.default || [["id", "=", 1]]);
},
/**
* Called when the debug input value is changed -> constructs the tree
* representation if valid or warn the user if invalid.
*
* @param {Event} e
*/
_onDebugInputChange: function (e) {
// When the debug input changes, the string prefix domain is read. If it
// is syntax-valid the widget is re-rendered and notifies the parents.
// If not, a warning is shown to the user and the input is ignored.
var domain;
try {
domain = Domain.prototype.stringToArray($(e.currentTarget).val());
} catch (err) { // If there is a syntax error, just ignore the change
this.do_warn(_t("Syntax error"), _t("Domain not properly formed"));
return;
}
this._redraw(domain).then((function () {
this.trigger_up("domain_changed", { child: this, alreadyRedrawn: true });
}).bind(this));
},
/**
* Called when a (child's) domain has changed -> redraw the entire tree
* representation if necessary
*
* @param {OdooEvent} e
*/
_onDomainChange: function (e) {
// If a subdomain notifies that it underwent some modifications, the
// TerabitsDomainSelector catches the message and performs a full re-rendering.
if (!e.data.alreadyRedrawn) {
this._redraw();
}
},
});
/**
* TerabitsDomainNode which handles a domain which cannot be split in another
* subdomains, i.e. composed of a field chain, an operator and a value.
@ -711,13 +886,21 @@ odoo.define("advanced_web_domain_widget.TerabitsDomainSelector", function (requi
const ids = (this.value) ? this.value : [];
wDefs.push(this._rpc({
model: model_rel,
method: 'search_read',
method: 'get_widget_name',
kwargs: {
domain: [['id', 'in', ids]],
fields: ['id', 'display_name'],
},
}).then(function (data) {
self.value = [];
if(model_rel == 'res.users' && _.contains(ids,0)){
self.tagsvalues.push([0, 'Environment User']);
self.value.push(0);
}
if(model_rel == 'res.company' && _.contains(ids,0)){
self.tagsvalues.push([0, 'Environment Company']);
self.value.push(0);
}
_.each(data, function (rec, index) {
self.tagsvalues.push([rec.id, rec.display_name]);
// to remove invalid id which is not in records
@ -1065,6 +1248,126 @@ odoo.define("advanced_web_domain_widget.TerabitsDomainSelector", function (requi
},
});
var TerabitsDomainLeaf2 = TerabitsDomainLeaf.extend({
willStart: function () {
var defs = [this._super.apply(this, arguments)];
// In edit mode, instantiate a field selector. This is done here in
// willStart and prepared by appending it to a dummy element because the
// TerabitsDomainLeaf rendering need some information which cannot be computed
// before the ModelFieldSelector is fully rendered (TODO).
this.fieldSelector = new ModelFieldSelector2(
this,
this.model,
this.chain !== undefined ? this.chain.toString().split(".") : [],
this.options
);
defs.push(this.fieldSelector.appendTo($("<div/>")).then((function () {
var wDefs = [];
var selectedField = this.fieldSelector.getSelectedField() || {};
if ((this.operator == 'in' && _.contains(['many2one', 'many2many', 'one2many'], selectedField.type) || selectedField.name == "id") && typeof (this.currentDomain[0][2][0]) == 'string') {
this.value = [];
} else {
this.value = this.currentDomain[0][2];
}
this.title = selectedField.string;
this.recordReader = new ModelRecordSelector(
this,
selectedField.relation,
this.chain !== undefined ? this.chain.toString().split(".") : [],
this.options,
this.title
);
if (!this.readonly) {
// Set list of operators according to field type
this.displayValue = this.value;
this.operators = this._getOperatorsFromType(selectedField.type, selectedField.name);
if (_.contains(["child_of", "parent_of", "like", "not like", "=like", "=ilike"], this.operator)) {
// In case user entered manually or from demo data
this.operators[this.operator] = operator_mapping[this.operator];
} else if (!this.operators[this.operator]) {
// In case the domain uses an unsupported operator for the
// field type
this.operators[this.operator] = "?";
}
// Set list of values according to field type
this.selectionChoices = null;
if (selectedField.type === "boolean") {
this.selectionChoices = [["1", _t("set (true)")], ["0", _t("not set (false)")]];
} else if (selectedField.type === "selection") {
this.selectionChoices = selectedField.selection;
}
else if ((_.contains(['many2one', 'many2many', 'one2many'], selectedField.type) || selectedField.name == "id") && (_.contains(['in', 'not in'], this.operator))) {
var model_rel = selectedField.name == "id"? selectedField.model: selectedField.relation
// var def = this.get_rec(model_rel)
// var model_rel = selectedField.relation
this.selectionChoices = null
// this.recordReader.appendTo($("<div/>"))
var self = this;
const ids = (this.value) ? this.value : [];
wDefs.push(this._rpc({
model: model_rel,
method: 'get_widget_name',
kwargs: {
domain: [['id', 'in', ids]],
fields: ['id', 'display_name'],
},
}).then(function (data) {
self.value = [];
if(model_rel == 'res.users' && _.contains(ids,0)){
self.tagsvalues.push([0, 'Environment User']);
self.value.push(0);
}
if(model_rel == 'res.company' && _.contains(ids,0)){
self.tagsvalues.push([0, 'Environment Company']);
self.value.push(0);
}
_.each(data, function (rec, index) {
self.tagsvalues.push([rec.id, rec.display_name]);
// to remove invalid id which is not in records
self.value.push(rec.id);
})
}));
wDefs.push(this.recordReader.appendTo($('<div/>')).then((function () {
this.displayValue = [];
// this.value = [];
}).bind(this)));
}
// Adapt display value and operator for rendering
try {
if (selectedField && !selectedField.relation && !_.isArray(this.value)) {
this.displayValue = field_utils.format[selectedField.type](this.value, selectedField);
}
} catch (err) {/**/ }
this.displayOperator = this.operator;
if (selectedField.type === "boolean") {
this.displayValue = this.value ? "1" : "0";
} else if ((this.operator === "!=" || this.operator === "=") && this.value === false) {
this.displayOperator = this.operator === "!=" ? "set" : "not set";
}
// TODO the value could be a m2o input, etc...
if (_.contains(["date", "datetime"], selectedField.type)) {
this.valueWidget = new (selectedField.type === "datetime" ? datepicker.DateTimeWidget : datepicker.DateWidget)(this);
wDefs.push(this.valueWidget.appendTo("<div/>").then((function () {
this.valueWidget.$el.addClass("o_domain_leaf_value_input");
this.valueWidget.setValue(moment(this.value));
this.valueWidget.on("datetime_changed", this, function () {
this._changeValue(this.valueWidget.getValue());
});
}).bind(this)));
}
return Promise.all(wDefs);
}
}).bind(this)));
return Promise.all(defs);
},
})
/**
@ -1086,8 +1389,19 @@ odoo.define("advanced_web_domain_widget.TerabitsDomainSelector", function (requi
return null
}
function instantiateNode2(parent, model, domain, options) {
if (domain.length > 1) {
return new TerabitsDomainTree2(parent, model, domain, options);
} else if (domain.length === 1) {
return new TerabitsDomainLeaf2(parent, model, domain, options);
}
return null
}
return {
TerabitsDomainSelector:TerabitsDomainSelector,
TerabitsDomainLeaf:TerabitsDomainLeaf
TerabitsDomainSelector2:TerabitsDomainSelector2,
TerabitsDomainLeaf:TerabitsDomainLeaf,
TerabitsDomainLeaf2:TerabitsDomainLeaf2,
};
});

View File

@ -10,7 +10,7 @@ odoo.define("advanced_web_domain_widget.DomainSelectorDialog", function (require
/**
* @class DomainSelectorDialog
*/
return Dialog.extend({
var DomainSelectorDialog = Dialog.extend({
init: function (parent, model, domain, options) {
this.model = model;
this.options = _.extend({
@ -51,5 +51,40 @@ odoo.define("advanced_web_domain_widget.DomainSelectorDialog", function (require
]);
},
});
var DomainSelectorDialog2 = DomainSelectorDialog.extend({
init: function (parent, model, domain, options) {
this.model = model;
this.options = _.extend({
readonly: true,
debugMode: false,
}, options || {});
var buttons;
if (this.options.readonly) {
buttons = [
{text: _t("Close"), close: true},
];
} else {
buttons = [
{text: _t("Save"), classes: "btn-primary", close: true, click: function () {
this.trigger_up("domain_selected", {domain: this.domainSelector.getDomain()});
}},
{text: _t("Discard"), close: true},
];
}
this._super(parent, _.extend({}, {
title: _t("Domain"),
buttons: buttons,
}, options || {}));
this.domainSelector = new DomainSelector.TerabitsDomainSelector2(this, model, domain, options);
},
})
return {
DomainSelectorDialog: DomainSelectorDialog,
DomainSelectorDialog2: DomainSelectorDialog2
}
});

View File

@ -586,7 +586,66 @@ var TerabitsModelFieldSelector = Widget.extend({
}
});
return TerabitsModelFieldSelector;
var TerabitsModelFieldSelector2 = TerabitsModelFieldSelector.extend({
_render: function () {
// Render the chain value
this.$value.html(core.qweb.render(this.template + ".value", {
chain: this.chain,
pages: this.pages,
}));
// Toggle the warning message
this.$valid.toggleClass('d-none', !!this.isValid());
// Adapt the popover content
var page = _.last(this.pages);
var title = "";
if (this.pages.length > 1) {
var prevField = _.findWhere(this.pages[this.pages.length - 2], {
name: (this.chain.length === this.pages.length) ? this.chain[this.chain.length - 2] : _.last(this.chain),
});
if (prevField) title = prevField.string;
}
this.$(".o_field_selector_popover_header .o_field_selector_title").text(title);
var lines = _.filter(page, this.options.filter);
if (this.searchValue) {
var matches = fuzzy.filter(this.searchValue, _.pluck(lines, 'string'));
lines = _.map(_.pluck(matches, 'index'), function (i) {
return lines[i];
});
}
this.$(".o_field_selector_page").replaceWith(core.qweb.render(this.template + ".page2", {
lines: lines,
followRelations: this.options.followRelations,
debug: this.options.debugMode,
}));
this.$input.val(this.chain.join("."));
},
_pushPageData: function (model) {
var def;
if (this.model === model && this.options.fields) {
def = Promise.resolve(sortFields(this.options.fields, model, this.options.order));
} else {
def = this._getModelFieldsFromCache(model, this.options.filters);
}
return def.then((function (fields) {
if(this.pages.length <= 0)
this.pages.push(fields);
}).bind(this));
},
_onPrevPageClick: function () {
// this._goToPrevPage();
},
})
return {ModelFieldSelector: TerabitsModelFieldSelector,
ModelFieldSelector2: TerabitsModelFieldSelector2};
/**
* Allows to transform a mapping field name -> field info in an array of the

View File

@ -104,7 +104,7 @@ odoo.define("advanced_web_domain_widget.ModelRecordSelector", function (require)
;
var def = this._rpc({
model: model,
method: 'search_read',
method: 'get_widget_name',
kwargs: {
domain: [],
fields: ['id', 'display_name'],
@ -113,6 +113,12 @@ odoo.define("advanced_web_domain_widget.ModelRecordSelector", function (require)
if (data) {
this.records = data
}
if(model == 'res.users'){
this.records.push({'id': 0, 'display_name': 'Environment User'})
}
if(model == 'res.company'){
this.records.push({'id': 0, 'display_name': 'Environment Company'})
}
// if(model == 'res.users'){
// this.records.push({'id':0,'display_name':'Environment User'})
// }

View File

View File

@ -16,6 +16,22 @@
</div>
<div t-else="">Select a model to add a filter.</div>
</t>
<t t-name="TerabitsFieldDomain.content2">
<div t-if="hasModel" class="o_field_domain_panel">
<!-- <i class="fa fa-arrow-right" role="img" aria-label="Domain" title="Domain"/>
<button t-if="isValid" class="btn btn-sm btn-secondary o_domain_show_selection_button" type="button">
<t t-esc="nbRecords"/> record(s)
</button> -->
<t t-if="!isValid">
<span class="text-warning" role="alert"><i class="fa fa-exclamation-triangle" role="img" aria-label="Warning" title="Warning"/> Invalid domain</span>
</t>
<button t-if="inDialogEdit" class="btn btn-sm btn-primary o_field_domain_dialog_button">Edit Domain</button>
</div>
<div t-else="">Select a model to add a filter.</div>
</t>
<t t-name="TerabitsDomainNode.ControlPanel">
<div t-if="!widget.readonly &amp;&amp; !widget.noControlPanel" class="o_domain_node_control_panel" role="toolbar" aria-label="Domain node">
<button class="btn o_domain_delete_node_button" title="Delete node" aria-label="Delete node"><i class="fa fa-times"/></button>
@ -259,6 +275,18 @@
</li>
</t>
</ul>
<ul t-name="TerabitsModelFieldSelector.page2" class="o_field_selector_page">
<t t-foreach="lines" t-as="line">
<t t-set="relationToFollow" t-value="followRelations(line) &amp;&amp; line.relation"/>
<li t-attf-class="o_field_selector_item #{relationToFollow and 'o_field_selector_next_page' or 'o_field_selector_select_button'}#{line_index == 0 and ' active' or ''}"
t-att-data-name="line.name">
<t t-esc="line.string"/>
<div t-if="debug" class="text-muted o_field_selector_item_title"><t t-esc="line.name"/> (<t t-esc="line.type"/>)</div>
<!-- <i t-if="relationToFollow" class="fa fa-chevron-right o_field_selector_relation_icon" role="img" aria-label="Relation to follow" title="Relation to follow"/> -->
</li>
</t>
</ul>
<ul t-name="TerabitsModelRecordSelector.page" class="o_record_selector_page">
<t t-foreach="lines" t-as="line">
<!-- <t t-set="relationToFollow" t-value="followRelations(line) &amp;&amp; line.relation"/> -->

View File

View File

@ -14,5 +14,4 @@ def post_install_action_dup_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
action_data_obj = env['action.data']
for action in env['ir.actions.actions'].search([]):
print(action.name,"===================")
action_data_obj.create({'name':action.name,'action_id':action.id})

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#################################################################################
# Author : Terabits Technolab (<www.terabits.xyz>)
# Copyright(c): 2021-23
# Copyright(c): 2023-24
# All Rights Reserved.
#
# This module is copyright property of the author mentioned above.
@ -11,12 +11,12 @@
{
'name': 'Simplify Access Management',
'version': '14.0.11.4.5',
'version': '14.0.12.8.5',
'sequence': 5,
'author': 'Terabits Technolab',
'license': 'OPL-1',
'category': 'Odex25-base',
'website': 'https://www.terabits.xyz/r/SNS',
'category': 'Services',
'website': 'https://www.terabits.xyz/apps/14.0/simplify_access_management',
'summary': """All In One Access Management App for setting the correct access rights for fields, models, menus, views for any module and for any user.
All in one access management App,
Easier then Record rules setup,
@ -160,11 +160,11 @@
""",
"images": ["static/description/banner.gif"],
"price": "370.99",
"price": "280.99",
"currency": "USD",
'data': [
'security/ir.model.access.csv',
'security/res_groups.xml',
'security/ir.model.access.csv',
'data/view_data.xml',
'views/access_management_view.xml',
'views/assets.xml',

View File

@ -1,3 +1,3 @@
# from . import main
from . import action
from . import export

View File

@ -0,0 +1,22 @@
from odoo import http
from odoo.exceptions import UserError
from odoo.addons.web.controllers.main import Export
from odoo.http import request
class Export(Export):
def fields_get(self, model):
fields=super().fields_get(model)
invisible_field_ids = request.env['hide.field'].sudo().search(
[('access_management_id.company_ids', 'in', request.env.company.id),
('model_id.model', '=', model), ('access_management_id.active', '=', True),
('access_management_id.user_ids', 'in', request.env.user.id),
('invisible','=',True)])
if not invisible_field_ids:
return fields
else :
for key, value in list(fields.items()):
for invisible_field in invisible_field_ids.field_id:
if key == invisible_field.name and key != "id":
del fields[key]
return fields

View File

@ -10,14 +10,15 @@ class access_domain_ah(models.Model):
'ir.model', string='Model', index=True, required=True, ondelete='cascade')
model_name = fields.Char(string='Model Name', related='model_id.model', readonly=True, store=True)
apply_domain = fields.Boolean('Apply Filter')
domain = fields.Char(string='Filter', default='[]')
domain = fields.Char(string='Filter', default='[]',
help="The create customised domain rule where we can customise rule by selecting specific fields and records")
access_management_id = fields.Many2one('access.management','Access Management')
read_right = fields.Boolean('Read',default=True)
create_right = fields.Boolean('Create')
write_right = fields.Boolean('Write')
delete_right = fields.Boolean('Delete')
read_right = fields.Boolean('Read',default=True, help="The set 'Read' access of the selected model for the specified users")
create_right = fields.Boolean('Create', help="The set 'Create' access of the selected model for the specified users")
write_right = fields.Boolean('Write', help="The set 'Write' access of the selected model for the specified users")
delete_right = fields.Boolean('Delete', help="The set 'Delete' access of the selected model for the specified users")
@api.onchange('apply_domain')
def _check_domain(self):

View File

@ -14,7 +14,8 @@ class access_management(models.Model):
active = fields.Boolean('Active', default=True)
hide_menu_ids = fields.Many2many('ir.ui.menu', 'access_management_menu_rel_ah', 'access_management_id', 'menu_id',
'Hide Menu')
'Hide Menu',
help="The menu or submenu added on above list will be hidden from the defined users.")
hide_field_ids = fields.One2many('hide.field', 'access_management_id', 'Hide Field', copy=True)
remove_action_ids = fields.One2many('remove.action', 'access_management_id', 'Remove Action', copy=True)
@ -32,16 +33,21 @@ class access_management(models.Model):
# Chatter
hide_chatter_ids = fields.One2many('hide.chatter', 'access_management_id', 'Hide Chatter', copy=True)
hide_chatter = fields.Boolean('Hide Chatter')
hide_send_mail = fields.Boolean('Hide Send Message')
hide_log_notes = fields.Boolean('Hide Log Notes')
hide_schedule_activity = fields.Boolean('Hide Schedule Activity')
hide_chatter = fields.Boolean('Hide Chatter',
help="The Chatter will be hidden in all model from the specified users.")
hide_send_mail = fields.Boolean('Hide Send Message',
help="The Send Message button will be hidden in chatter of all model from the specified users.")
hide_log_notes = fields.Boolean('Hide Log Notes',
help="The Log Notes button will be hidden in chatter of all model from the specified users.")
hide_schedule_activity = fields.Boolean('Hide Schedule Activity',
help="The Schedule Activity button will be hidden in chatter of all model from the specified users.")
hide_export = fields.Boolean()
hide_import = fields.Boolean()
disable_login = fields.Boolean('Disable Login')
hide_export = fields.Boolean(help="The Export button will be hidden in all model from the specified users.")
hide_import = fields.Boolean(help="The Import button will be hidden in all model from the specified users.")
disable_login = fields.Boolean('Disable Login',help="The Users can not login if this button is chek.")
disable_debug_mode = fields.Boolean('Disable Developer Mode')
disable_debug_mode = fields.Boolean('Disable Developer Mode',
help="Developer mode will be hidden from the defined users.")
company_ids = fields.Many2many('res.company', 'access_management_comapnay_rel', 'access_management_id',
'company_id', 'Companies', required=True, default=lambda self: self.env.company)
@ -49,6 +55,7 @@ class access_management(models.Model):
hide_filters_groups_ids = fields.One2many('hide.filters.groups', 'access_management_id', 'Hide Filters/Group By',
copy=True)
is_apply_on_without_company = fields.Boolean()
def _count_total_rules(self):
for rec in self:
rule = 0
@ -110,7 +117,7 @@ class access_management(models.Model):
return res
def get_remove_options(self, model):
restrict_export = self.env['access.management'].search([('company_ids', 'in', self.env.company.id),
restrict_export = self.env['access.management'].sudo().search([('company_ids', 'in', self.env.company.id),
('active', '=', True),
('user_ids', 'in', self.env.user.id),
('hide_export', '=', True)], limit=1).id
@ -158,7 +165,7 @@ class access_management(models.Model):
hide_schedule_activity = False
if model and hide_send_mail or hide_log_notes or hide_schedule_activity:
hide_ids = self.env['hide.chatter'].search([('access_management_id.company_ids', 'in', company_id),
hide_ids = self.env['hide.chatter'].sudo().search([('access_management_id.company_ids', 'in', company_id),
('access_management_id.active', '=', True),
('access_management_id.user_ids', 'in', user_id),
('model_id.model', '=', model)])
@ -192,9 +199,23 @@ class access_management(models.Model):
break
if not hide_export and model:
if self.env['remove.action'].search([('access_management_id', 'in', access_ids.ids),
if self.env['remove.action'].sudo().search([('access_management_id', 'in', access_ids.ids),
('model_id.model', '=', model),
('restrict_export', '=', True)]):
hide_export = True
return hide_export
def get_hidden_field(self, model=False):
if model:
hidden_fields = []
hide_field_obj = self.env['hide.field'].sudo()
for hide_field in hide_field_obj.search(
[('access_management_id.company_ids', 'in', self.env.company.id),
('model_id.model', '=', model), ('access_management_id.active', '=', True),
('access_management_id.user_ids', 'in', self._uid), ('invisible', '=', True)]):
for field in hide_field.field_id:
if field.name:
hidden_fields.append(field.name)
return hidden_fields
return []

View File

@ -9,7 +9,9 @@ class hide_chatter(models.Model):
access_management_id = fields.Many2one('access.management', 'Access Management')
model_id = fields.Many2one('ir.model', 'Model')
hide_chatter = fields.Boolean('Chatter')
hide_send_mail = fields.Boolean('Send Message')
hide_log_notes = fields.Boolean('Log Notes')
hide_schedule_activity = fields.Boolean('Schedule Activity')
hide_chatter = fields.Boolean('Chatter'
,help="The Chatter will be hidden in selected model from the specified users.")
hide_send_mail = fields.Boolean('Send Message'
,help="The Send Message button will be hidden in chatter of selected model from the specified users.")
hide_log_notes = fields.Boolean('Log Notes', help="The Log Notes button will be hidden in chatter of selected model from the specified users.")
hide_schedule_activity = fields.Boolean('Schedule Activity',help="The Schedule Activity button will be hidden in chatter of selected model from the specified users.")

View File

@ -11,8 +11,8 @@ class hide_field(models.Model):
field_id = fields.Many2many('ir.model.fields', 'hide_field_ir_model_fields_rel', 'hide_field_id', 'ir_field_id', 'Field')
invisible = fields.Boolean('Invisible')
readonly = fields.Boolean('Read-Only')
required = fields.Boolean('Required')
external_link = fields.Boolean('Remove External Link')
invisible = fields.Boolean('Invisible', help="Selected Field will be hidden in selected model from the defined users.")
readonly = fields.Boolean('Read-Only', help="Selected Field will be Read only in selected model from the defined users.")
required = fields.Boolean('Required', help="Selected Field will be set as required for selected model from the defined users.")
external_link = fields.Boolean('Remove External Link',help="External Link will be hidden for relational fields in selected model from the defined users.")

View File

@ -9,9 +9,11 @@ class hide_filters_groups(models.Model):
model_name = fields.Char(string='Model Name', related='model_id.model', readonly=True, store=True)
filters_store_model_nodes_ids = fields.Many2many('store.filters.groups', 'filters_hide_filters_groups_store_filters_groups_rel',
'hide_id', 'store_id', string='Hide Filters', domain="[('node_option','=','filter')]")
'hide_id', 'store_id', string='Hide Filters', domain="[('node_option','=','filter')]",
help="The Defalut filter are added on list will be hidden in search view of selected model from the specified users.")
groups_store_model_nodes_ids = fields.Many2many('store.filters.groups', 'groups_hide_filters_groups_store_filters_groups_rel',
'hide_id', 'store_id', string='Hide Groups', domain="[('node_option','=','group')]")
'hide_id', 'store_id', string='Hide Groups', domain="[('node_option','=','group')]",
help="The Defalut Group are added on list will be hidden in search view of selected model from the specified users.")
access_management_id = fields.Many2one('access.management', 'Access Management')

View File

@ -12,9 +12,12 @@ class hide_view_nodes(models.Model):
model_name = fields.Char(string='Model Name', related='model_id.model', readonly=True, store=True)
btn_store_model_nodes_ids = fields.Many2many('store.model.nodes','btn_hide_view_nodes_store_model_nodes_rel','hide_id','store_id',string='Hide Button',domain="[('node_option','=','button')]")
page_store_model_nodes_ids = fields.Many2many('store.model.nodes','page_hide_view_nodes_store_model_nodes_rel','hide_id','store_id',string='Hide Tab/Page',domain="[('node_option','=','page')]")
link_store_model_nodes_ids = fields.Many2many('store.model.nodes','link_hide_view_nodes_store_model_nodes_rel','hide_id','store_id',string='Hide Kanban Link',domain="[('node_option','=','link')]")
btn_store_model_nodes_ids = fields.Many2many('store.model.nodes','btn_hide_view_nodes_store_model_nodes_rel','hide_id','store_id',string='Hide Button',domain="[('node_option','=','button')]",
help="The Buttons are added on list will be hidden in selected model from the defined users.")
page_store_model_nodes_ids = fields.Many2many('store.model.nodes','page_hide_view_nodes_store_model_nodes_rel','hide_id','store_id',string='Hide Tab/Page',domain="[('node_option','=','page')]",
help="The Tabs(pages) are added on list will be hidden in selected model from the defined users.")
link_store_model_nodes_ids = fields.Many2many('store.model.nodes','link_hide_view_nodes_store_model_nodes_rel','hide_id','store_id',string='Hide Kanban Link',domain="[('node_option','=','link')]",
help="The Kanban view action are added on list will be hidden in selected model from the defined users.")
access_management_id = fields.Many2one('access.management','Access Management')

View File

@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
import logging
from odoo.http import request
from odoo import api, fields, models, tools, _
from odoo.exceptions import Warning, ValidationError, AccessError
from odoo import api, fields, models, tools,_
from odoo.exceptions import Warning, ValidationError,AccessError
_logger = logging.getLogger(__name__)
class ir_model_access(models.Model):
_inherit = 'ir.model.access'
# The context parameter is useful when the method translates error messages.
# But as the method raises an exception in that case, the key 'lang' might
# not be really necessary as a cache key, unless the `ormcache_context`
@ -30,6 +31,7 @@ class ir_model_access(models.Model):
_logger.error('Missing model %s', model)
self.flush(self._fields)
"""
This part is writen to by pass base access rule and apply dynamic rule of access management rule,
@ -41,7 +43,7 @@ class ir_model_access(models.Model):
if model:
self._cr.execute("SELECT id FROM ir_model WHERE model='" + model + "'")
model_numeric_id = self._cr.fetchone()[0]
if model_numeric_id and isinstance(model_numeric_id, int) and self.env.user:
if model_numeric_id and isinstance(model_numeric_id,int) and self.env.user:
try:
self._cr.execute("""
SELECT dm.id
@ -53,13 +55,14 @@ class ir_model_access(models.Model):
IN (SELECT amusr.access_management_id
FROM access_management_users_rel_ah as amusr
WHERE amusr.user_id=%s))
""", [model_numeric_id, self.env.user.id])
""",[model_numeric_id, self.env.user.id])
except:
pass
access_domain_ah_ids = self.env['access.domain.ah'].browse(row[0] for row in self._cr.fetchall()).filtered(lambda line: self.env.company in line.access_management_id.company_ids)
access_domain_ah_ids = self.env['access.domain.ah'].sudo().browse(row[0] for row in self._cr.fetchall()).filtered(lambda line: self.env.company in line.access_management_id.company_ids)
if access_domain_ah_ids:
return True
# We check if a specific rule exists
self._cr.execute("""SELECT MAX(CASE WHEN perm_{mode} THEN 1 ELSE 0 END)
FROM ir_model_access a
@ -88,7 +91,7 @@ class ir_model_access(models.Model):
msg_heads = {
# Messages are declared in extenso so they are properly exported in translation terms
'read': _("You are not allowed to access '%(document_kind)s' (%(document_model)s) records.", document_kind=document_kind, document_model=model),
'write': _("You are not allowed to modify '%(document_kind)s' (%(document_model)s) records.", document_kind=document_kind, document_model=model),
'write': _("You are not allowed to modify '%(document_kind)s' (%(document_model)s) records.", document_kind=document_kind, document_model=model),
'create': _("You are not allowed to create '%(document_kind)s' (%(document_model)s) records.", document_kind=document_kind, document_model=model),
'unlink': _("You are not allowed to delete '%(document_kind)s' (%(document_model)s) records.", document_kind=document_kind, document_model=model),
}
@ -102,7 +105,11 @@ class ir_model_access(models.Model):
resolution_info = _("Contact your administrator to request access if necessary.")
_logger.info('Access Denied by ACLs for operation: %s, uid: %s, model: %s', mode, self._uid, model)
msg = """{operation_error} {group_info} {resolution_info}""".format(
msg = """{operation_error}
{group_info}
{resolution_info}""".format(
operation_error=operation_error,
group_info=group_info,
resolution_info=resolution_info)
@ -116,24 +123,22 @@ class ir_model_access(models.Model):
if data and data[0] != 'installed':
read_value = False
if self.env.user.id and read_value:
company_id = request.httprequest.cookies.get('cids') and request.httprequest.cookies.get('cids').split(',')[0] or request.env.company.id
a = "select access_management_id from access_management_comapnay_rel where company_id = " + company_id
if not company_id:
a = "select access_management_id from access_management_comapnay_rel where company_id = NULL"
a = "select access_management_id from access_management_comapnay_rel where company_id = " + str(request.httprequest.cookies.get('cids') and request.httprequest.cookies.get('cids').split(',')[0] or request.env.company.id)
self._cr.execute(a)
a = self._cr.fetchall()
if a:
a = "select access_management_id from access_management_users_rel_ah where user_id = " + str(self.env.user.id) + " AND access_management_id in " + str(tuple([i[0] for i in a] + [0]))
if a:
a = "select access_management_id from access_management_users_rel_ah where user_id = " + str(self.env.user.id) + " AND access_management_id in " + str(tuple([i[0] for i in a]+[0]))
self._cr.execute(a)
a = self._cr.fetchall()
if a:
a = "SELECT id FROM access_management WHERE active='t' AND id in " + str(tuple([i[0] for i in a] + [0])) + " and readonly = True"
a = "SELECT id FROM access_management WHERE active='t' AND id in " + str(tuple([i[0] for i in a]+[0])) + " and readonly = True"
self._cr.execute(a)
a = self._cr.fetchall()
if bool(a):
if mode != 'read':
if mode != 'read':
return False
except:
pass
return bool(r)

View File

@ -33,14 +33,14 @@ class ir_rule(models.Model):
model_list = ['mail.activity','res.users.log','res.users','mail.channel','mail.alias','bus.presence','res.lang']
if self.env.user.id and read_value and not all_data:
if model_name not in model_list:
# self._cr.execute("""SELECT am.id FROM access_management as am
# WHERE active='t' AND readonly = True AND am.id
# IN (SELECT au.access_management_id
# FROM access_management_users_rel_ah as au
# WHERE user_id = %s AND am.id
# IN (SELECT ac.access_management_id
# FROM access_management_comapnay_rel as ac
# WHERE ac.company_id=%s))"""%(self.env.user.id, self.env.company.id))
self._cr.execute("""SELECT am.id FROM access_management as am
WHERE active='t' AND readonly = True AND am.id
IN (SELECT au.access_management_id
FROM access_management_users_rel_ah as au
WHERE user_id = %s AND am.id
IN (SELECT ac.access_management_id
FROM access_management_comapnay_rel as ac
WHERE ac.company_id=%s))"""%(self.env.user.id, self.env.company.id))
# a = "select access_management_id from access_management_comapnay_rel where company_id = " + str(self.env.company.id)
# self._cr.execute(a)
# a = self._cr.fetchall()
@ -85,11 +85,13 @@ class ir_rule(models.Model):
""", [model_numeric_id, self.env.user.id])
except:
pass
access_domain_ah_ids = self.env['access.domain.ah'].browse(
access_domain_ah_ids = self.env['access.domain.ah'].sudo().browse(
row[0] for row in self._cr.fetchall()).filtered(
lambda line: self.env.company in line.access_management_id.company_ids)
# access_domain_ah_ids = access_domain_ah_ids.filtered(lambda line: self.env.company in line.access_management_id.company_ids)
if access_domain_ah_ids:
all_company = self.env['res.company'].sudo().search([]).ids
company_domain = []
domain_list = []
if model_name == 'res.partner':
# jo aya user related jetala partner 6 ana access alag thi apididha 6 error no ave atle
@ -99,8 +101,23 @@ class ir_rule(models.Model):
eval_context = self._eval_context()
# only domain records
length = len(access_domain_ah_ids.sudo()) if access_domain_ah_ids.sudo() else 0
com_domain=[]
for access in access_domain_ah_ids.sudo():
dom = safe_eval(access.domain, eval_context) if access.domain else []
if not dom and isinstance(dom,list):
if length>1:
domain_list.insert(0,'|')
domain_list += [('id', '!=', False)]
length -= 1
company_domain=[]
if self.env[model_name]._fields.get('company_id'):
if res:
for domain_tuple in res:
if isinstance(domain_tuple,tuple) and domain_tuple[0] == 'company_id':
all_company = self.env['res.company'].sudo().browse(domain_tuple[2]).ids
company_domain_tuple = ('company_id','in',access.access_management_id.company_ids.ids)
company_domain.append(company_domain_tuple)
if dom:
dom = expression.normalize_domain(dom)
for dom_tuple in dom:
@ -138,14 +155,49 @@ class ir_rule(models.Model):
right_value[zero_index] = self.env.company.id
if operator_value == 'date_filter':
domain_list += prepare_domain_v2(dom_tuple)
domain_list.insert(0,'&')
domain_list+= company_domain
else:
domain_list.append(dom_tuple)
if company_domain:
company_domain.append(dom_tuple)
domain_list.insert(0,'&')
domain_list+= company_domain
else:
domain_list.append(dom_tuple)
else:
domain_list.append(dom_tuple)
if length > 1:
domain_list.insert(0, '|')
length -= 1
if self.env[model_name]._fields.get('company_id'):
if res:
for domain_tuple in res:
dom = domain_tuple
if isinstance(dom,tuple):
if domain_tuple[0] == 'company_id':
if len(all_company)>1:
for company in access.access_management_id.company_ids.ids:
if company in all_company:
all_company.remove(company)
if all_company and False not in all_company:
if access.access_management_id.is_apply_on_without_company:
domain = ('company_id','in',all_company+[False])
else:
domain = ('company_id','in',all_company)
if domain not in com_domain:
com_domain.append(('company_id','in',all_company))
else:
if isinstance(domain_tuple[2],list):
if domain_tuple[2][0] not in access.access_management_id.company_ids.ids:
com_domain.append(domain_tuple)
else:
com_domain.append(domain_tuple)
if domain_list:
if com_domain:
domain_list.insert(0, '|')
domain_list+=com_domain
return domain_list
return res

View File

@ -13,12 +13,12 @@ class ir_ui_view(models.Model):
hide_button_obj = self.env['hide.view.nodes']
if name_manager.Model._name == 'res.config.settings' and node.tag == 'div' and node.get('string'):
for setting_tab in hide_button_obj.sudo().search([('access_management_id.company_ids','in',self.env.company.id),('model_id.model','=',name_manager.Model._name),('access_management_id.active','=',True),('access_management_id.user_ids','in',self._uid)]).mapped('page_store_model_nodes_ids'):
if node.get('string') == setting_tab.attribute_string and node.get('data-key') == setting_tab.attribute_name:
if node.get('data-key') == setting_tab.attribute_name:
node_info['modifiers']['invisible'] = True
node.set('invisible', '1')
if node.tag == 'a':
if node.text and '\n' not in node.text and 'type' in node.attrib.keys() and node.attrib['type'] and 'name' in node.attrib.keys() and node.attrib['name']:
if hide_view_node_obj.search([
if hide_view_node_obj.sudo().search([
('model_id.model','=',name_manager.Model._name),
('access_management_id.active','=',True),
('access_management_id.company_ids','in',self.env.company.id),
@ -38,7 +38,7 @@ class ir_ui_view(models.Model):
if hide_field.external_link:
options_dict = {}
if 'widget' in node.attrib.keys():
if node.attrib['widget'] == 'product_configurator':
if node.attrib['widget'] == 'product_configurator' or node.attrib['widget'] == 'many2one_avatar_user':
del node.attrib['widget']
if 'options' in node.attrib.keys():
options_dict = ast.literal_eval(node.attrib['options'])

View File

@ -15,7 +15,7 @@ class BaseModel(models.AbstractModel):
@api.model
def load_views(self, views, options=None):
actions_and_prints = []
for access in self.env['remove.action'].search([('access_management_id.company_ids', 'in', self.env.company.id),
for access in self.env['remove.action'].sudo().search([('access_management_id.company_ids', 'in', self.env.company.id),
('access_management_id', 'in',
self.env.user.access_management_ids.ids),
('model_id.model', '=', self._name)]):
@ -49,13 +49,13 @@ class BaseModel(models.AbstractModel):
res = super().fields_view_get(view_id, view_type, toolbar, submenu)
access_management_obj = self.env['access.management']
cids = request.httprequest.cookies.get('cids') and request.httprequest.cookies.get('cids').split(',')[0] or request.env.company.id
readonly_access_id = access_management_obj.search([('company_ids','in',int(cids)),('active','=',True),('user_ids','in',self.env.user.id),('readonly','=',True)])
readonly_access_id = access_management_obj.sudo().search([('company_ids','in',int(cids)),('active','=',True),('user_ids','in',self.env.user.id),('readonly','=',True)])
access_recs = self.env['access.domain.ah'].search([('access_management_id.company_ids','in',self.env.company.id),('access_management_id.user_ids','in',self.env.user.id),('access_management_id.active','=',True),('model_id.model','=',res['model'])])
access_model_recs = self.env['remove.action'].search([('access_management_id.company_ids','in',self.env.company.id),('access_management_id.user_ids','in',self.env.user.id),('access_management_id.active','=',True),('model_id.model','=',res['model'])])
access_recs = self.env['access.domain.ah'].sudo().search([('access_management_id.company_ids','in',self.env.company.id),('access_management_id.user_ids','in',self.env.user.id),('access_management_id.active','=',True),('model_id.model','=',res['model'])])
access_model_recs = self.env['remove.action'].sudo().search([('access_management_id.company_ids','in',self.env.company.id),('access_management_id.user_ids','in',self.env.user.id),('access_management_id.active','=',True),('model_id.model','=',res['model'])])
if view_type == 'form':
access_management_id = access_management_obj.search([('company_ids', 'in', self.env.company.id),
access_management_id = access_management_obj.sudo().search([('company_ids', 'in', self.env.company.id),
('active', '=', True),
('user_ids', 'in', self.env.user.id),
('hide_chatter', '=', True)],
@ -66,7 +66,7 @@ class BaseModel(models.AbstractModel):
div.getparent().remove(div)
res['arch'] = etree.tostring(doc, encoding='unicode')
else:
if self.env['hide.chatter'].search([('access_management_id.company_ids', 'in', self.env.company.id),
if self.env['hide.chatter'].sudo().search([('access_management_id.company_ids', 'in', self.env.company.id),
('access_management_id.active', '=', True),
('access_management_id.user_ids', 'in', self.env.user.id),
('model_id.model', '=', self._name),
@ -78,7 +78,7 @@ class BaseModel(models.AbstractModel):
div.getparent().remove(div)
res['arch'] = etree.tostring(doc, encoding='unicode')
restrict_import = access_management_obj.search([('company_ids', 'in', self.env.company.id),
restrict_import = access_management_obj.sudo().search([('company_ids', 'in', self.env.company.id),
('active', '=', True),
('user_ids', 'in', self.env.user.id),
('hide_import', '=', True)], limit=1).id
@ -187,7 +187,7 @@ class BaseModel(models.AbstractModel):
FROM access_management_users_rel_ah as amusr
WHERE amusr.user_id=%s))
""",[model_numeric_id, self.env.user.id])
records = self.env['access.domain.ah'].browse(row[0] for row in self._cr.fetchall())
records = self.env['access.domain.ah'].sudo().browse(row[0] for row in self._cr.fetchall())
except:
pass
return records
@ -214,50 +214,50 @@ class BaseModel(models.AbstractModel):
domain_list += partner_domain
# eval_context = rec._eval_context()
dom = safe_eval(record.domain) if record.domain else []
dom = expression.normalize_domain(dom)
model_name = self._name
if isinstance(dom,list):
remove_tuple = False
for dom_tuple in dom:
if isinstance(dom_tuple, tuple):
left_value = dom_tuple[0]
operator_value = dom_tuple[1]
right_value = dom_tuple[2]
left_value_split_list = left_value.split('.')
model_string = model_name
left_user = False
left_company = False
for field in left_value_split_list:
if dom:
dom = expression.normalize_domain(dom)
model_name = self._name
if isinstance(dom,list):
for dom_tuple in dom:
if isinstance(dom_tuple, tuple):
left_value = dom_tuple[0]
operator_value = dom_tuple[1]
right_value = dom_tuple[2]
left_value_split_list = left_value.split('.')
model_string = model_name
left_user = False
left_company = False
model_obj = self.env[model_string]
field_type = model_obj.fields_get()[field]['type']
if field_type in ['many2one', 'many2many', 'one2many']:
field_relation = model_obj.fields_get()[field]['relation']
model_string = field_relation
if model_string == 'res.users':
left_user = True
if model_string == 'res.company':
left_company = True
for field in left_value_split_list:
left_user = False
left_company = False
model_obj = self.env[model_string]
field_type = model_obj.fields_get()[field]['type']
if field_type in ['many2one', 'many2many', 'one2many']:
field_relation = model_obj.fields_get()[field]['relation']
model_string = field_relation
if model_string == 'res.users':
left_user = True
if model_string == 'res.company':
left_company = True
if left_user:
if operator_value in ['in', 'not in']:
if isinstance(right_value, list) and 0 in right_value:
zero_index = right_value.index(0)
right_value[zero_index] = self.env.user.id
if left_user:
if operator_value in ['in', 'not in']:
if isinstance(right_value, list) and 0 in right_value:
zero_index = right_value.index(0)
right_value[zero_index] = self.env.user.id
if left_company:
if operator_value in ['in', 'not in']:
if isinstance(right_value, list) and 0 in right_value:
zero_index = right_value.index(0)
right_value[zero_index] = self.env.company.id
already_add = False
if operator_value == 'date_filter':
domain_list += prepare_domain_v2(dom_tuple)
if left_company:
if operator_value in ['in', 'not in']:
if isinstance(right_value, list) and 0 in right_value:
zero_index = right_value.index(0)
right_value[zero_index] = self.env.company.id
already_add = False
if operator_value == 'date_filter':
domain_list += prepare_domain_v2(dom_tuple)
else:
domain_list.append(dom_tuple)
else:
domain_list.append(dom_tuple)
else:
domain_list.append(dom_tuple)
# if length > 1:
# domain_list.insert(0, '|')
# length -= 1

View File

@ -7,16 +7,23 @@ class remove_action(models.Model):
access_management_id = fields.Many2one('access.management', 'Access Management')
model_id = fields.Many2one('ir.model', 'Model')
view_data_ids = fields.Many2many('view.data', 'remove_action_view_data_rel_ah', 'remove_action_id', 'view_data_id', 'Hide Views')
server_action_ids = fields.Many2many('action.data' ,'remove_action_server_action_data_rel_ah', 'remove_action_id', 'server_action_id', 'Hide Actions', domain="[('action_id.binding_model_id','=',model_id),('action_id.type','!=','ir.actions.report')]")
report_action_ids = fields.Many2many('action.data' ,'remove_action_report_action_data_rel_ah', 'remove_action_id', 'report_action_id', 'Hide Reports', domain="[('action_id.binding_model_id','=',model_id),('action_id.type','=','ir.actions.report')]")
restrict_export = fields.Boolean('Hide Export')
restrict_import = fields.Boolean('Hide Import')
view_data_ids = fields.Many2many('view.data', 'remove_action_view_data_rel_ah', 'remove_action_id', 'view_data_id', 'Hide Views',
help="The views are added on list will be hidden in selected model from the defined users.")
server_action_ids = fields.Many2many('action.data' ,'remove_action_server_action_data_rel_ah', 'remove_action_id', 'server_action_id', 'Hide Actions', domain="[('action_id.binding_model_id','=',model_id),('action_id.type','!=','ir.actions.report')]",
help="The actions are added on list will be hidden in selected model from the defined users.")
report_action_ids = fields.Many2many('action.data' ,'remove_action_report_action_data_rel_ah', 'remove_action_id', 'report_action_id', 'Hide Reports', domain="[('action_id.binding_model_id','=',model_id),('action_id.type','=','ir.actions.report')]",
help="The Reports are added on list will be hidden in selected model from the defined users.")
restrict_export = fields.Boolean('Hide Export',
help="Export Button will be hidden in selected model from the defined users.")
restrict_import = fields.Boolean('Hide Import',
help="Import Button will be hidden in selected model from the defined users.")
readonly = fields.Boolean('Read-Only')
restrict_create = fields.Boolean('Hide Create')
restrict_edit = fields.Boolean('Hide Edit')
restrict_delete = fields.Boolean('Hide Delete')
restrict_archive_unarchive = fields.Boolean('Hide Archive/Unarchive')
restrict_duplicate = fields.Boolean('Hide Duplicate')
restrict_chatter = fields.Boolean('Hide Chatter')
restrict_create = fields.Boolean('Hide Create',help="Create Button will be hidden in selected model from the defined users.")
restrict_edit = fields.Boolean('Hide Edit',
help="Edit Button will be hidden in selected model from the defined users.")
restrict_delete = fields.Boolean('Hide Delete',
help="Delete Button will be hidden in selected model from the defined users.")
restrict_archive_unarchive = fields.Boolean('Hide Archive/Unarchive',help="Archive and Unarchive action will be hidden in selected model from the defined users.")
restrict_duplicate = fields.Boolean('Hide Duplicate',help="Duplicate action will be hidden in selected model from the defined users.")
restrict_chatter = fields.Boolean('Hide Chatter',help="The Chatter will be hidden in selected model from the defined users.")

View File

@ -37,7 +37,7 @@ class res_users(models.Model):
self = api.Environment(cr, SUPERUSER_ID, {})[cls._name]
access_management_obj = self.env['access.management']
if access_management_obj.search([('user_ids','in',res),('disable_login','=',True)]).id:
if access_management_obj.sudo().search([('user_ids','in',res),('disable_login','=',True)]).id:
raise AccessDenied()
except AccessDenied:
_logger.info("Login failed for db:%s login:%s from ", db, login)

View File

@ -1,12 +1,22 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_access_management,access.access.management,model_access_management,,1,1,1,1
access_remove_action,access.remove.action,model_remove_action,,1,1,1,1
access_hide_field,access.hide.field,model_hide_field,,1,1,1,1
access_access_management_portal_user,access.access.management.portal.user,model_access_management,,1,0,0,0
access_remove_action_portal_user,access.remove.action.portal.user,model_remove_action,,1,0,0,0
access_hide_field_portal_user,access.hide.field.portal.user,model_hide_field,,1,0,0,0
access_access_domain_ah_portal_user,access.access.domain.ah.portal.user,model_access_domain_ah,,1,0,0,0
access_hide_view_nodes_portal_user,access.hide.view.nodes.portal.user,model_hide_view_nodes,,1,0,0,0
access_hide_filters_groups_portal_user,access_hide_filters_groups_portal_user,model_hide_filters_groups,base.group_user,1,0,0,0
access_hide_chatter_portal_user,access.hide.chatter.portal.user,model_hide_chatter,,1,0,0,0
access_action_data,access.action.data,model_action_data,,1,1,1,1
access_view_data,access.view.data,model_view_data,,1,1,1,1
access_access_domain_ah,access.access.domain.ah,model_access_domain_ah,,1,1,1,1
access_hide_view_nodes,access.hide.view.nodes,model_hide_view_nodes,,1,1,1,1
access_store_model_nodes,access.store.model.nodes,model_store_model_nodes,,1,1,1,1
access_hide_filters_groups,access_hide_filters_groups,model_hide_filters_groups,base.group_user,1,1,1,1
access_store_filters_groups,access_store_filters_groups,model_store_filters_groups,base.group_user,1,1,1,1
access_hide_chatter,access.hide.chatter,model_hide_chatter,,1,1,1,1
access_menu_item,access.menu.item,model_menu_item,,1,1,1,1
access_access_management,access.access.management,model_access_management,simplify_access_management.group_access_management_bits,1,1,1,1
access_access_domain_ah,access.access.domain.ah,model_access_domain_ah,simplify_access_management.group_access_management_bits,1,1,1,1
access_remove_action,access.remove.action,model_remove_action,simplify_access_management.group_access_management_bits,1,1,1,1
access_hide_field,access.hide.field,model_hide_field,simplify_access_management.group_access_management_bits,1,1,1,1
access_hide_view_nodes,access.hide.view.nodes,model_hide_view_nodes,simplify_access_management.group_access_management_bits,1,1,1,1
access_hide_filters_groups,access_hide_filters_groups,model_hide_filters_groups,simplify_access_management.group_access_management_bits,1,1,1,1
access_hide_chatter,access.hide.chatter,model_hide_chatter,simplify_access_management.group_access_management_bits,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_access_management access_access_management_portal_user access.access.management access.access.management.portal.user model_access_management 1 1 0 1 0 1 0
3 access_remove_action access_remove_action_portal_user access.remove.action access.remove.action.portal.user model_remove_action 1 1 0 1 0 1 0
4 access_hide_field access_hide_field_portal_user access.hide.field access.hide.field.portal.user model_hide_field 1 1 0 1 0 1 0
5 access_access_domain_ah_portal_user access.access.domain.ah.portal.user model_access_domain_ah 1 0 0 0
6 access_hide_view_nodes_portal_user access.hide.view.nodes.portal.user model_hide_view_nodes 1 0 0 0
7 access_hide_filters_groups_portal_user access_hide_filters_groups_portal_user model_hide_filters_groups base.group_user 1 0 0 0
8 access_hide_chatter_portal_user access.hide.chatter.portal.user model_hide_chatter 1 0 0 0
9 access_action_data access.action.data model_action_data 1 1 1 1
10 access_action_data access_view_data access.action.data access.view.data model_action_data model_view_data 1 1 1 1
11 access_view_data access_store_model_nodes access.view.data access.store.model.nodes model_view_data model_store_model_nodes 1 1 1 1
access_access_domain_ah access.access.domain.ah model_access_domain_ah 1 1 1 1
access_hide_view_nodes access.hide.view.nodes model_hide_view_nodes 1 1 1 1
12 access_store_model_nodes access_store_filters_groups access.store.model.nodes access_store_filters_groups model_store_model_nodes model_store_filters_groups base.group_user 1 1 1 1
access_hide_filters_groups access_hide_filters_groups model_hide_filters_groups base.group_user 1 1 1 1
13 access_store_filters_groups access_menu_item access_store_filters_groups access.menu.item model_store_filters_groups model_menu_item base.group_user 1 1 1 1
14 access_hide_chatter access_access_management access.hide.chatter access.access.management model_hide_chatter model_access_management simplify_access_management.group_access_management_bits 1 1 1 1
15 access_access_domain_ah access.access.domain.ah model_access_domain_ah simplify_access_management.group_access_management_bits 1 1 1 1
16 access_remove_action access.remove.action model_remove_action simplify_access_management.group_access_management_bits 1 1 1 1
17 access_hide_field access.hide.field model_hide_field simplify_access_management.group_access_management_bits 1 1 1 1
18 access_hide_view_nodes access.hide.view.nodes model_hide_view_nodes simplify_access_management.group_access_management_bits 1 1 1 1
19 access_hide_filters_groups access_hide_filters_groups model_hide_filters_groups simplify_access_management.group_access_management_bits 1 1 1 1
20 access_hide_chatter access.hide.chatter model_hide_chatter simplify_access_management.group_access_management_bits 1 1 1 1
21
22

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="group_access_management_spt" model="res.groups">
<record id="group_access_management_bits" model="res.groups">
<field name="name">Access Management</field>
</record>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -0,0 +1,35 @@
odoo.define("simpily_access_management.custom_filter_item", function (require) {
"use strict";
const ControlPanel = require("web.ControlPanel");
const { patch } = require("web.utils");
var rpc = require("web.rpc");
patch(ControlPanel, "ControlPanelPatchBits", {
async mounted() {
var self = this;
this._super(...arguments);
self.removedFields = await rpc.query({
model: "access.management",
method: "get_hidden_field",
args: ["", this?.props?.view?.model],
});
this.removedFields.forEach((element) => {
delete self.fields[element];
});
},
async patched() {
var self = this;
this._super(...arguments);
self.removedFields = await rpc.query({
model: "access.management",
method: "get_hidden_field",
args: ["", this?.props?.view?.model],
});
this.removedFields.forEach((element) => {
delete self.fields[element];
});
},
});
});

View File

@ -0,0 +1,24 @@
odoo.define(
"simpily_access_management.group_by_menu",
function (require) {
"use strict";
const { patch } = require("web.utils");
const GroupByMenu = require("web.GroupByMenu");
var rpc = require("web.rpc");
patch(GroupByMenu, "GroupByMenuHideFieldPatch", {
async willStart() {
await this._super(...arguments);
const res = await rpc.query({
model: "access.management",
method: "get_hidden_field",
args: ["", this?.env?.searchModel?.config?.modelName],
});
debugger
this.fields = this.fields.filter((ele) => !res.includes(ele.name));
},
});
}
);

View File

@ -2,55 +2,60 @@ odoo.define('simplify_access_management.hide_chatter', function (require) {
"use strict";
var FormRenderer = require('web.FormRenderer');
var Session = require("web.Session");
var session = require("web.Session");
var rpc = require('web.rpc');
FormRenderer.include({
_render: function () {
const res = this._super.apply(this, arguments);
_render: async function () {
const res = await this._super.apply(this, arguments);
const self = this;
// this._super.apply(this, arguments);
var hash = window.location.hash.substring(1);
hash = JSON.parse('{"' + hash.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) { return key===""?value:decodeURIComponent(value) })
if(!hash.cids) {
const session = this.getSession();
hash.cids = String(session.company_id);
var hash = window.location.hash.replace("#", '').split("&");
let cids;
if(hash.findIndex(ele => ele.includes("cid")) == -1)
cids = session.company_id;
else {
cids = hash.filter(ele => ele.includes("cid"))[0].split("=")[1].split(",");
cids = cids.length > 0? parseInt(cids[0]): session.company_id;
}
rpc.query({
model:'access.management',
method: 'get_chatter_hide_details',
args: [parseInt(hash.cids.charAt(0)), hash.model]
}).then(function(result){
if(result['hide_send_mail'] == false)
{
var btn1 = setInterval(function() {
if ($('.o_ChatterTopbar_buttonSendMessage').length) {
$('.o_ChatterTopbar_buttonSendMessage').remove();
clearInterval(btn1);
}
}, 50);
}
if(result['hide_log_notes'] == false)
{
var btn2 = setInterval(function() {
if ($('.o_ChatterTopbar_buttonLogNote').length) {
$('.o_ChatterTopbar_buttonLogNote').remove();
clearInterval(btn2);
}
}, 50);
}
if(result['hide_schedule_activity'] == false)
{
var btn3 = setInterval(function() {
if ($('.o_ChatterTopbar_buttonScheduleActivity').length) {
$('.o_ChatterTopbar_buttonScheduleActivity').remove();
clearInterval(btn3);
}
}, 50);
}
let model = hash.filter(ele=>ele.includes("model"))?.[0];
model = model? model.split("=")?.[1].split(",")?.[0]: model;
if(cids && model) {
rpc.query({
model:'access.management',
method: 'get_chatter_hide_details',
args: [cids, model]
}).then(function(result){
if(result['hide_send_mail'] == false)
{
var btn1 = setInterval(function() {
if ($('.o_ChatterTopbar_buttonSendMessage').length) {
$('.o_ChatterTopbar_buttonSendMessage').remove();
clearInterval(btn1);
}
}, 50);
}
if(result['hide_log_notes'] == false)
{
var btn2 = setInterval(function() {
if ($('.o_ChatterTopbar_buttonLogNote').length) {
$('.o_ChatterTopbar_buttonLogNote').remove();
clearInterval(btn2);
}
}, 50);
}
if(result['hide_schedule_activity'] == false)
{
var btn3 = setInterval(function() {
if ($('.o_ChatterTopbar_buttonScheduleActivity').length) {
$('.o_ChatterTopbar_buttonScheduleActivity').remove();
clearInterval(btn3);
}
}, 50);
}
});
});
}
return res;
},
});

View File

@ -2,7 +2,7 @@ odoo.define('simplify_access_management.hide_export', function (require) {
"use strict";
var ListRenderer = require('web.ListRenderer');
var session
var session = require("web.Session");
var rpc = require('web.rpc');
ListRenderer.include({
@ -10,30 +10,34 @@ odoo.define('simplify_access_management.hide_export', function (require) {
_render: function () {
const res = this._super.apply(this, arguments);
const self = this;
// this._super.apply(this, arguments);
var hash = window.location.hash.substring(1);
hash = JSON.parse('{"' + hash.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) { return key===""?value:decodeURIComponent(value) })
if(!hash.cids) {
const session = this.getSession();
hash.cids = String(session.company_id);
this._super.apply(this, arguments);
var hash = window.location.hash.replace("#", '').split("&");
let cids;
if(hash.findIndex(ele => ele.includes("cid")) == -1)
cids = session.company_id;
else {
cids = hash.filter(ele => ele.includes("cid"))[0].split("=")[1].split(",");
cids = cids.length > 0? parseInt(cids[0]): session.company_id;
}
let model = hash.filter(ele=>ele.includes("model"))?.[0];
model = model? model.split("=")?.[1].split(",")?.[0]: model;
if(cids && model) {
rpc.query({
model:'access.management',
method: 'is_export_hide',
args: [cids, model]
}).then(function(result){
if(result) {
var btn1 = setInterval(function() {
if ($('.o_list_export_xlsx').length) {
$('.o_list_export_xlsx').remove();
clearInterval(btn1);
}
}, 50);
}
});
}
rpc.query({
model:'access.management',
method: 'is_export_hide',
args: [parseInt(hash.cids.charAt(0)), hash.model]
}).then(function(result){
debugger
if(result) {
debugger
var btn1 = setInterval(function() {
if ($('.o_list_export_xlsx').length) {
$('.o_list_export_xlsx').remove();
clearInterval(btn1);
}
}, 50);
}
});
return res;
},

View File

@ -0,0 +1,24 @@
odoo.define(
"simpily_access_management.pivot_renderer_patch",
function (require) {
"use strict";
const { patch } = require("web.utils");
const PivotRenderer = require("web.PivotRenderer");
var rpc = require("web.rpc");
patch(PivotRenderer, "PivotRendererHideFieldPatch", {
async _updateTooltip() {
await this._super(...arguments);
const res = await rpc.query({
model: "access.management",
method: "get_hidden_field",
args: ["", this?.props?.modelName],
});
this.props.selectionGroupBys = this.props.selectionGroupBys.filter(
(ele) => !res.includes(ele.name)
);
},
});
}
);

View File

@ -49,7 +49,6 @@ odoo.define("advanced_web_domain_widget.DomainSelector", function (require) {
if (!this.readonly) {
// Set list of operators according to field type
this.displayValue = this.value;
debugger
this.operators = this._getOperatorsFromType(selectedField.type, selectedField.name);
if (_.contains(["child_of", "parent_of", "like", "not like", "=like", "=ilike"], this.operator)) {
// In case user entered manually or from demo data

View File

@ -4,7 +4,7 @@
<record id="access_management_form_view_ah" model="ir.ui.view">
<field name="name">access_management_form_view_ah</field>
<field name="groups_id" eval="[(4, ref('group_access_management_spt'))]"/>
<field name="groups_id" eval="[(4, ref('group_access_management_bits'))]"/>
<field name="model">access.management</field>
<field name="arch" type="xml">
<form>
@ -40,6 +40,7 @@
<group>
<field name="name" default_focus="1" required='1'/>
<field name="readonly"/>
<field name="is_apply_on_without_company"/>
<field name="company_ids" options="{'no_create': True}" widget="many2many_tags"
groups="base.group_multi_company"/>
<field name="self_model_ids" widget="many2many_tags" invisible="1"/>
@ -130,15 +131,17 @@
<!-- <field name="model_id" options="{'no_create': True}" domain="[('id','not in',parent.self_model_ids),('abstract','=',False)]" /> -->
<field name="model_id" context="{'is_access_rights': True}"
options="{'no_open': True, 'no_create': True, 'no_create_edit': True}"
required='1' domain="[('id','not in',parent.self_model_ids)]"/>
required='1' domain="[('id','not in',parent.self_model_ids)]"
width='10'/>
<field name="field_id" context="{'is_access_rights': True}"
options="{'no_open': True, 'no_create': True, 'no_create_edit': True}"
widget='many2many_tags' domain="[('model_id','=',model_id)]"/>
<field name="invisible" width='1'/>
<field name="readonly" width='1'
widget='many2many_tags' domain="[('model_id','=',model_id)]"
width='10'/>
<field name="invisible" width='10'/>
<field name="readonly" width='10'
attrs="{'column_invisible' : [('parent.readonly','=',True)]}"/>
<field name="required" width='1'/>
<field name="external_link" width='1'/>
<field name="required" width='10'/>
<field name="external_link" width='10'/>
</tree>
</field>
<p role="alert"
@ -189,12 +192,12 @@
</sheet>
</form>
<tree>
<field name="model_id"/>
<field name="domain"/>
<field name="read_right"/>
<field name="create_right"/>
<field name="write_right"/>
<field name="delete_right"/>
<field name="model_id" width='10'/>
<field name="domain" width='10'/>
<field name="read_right" width='10'/>
<field name="create_right" width='10'/>
<field name="write_right" width='10'/>
<field name="delete_right" width='10'/>
</tree>
</field>
<p role="alert"
@ -366,7 +369,7 @@
<record id="access_management_tree_view_ah" model="ir.ui.view">
<field name="name">access_management_tree_view_ah</field>
<field name="groups_id" eval="[(4, ref('group_access_management_spt'))]"/>
<field name="groups_id" eval="[(4, ref('group_access_management_bits'))]"/>
<field name="model">access.management</field>
<field name="arch" type="xml">
<tree decoration-muted="(not active)">
@ -389,8 +392,8 @@
</record>
<menuitem id="main_menu_simplify_access_management" name="Access Studio" action="action_access_management_ah"
groups="group_access_management_spt"
/>
groups="group_access_management_bits"
web_icon="simplify_access_management,static/description/icon.png"/>
</data>
</odoo>

View File

@ -5,12 +5,15 @@
<template id="assets_backend" name="simplify_access_management_view assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/simplify_access_management/static/src/js/action_items.js"></script>
<script type="text/javascript" src="/simplify_access_management/static/src/js/widget/DomainSelector.js"></script>
<script type="text/javascript" src="/simplify_access_management/static/src/js/widget/ModelRecordSelector.js"></script>
<!-- <script type="text/javascript" src="/simplify_access_management/static/src/js/widget/DomainSelector.js"></script>
<script type="text/javascript" src="/simplify_access_management/static/src/js/widget/ModelRecordSelector.js"></script> -->
<script type="text/javascript" src="/simplify_access_management/static/src/js/data_manager.js"></script>
<script type="text/javascript" src="/simplify_access_management/static/src/js/action_manager.js"></script>
<script type="text/javascript" src="/simplify_access_management/static/src/js/hide_chatter.js"></script>
<script type="text/javascript" src="/simplify_access_management/static/src/js/hide_export.js"></script>
<script type="text/javascript" src="/simplify_access_management/static/src/js/pivot_renderer_patch.js"></script>
<script type="text/javascript" src="/simplify_access_management/static/src/js/custom_filter_item.js"></script>
<script type="text/javascript" src="/simplify_access_management/static/src/js/groupby_menu.js"></script>
</xpath>
</template>
</data>

View File

@ -8,7 +8,7 @@
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page string='Access Management' groups="simplify_access_management.group_access_management_spt">
<page string='Access Management' groups="simplify_access_management.group_access_management_bits">
<field name='access_management_ids'>
<tree>
<field name="name" />