first commit
This commit is contained in:
commit
01f4ad9971
32
README.md
Normal file
32
README.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# User Access Rights Export
|
||||||
|
|
||||||
|
Generate an Excel workbook that consolidates the access rights of every user in your Odoo 17 instance.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* One-click wizard accessible to system administrators.
|
||||||
|
* Summary worksheet listing all users with counts of granted ACLs and record rules.
|
||||||
|
* Summary worksheet listing each security group with user counts, ACL totals, and record rule totals.
|
||||||
|
* Dedicated worksheet per user including:
|
||||||
|
* Model access control list (CRUD) permissions derived from `ir.model.access`.
|
||||||
|
* Record rule visibility and domain definitions from `ir.rule`.
|
||||||
|
* Dedicated worksheet per security group including:
|
||||||
|
* Group-specific ACL permissions.
|
||||||
|
* Record rules that apply to the group.
|
||||||
|
* Workbook generated entirely in-memory using `xlsxwriter`.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Copy the `user_access_rights_export` directory into your Odoo addons path.
|
||||||
|
2. Update your addons list and install the module via Apps.
|
||||||
|
3. Requires the Python package `xlsxwriter` (bundled with standard Odoo installations).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Navigate to **Settings → Technical → User Access Rights Export**.
|
||||||
|
2. Click **Generate**. The module will produce and download an `.xlsx` file.
|
||||||
|
3. Open the Excel file to inspect summary metrics and per-user details.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
Only members of the **Settings / Technical (System Administrator)** group can execute the export wizard.
|
||||||
2
__init__.py
Normal file
2
__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import wizard
|
||||||
34
__manifest__.py
Normal file
34
__manifest__.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
"name": "User Access Rights Export",
|
||||||
|
"version": "17.0.1.0.0",
|
||||||
|
"category": "Settings/Technical",
|
||||||
|
"summary": "Export detailed user access rights (model ACLs and record rules) to Excel",
|
||||||
|
"description": """
|
||||||
|
User Access Rights Export
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Generate an Excel workbook detailing the access rights of all internal users.
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
* Summary worksheet with key metrics per user.
|
||||||
|
* Dedicated worksheet per user including:
|
||||||
|
- Model access rights (CRUD permissions).
|
||||||
|
- Record rules with domains and permissions.
|
||||||
|
* XLSX output generated in-memory via xlsxwriter.
|
||||||
|
""",
|
||||||
|
"author": "Suherdy Yacob",
|
||||||
|
"website": "https://www.example.com",
|
||||||
|
"license": "LGPL-3",
|
||||||
|
"depends": [
|
||||||
|
"base",
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
"security/ir.model.access.csv",
|
||||||
|
"views/user_access_rights_wizard_views.xml",
|
||||||
|
],
|
||||||
|
"installable": True,
|
||||||
|
"application": False,
|
||||||
|
"auto_install": False,
|
||||||
|
}
|
||||||
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
BIN
__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
2
security/ir.model.access.csv
Normal file
2
security/ir.model.access.csv
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_user_access_rights_wizard,user.access.rights.wizard,model_user_access_rights_wizard,base.group_system,1,1,1,1
|
||||||
|
46
views/user_access_rights_wizard_views.xml
Normal file
46
views/user_access_rights_wizard_views.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_user_access_rights_wizard_form" model="ir.ui.view">
|
||||||
|
<field name="name">user.access.rights.wizard.form</field>
|
||||||
|
<field name="model">user.access.rights.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Export User Access Rights">
|
||||||
|
<sheet>
|
||||||
|
<group col="1" class="o_form_full_width">
|
||||||
|
<separator string="Overview" colspan="1"/>
|
||||||
|
<div class="o_form_description">
|
||||||
|
<p><strong>Generate a consolidated Excel workbook of user access rights.</strong></p>
|
||||||
|
<ul class="o_form_list">
|
||||||
|
<li>Includes model ACL permissions (Read, Write, Create, Delete) per user.</li>
|
||||||
|
<li>Captures applicable record rules and their domains.</li>
|
||||||
|
<li>Download starts automatically once the report is ready.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
<group col="2" modifiers="{'invisible': [('excel_file', '=', False)]}">
|
||||||
|
<field name="filename" readonly="1" string="Generated File"/>
|
||||||
|
<field name="excel_file" filename="filename" invisible="1"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
<footer>
|
||||||
|
<button string="Generate" type="object" name="action_generate_report" class="btn-primary"/>
|
||||||
|
<button string="Close" special="cancel" class="btn-secondary"/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_user_access_rights_wizard" model="ir.actions.act_window">
|
||||||
|
<field name="name">Export User Access Rights</field>
|
||||||
|
<field name="res_model">user.access.rights.wizard</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_user_access_rights_export_root"
|
||||||
|
name="User Access Rights Export"
|
||||||
|
parent="base.menu_custom"
|
||||||
|
sequence="50"
|
||||||
|
action="action_user_access_rights_wizard"
|
||||||
|
groups="base.group_system"/>
|
||||||
|
</odoo>
|
||||||
2
wizard/__init__.py
Normal file
2
wizard/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import user_access_rights_wizard
|
||||||
BIN
wizard/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
wizard/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
wizard/__pycache__/user_access_rights_wizard.cpython-312.pyc
Normal file
BIN
wizard/__pycache__/user_access_rights_wizard.cpython-312.pyc
Normal file
Binary file not shown.
583
wizard/user_access_rights_wizard.py
Normal file
583
wizard/user_access_rights_wizard.py
Normal file
@ -0,0 +1,583 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import base64
|
||||||
|
import io
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from odoo import _, api, fields, models
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
from odoo.tools.misc import xlsxwriter
|
||||||
|
|
||||||
|
|
||||||
|
class UserAccessRightsWizard(models.TransientModel):
|
||||||
|
_name = "user.access.rights.wizard"
|
||||||
|
_description = "User Access Rights Export Wizard"
|
||||||
|
|
||||||
|
excel_file = fields.Binary(string="Excel File", readonly=True)
|
||||||
|
filename = fields.Char(string="File Name", readonly=True)
|
||||||
|
|
||||||
|
def action_generate_report(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if not xlsxwriter:
|
||||||
|
raise UserError(_("The python library xlsxwriter is required to export Excel files."))
|
||||||
|
|
||||||
|
users = self._get_users()
|
||||||
|
user_data = []
|
||||||
|
user_summary_rows = []
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
group_names = ", ".join(user.groups_id.mapped("display_name")) or _("No Groups")
|
||||||
|
model_access = self._collect_model_access(user)
|
||||||
|
record_rules = self._collect_record_rules(user)
|
||||||
|
|
||||||
|
user_summary_rows.append({
|
||||||
|
"name": user.display_name,
|
||||||
|
"login": user.login or "",
|
||||||
|
"groups": group_names,
|
||||||
|
"active": self._bool_to_str(user.active),
|
||||||
|
"model_count": len(model_access),
|
||||||
|
"rule_count": len(record_rules),
|
||||||
|
})
|
||||||
|
|
||||||
|
user_data.append({
|
||||||
|
"user": user,
|
||||||
|
"groups": group_names,
|
||||||
|
"model_access": model_access,
|
||||||
|
"record_rules": record_rules,
|
||||||
|
})
|
||||||
|
|
||||||
|
groups = self._get_groups()
|
||||||
|
group_data = []
|
||||||
|
group_summary_rows = []
|
||||||
|
|
||||||
|
for group in groups:
|
||||||
|
model_access = self._collect_group_model_access(group)
|
||||||
|
record_rules = self._collect_group_record_rules(group)
|
||||||
|
users_in_group = group.users
|
||||||
|
|
||||||
|
group_summary_rows.append({
|
||||||
|
"name": group.display_name,
|
||||||
|
"technical_name": group.full_name,
|
||||||
|
"category": group.category_id.display_name or _("Uncategorized"),
|
||||||
|
"user_count": len(users_in_group),
|
||||||
|
"model_count": len(model_access),
|
||||||
|
"rule_count": len(record_rules),
|
||||||
|
})
|
||||||
|
|
||||||
|
group_data.append({
|
||||||
|
"group": group,
|
||||||
|
"users": users_in_group,
|
||||||
|
"model_access": model_access,
|
||||||
|
"record_rules": record_rules,
|
||||||
|
})
|
||||||
|
|
||||||
|
output = io.BytesIO()
|
||||||
|
workbook = xlsxwriter.Workbook(output, {"in_memory": True})
|
||||||
|
formats = self._build_formats(workbook)
|
||||||
|
used_sheet_names = set()
|
||||||
|
|
||||||
|
try:
|
||||||
|
user_overview_sheet = self._make_unique_sheet_name(_("Users Overview"), used_sheet_names)
|
||||||
|
used_sheet_names.add(user_overview_sheet)
|
||||||
|
self._write_user_summary_sheet(workbook, formats, user_overview_sheet, user_summary_rows)
|
||||||
|
|
||||||
|
if group_summary_rows:
|
||||||
|
group_overview_sheet = self._make_unique_sheet_name(_("Groups Overview"), used_sheet_names)
|
||||||
|
used_sheet_names.add(group_overview_sheet)
|
||||||
|
self._write_group_summary_sheet(workbook, formats, group_overview_sheet, group_summary_rows)
|
||||||
|
|
||||||
|
for data in user_data:
|
||||||
|
sheet_name = self._make_unique_sheet_name(
|
||||||
|
data["user"].display_name or _("User"),
|
||||||
|
used_sheet_names,
|
||||||
|
)
|
||||||
|
used_sheet_names.add(sheet_name)
|
||||||
|
self._write_user_sheet(workbook, formats, sheet_name, data)
|
||||||
|
|
||||||
|
for data in group_data:
|
||||||
|
sheet_name = self._make_unique_sheet_name(
|
||||||
|
data["group"].display_name or _("Group"),
|
||||||
|
used_sheet_names,
|
||||||
|
)
|
||||||
|
used_sheet_names.add(sheet_name)
|
||||||
|
self._write_group_sheet(workbook, formats, sheet_name, data)
|
||||||
|
finally:
|
||||||
|
workbook.close()
|
||||||
|
|
||||||
|
file_content = output.getvalue()
|
||||||
|
filename = "user_access_rights_%s.xlsx" % datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
|
||||||
|
self.write({
|
||||||
|
"excel_file": base64.b64encode(file_content),
|
||||||
|
"filename": filename,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
"type": "ir.actions.act_url",
|
||||||
|
"url": "/web/content/?model=%s&id=%s&field=excel_file&filename_field=filename&download=true"
|
||||||
|
% (self._name, self.id),
|
||||||
|
"target": "self",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_users(self):
|
||||||
|
return self.env["res.users"].sudo().with_context(active_test=False).search([], order="name")
|
||||||
|
|
||||||
|
def _get_groups(self):
|
||||||
|
return self.env["res.groups"].sudo().with_context(active_test=False).search([], order="category_id, name")
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _collect_model_access(self, user):
|
||||||
|
user_groups = user.groups_id
|
||||||
|
acl_model = self.env["ir.model.access"].sudo().with_context(active_test=False)
|
||||||
|
acl_records = acl_model.search([], order="model_id, id")
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for acl in acl_records:
|
||||||
|
applies = not acl.group_id or acl.group_id in user_groups
|
||||||
|
if not applies:
|
||||||
|
continue
|
||||||
|
|
||||||
|
result.append({
|
||||||
|
"model": acl.model_id.model,
|
||||||
|
"model_name": acl.model_id.name,
|
||||||
|
"group": acl.group_id.display_name if acl.group_id else _("All Users"),
|
||||||
|
"perm_read": self._bool_to_str(acl.perm_read),
|
||||||
|
"perm_write": self._bool_to_str(acl.perm_write),
|
||||||
|
"perm_create": self._bool_to_str(acl.perm_create),
|
||||||
|
"perm_unlink": self._bool_to_str(acl.perm_unlink),
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _collect_group_model_access(self, group):
|
||||||
|
acl_model = self.env["ir.model.access"].sudo().with_context(active_test=False)
|
||||||
|
acl_records = acl_model.search([("group_id", "=", group.id)], order="model_id, id")
|
||||||
|
result = []
|
||||||
|
for acl in acl_records:
|
||||||
|
result.append({
|
||||||
|
"model": acl.model_id.model,
|
||||||
|
"model_name": acl.model_id.name,
|
||||||
|
"perm_read": self._bool_to_str(acl.perm_read),
|
||||||
|
"perm_write": self._bool_to_str(acl.perm_write),
|
||||||
|
"perm_create": self._bool_to_str(acl.perm_create),
|
||||||
|
"perm_unlink": self._bool_to_str(acl.perm_unlink),
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _collect_record_rules(self, user):
|
||||||
|
user_groups = user.groups_id
|
||||||
|
rule_model = self.env["ir.rule"].sudo().with_context(active_test=False)
|
||||||
|
rules = rule_model.search([], order="model_id, id")
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for rule in rules:
|
||||||
|
is_global = bool(getattr(rule, "global", False))
|
||||||
|
applies = is_global or (rule.groups and any(g in user_groups for g in rule.groups))
|
||||||
|
if not applies:
|
||||||
|
continue
|
||||||
|
|
||||||
|
group_names = ", ".join(rule.groups.mapped("display_name")) if rule.groups else _("All Users")
|
||||||
|
domain = rule.domain_force or "[]"
|
||||||
|
domain = re.sub(r"\s+", " ", domain).strip()
|
||||||
|
|
||||||
|
result.append({
|
||||||
|
"name": rule.name or _("Unnamed Rule"),
|
||||||
|
"model": rule.model_id.model,
|
||||||
|
"model_name": rule.model_id.name,
|
||||||
|
"domain": domain,
|
||||||
|
"group": group_names,
|
||||||
|
"global": self._bool_to_str(is_global),
|
||||||
|
"perm_read": self._bool_to_str(rule.perm_read),
|
||||||
|
"perm_write": self._bool_to_str(rule.perm_write),
|
||||||
|
"perm_create": self._bool_to_str(rule.perm_create),
|
||||||
|
"perm_unlink": self._bool_to_str(rule.perm_unlink),
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _collect_group_record_rules(self, group):
|
||||||
|
rule_model = self.env["ir.rule"].sudo().with_context(active_test=False)
|
||||||
|
rules = rule_model.search([], order="model_id, id")
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for rule in rules:
|
||||||
|
if group not in rule.groups:
|
||||||
|
continue
|
||||||
|
|
||||||
|
domain = rule.domain_force or "[]"
|
||||||
|
domain = re.sub(r"\s+", " ", domain).strip()
|
||||||
|
|
||||||
|
result.append({
|
||||||
|
"name": rule.name or _("Unnamed Rule"),
|
||||||
|
"model": rule.model_id.model,
|
||||||
|
"model_name": rule.model_id.name,
|
||||||
|
"domain": domain,
|
||||||
|
"perm_read": self._bool_to_str(rule.perm_read),
|
||||||
|
"perm_write": self._bool_to_str(rule.perm_write),
|
||||||
|
"perm_create": self._bool_to_str(rule.perm_create),
|
||||||
|
"perm_unlink": self._bool_to_str(rule.perm_unlink),
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _build_formats(self, workbook):
|
||||||
|
return {
|
||||||
|
"header": workbook.add_format({
|
||||||
|
"bold": True,
|
||||||
|
"bg_color": "#F2F2F2",
|
||||||
|
"border": 1,
|
||||||
|
"text_wrap": True,
|
||||||
|
}),
|
||||||
|
"section": workbook.add_format({
|
||||||
|
"bold": True,
|
||||||
|
"font_size": 12,
|
||||||
|
"bottom": 1,
|
||||||
|
}),
|
||||||
|
"text": workbook.add_format({
|
||||||
|
"border": 1,
|
||||||
|
}),
|
||||||
|
"wrap": workbook.add_format({
|
||||||
|
"border": 1,
|
||||||
|
"text_wrap": True,
|
||||||
|
}),
|
||||||
|
"center": workbook.add_format({
|
||||||
|
"border": 1,
|
||||||
|
"align": "center",
|
||||||
|
}),
|
||||||
|
"title": workbook.add_format({
|
||||||
|
"bold": True,
|
||||||
|
"font_size": 14,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _write_user_summary_sheet(self, workbook, formats, sheet_name, rows):
|
||||||
|
worksheet = workbook.add_worksheet(sheet_name)
|
||||||
|
headers = [
|
||||||
|
_("User"),
|
||||||
|
_("Login"),
|
||||||
|
_("Groups"),
|
||||||
|
_("Active"),
|
||||||
|
_("Model ACLs"),
|
||||||
|
_("Record Rules"),
|
||||||
|
]
|
||||||
|
column_widths = [len(header) + 2 for header in headers]
|
||||||
|
|
||||||
|
worksheet.freeze_panes(1, 0)
|
||||||
|
|
||||||
|
for col, header in enumerate(headers):
|
||||||
|
worksheet.write(0, col, header, formats["header"])
|
||||||
|
|
||||||
|
for row_idx, data in enumerate(rows, start=1):
|
||||||
|
values = [
|
||||||
|
data["name"],
|
||||||
|
data["login"],
|
||||||
|
data["groups"],
|
||||||
|
data["active"],
|
||||||
|
data["model_count"],
|
||||||
|
data["rule_count"],
|
||||||
|
]
|
||||||
|
for col_idx, value in enumerate(values):
|
||||||
|
fmt = formats["wrap"] if col_idx == 2 else formats["text"]
|
||||||
|
worksheet.write(row_idx, col_idx, value, fmt)
|
||||||
|
column_widths[col_idx] = min(
|
||||||
|
max(column_widths[col_idx], len(str(value)) + 2),
|
||||||
|
80,
|
||||||
|
)
|
||||||
|
|
||||||
|
for col_idx, width in enumerate(column_widths):
|
||||||
|
worksheet.set_column(col_idx, col_idx, width)
|
||||||
|
|
||||||
|
def _write_group_summary_sheet(self, workbook, formats, sheet_name, rows):
|
||||||
|
worksheet = workbook.add_worksheet(sheet_name)
|
||||||
|
headers = [
|
||||||
|
_("Group"),
|
||||||
|
_("Technical Name"),
|
||||||
|
_("Category"),
|
||||||
|
_("Users"),
|
||||||
|
_("Model ACLs"),
|
||||||
|
_("Record Rules"),
|
||||||
|
]
|
||||||
|
column_widths = [len(header) + 2 for header in headers]
|
||||||
|
|
||||||
|
worksheet.freeze_panes(1, 0)
|
||||||
|
|
||||||
|
for col, header in enumerate(headers):
|
||||||
|
worksheet.write(0, col, header, formats["header"])
|
||||||
|
|
||||||
|
for row_idx, data in enumerate(rows, start=1):
|
||||||
|
values = [
|
||||||
|
data["name"],
|
||||||
|
data["technical_name"],
|
||||||
|
data["category"],
|
||||||
|
data["user_count"],
|
||||||
|
data["model_count"],
|
||||||
|
data["rule_count"],
|
||||||
|
]
|
||||||
|
for col_idx, value in enumerate(values):
|
||||||
|
fmt = formats["text"]
|
||||||
|
worksheet.write(row_idx, col_idx, value, fmt)
|
||||||
|
column_widths[col_idx] = min(
|
||||||
|
max(column_widths[col_idx], len(str(value)) + 2),
|
||||||
|
80,
|
||||||
|
)
|
||||||
|
|
||||||
|
for col_idx, width in enumerate(column_widths):
|
||||||
|
worksheet.set_column(col_idx, col_idx, width)
|
||||||
|
|
||||||
|
def _write_user_sheet(self, workbook, formats, sheet_name, data):
|
||||||
|
worksheet = workbook.add_worksheet(sheet_name)
|
||||||
|
row = 0
|
||||||
|
|
||||||
|
user = data["user"]
|
||||||
|
worksheet.write(row, 0, _("User Access Rights: %s") % (user.display_name,), formats["title"])
|
||||||
|
row += 2
|
||||||
|
|
||||||
|
info_pairs = [
|
||||||
|
(_("Name"), user.display_name or ""),
|
||||||
|
(_("Login"), user.login or ""),
|
||||||
|
(_("Email"), user.email or ""),
|
||||||
|
(_("Active"), self._bool_to_str(user.active)),
|
||||||
|
(_("Groups"), data["groups"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
column_widths = [0, 0]
|
||||||
|
for label, value in info_pairs:
|
||||||
|
worksheet.write(row, 0, label, formats["header"])
|
||||||
|
worksheet.write(row, 1, value, formats["wrap"])
|
||||||
|
column_widths[0] = min(max(column_widths[0], len(label) + 2), 40)
|
||||||
|
column_widths[1] = min(max(column_widths[1], len(value) + 2), 80)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
worksheet.set_column(0, 0, column_widths[0] or 18)
|
||||||
|
worksheet.set_column(1, 1, column_widths[1] or 50)
|
||||||
|
|
||||||
|
row += 1
|
||||||
|
worksheet.write(row, 0, _("Model Access Rights"), formats["section"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
headers = [
|
||||||
|
_("Model Technical Name"),
|
||||||
|
_("Model"),
|
||||||
|
_("Applies To Group"),
|
||||||
|
_("Read"),
|
||||||
|
_("Write"),
|
||||||
|
_("Create"),
|
||||||
|
_("Delete"),
|
||||||
|
]
|
||||||
|
for col, header in enumerate(headers):
|
||||||
|
worksheet.write(row, col, header, formats["header"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
model_column_widths = [len(header) + 2 for header in headers]
|
||||||
|
for record in data["model_access"]:
|
||||||
|
values = [
|
||||||
|
record["model"],
|
||||||
|
record["model_name"],
|
||||||
|
record["group"],
|
||||||
|
record["perm_read"],
|
||||||
|
record["perm_write"],
|
||||||
|
record["perm_create"],
|
||||||
|
record["perm_unlink"],
|
||||||
|
]
|
||||||
|
for col_idx, value in enumerate(values):
|
||||||
|
fmt = formats["wrap"] if col_idx in (1, 2) else formats["center"] if col_idx >= 3 else formats["text"]
|
||||||
|
worksheet.write(row, col_idx, value, fmt)
|
||||||
|
model_column_widths[col_idx] = min(
|
||||||
|
max(model_column_widths[col_idx], len(str(value)) + 2),
|
||||||
|
70,
|
||||||
|
)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
for col_idx, width in enumerate(model_column_widths):
|
||||||
|
worksheet.set_column(col_idx, col_idx, width)
|
||||||
|
|
||||||
|
row += 1
|
||||||
|
worksheet.write(row, 0, _("Record Rules"), formats["section"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
rule_headers = [
|
||||||
|
_("Rule Name"),
|
||||||
|
_("Model Technical Name"),
|
||||||
|
_("Model"),
|
||||||
|
_("Domain"),
|
||||||
|
_("Applies To Group"),
|
||||||
|
_("Global"),
|
||||||
|
_("Read"),
|
||||||
|
_("Write"),
|
||||||
|
_("Create"),
|
||||||
|
_("Delete"),
|
||||||
|
]
|
||||||
|
for col, header in enumerate(rule_headers):
|
||||||
|
worksheet.write(row, col, header, formats["header"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
rule_column_widths = [len(header) + 2 for header in rule_headers]
|
||||||
|
for record in data["record_rules"]:
|
||||||
|
values = [
|
||||||
|
record["name"],
|
||||||
|
record["model"],
|
||||||
|
record["model_name"],
|
||||||
|
record["domain"],
|
||||||
|
record["group"],
|
||||||
|
record["global"],
|
||||||
|
record["perm_read"],
|
||||||
|
record["perm_write"],
|
||||||
|
record["perm_create"],
|
||||||
|
record["perm_unlink"],
|
||||||
|
]
|
||||||
|
for col_idx, value in enumerate(values):
|
||||||
|
if col_idx in (3, 4):
|
||||||
|
fmt = formats["wrap"]
|
||||||
|
elif col_idx >= 5:
|
||||||
|
fmt = formats["center"]
|
||||||
|
else:
|
||||||
|
fmt = formats["text"]
|
||||||
|
worksheet.write(row, col_idx, value, fmt)
|
||||||
|
rule_column_widths[col_idx] = min(
|
||||||
|
max(rule_column_widths[col_idx], len(str(value)) + 2),
|
||||||
|
90 if col_idx == 3 else 70,
|
||||||
|
)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
for col_idx, width in enumerate(rule_column_widths):
|
||||||
|
worksheet.set_column(col_idx, col_idx, width)
|
||||||
|
|
||||||
|
worksheet.freeze_panes(4 + len(data["model_access"]), 0)
|
||||||
|
|
||||||
|
def _write_group_sheet(self, workbook, formats, sheet_name, data):
|
||||||
|
worksheet = workbook.add_worksheet(sheet_name)
|
||||||
|
row = 0
|
||||||
|
|
||||||
|
group = data["group"]
|
||||||
|
worksheet.write(row, 0, _("Group Access Rights: %s") % (group.display_name,), formats["title"])
|
||||||
|
row += 2
|
||||||
|
|
||||||
|
user_names = ", ".join(data["users"].mapped("display_name")) or _("No Users")
|
||||||
|
|
||||||
|
info_pairs = [
|
||||||
|
(_("Name"), group.display_name or ""),
|
||||||
|
(_("Technical Name"), group.full_name or ""),
|
||||||
|
(_("Category"), group.category_id.display_name or _("Uncategorized")),
|
||||||
|
(_("Users"), user_names),
|
||||||
|
]
|
||||||
|
|
||||||
|
column_widths = [0, 0]
|
||||||
|
for label, value in info_pairs:
|
||||||
|
worksheet.write(row, 0, label, formats["header"])
|
||||||
|
worksheet.write(row, 1, value, formats["wrap"])
|
||||||
|
column_widths[0] = min(max(column_widths[0], len(label) + 2), 40)
|
||||||
|
column_widths[1] = min(max(column_widths[1], len(value) + 2), 80)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
worksheet.set_column(0, 0, column_widths[0] or 18)
|
||||||
|
worksheet.set_column(1, 1, column_widths[1] or 50)
|
||||||
|
|
||||||
|
row += 1
|
||||||
|
worksheet.write(row, 0, _("Model Access Rights"), formats["section"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
headers = [
|
||||||
|
_("Model Technical Name"),
|
||||||
|
_("Model"),
|
||||||
|
_("Read"),
|
||||||
|
_("Write"),
|
||||||
|
_("Create"),
|
||||||
|
_("Delete"),
|
||||||
|
]
|
||||||
|
for col, header in enumerate(headers):
|
||||||
|
worksheet.write(row, col, header, formats["header"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
model_column_widths = [len(header) + 2 for header in headers]
|
||||||
|
for record in data["model_access"]:
|
||||||
|
values = [
|
||||||
|
record["model"],
|
||||||
|
record["model_name"],
|
||||||
|
record["perm_read"],
|
||||||
|
record["perm_write"],
|
||||||
|
record["perm_create"],
|
||||||
|
record["perm_unlink"],
|
||||||
|
]
|
||||||
|
for col_idx, value in enumerate(values):
|
||||||
|
fmt = formats["wrap"] if col_idx == 1 else formats["center"] if col_idx >= 2 else formats["text"]
|
||||||
|
worksheet.write(row, col_idx, value, fmt)
|
||||||
|
model_column_widths[col_idx] = min(
|
||||||
|
max(model_column_widths[col_idx], len(str(value)) + 2),
|
||||||
|
70,
|
||||||
|
)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
for col_idx, width in enumerate(model_column_widths):
|
||||||
|
worksheet.set_column(col_idx, col_idx, width)
|
||||||
|
|
||||||
|
row += 1
|
||||||
|
worksheet.write(row, 0, _("Record Rules"), formats["section"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
rule_headers = [
|
||||||
|
_("Rule Name"),
|
||||||
|
_("Model Technical Name"),
|
||||||
|
_("Model"),
|
||||||
|
_("Domain"),
|
||||||
|
_("Read"),
|
||||||
|
_("Write"),
|
||||||
|
_("Create"),
|
||||||
|
_("Delete"),
|
||||||
|
]
|
||||||
|
for col, header in enumerate(rule_headers):
|
||||||
|
worksheet.write(row, col, header, formats["header"])
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
rule_column_widths = [len(header) + 2 for header in rule_headers]
|
||||||
|
for record in data["record_rules"]:
|
||||||
|
values = [
|
||||||
|
record["name"],
|
||||||
|
record["model"],
|
||||||
|
record["model_name"],
|
||||||
|
record["domain"],
|
||||||
|
record["perm_read"],
|
||||||
|
record["perm_write"],
|
||||||
|
record["perm_create"],
|
||||||
|
record["perm_unlink"],
|
||||||
|
]
|
||||||
|
for col_idx, value in enumerate(values):
|
||||||
|
if col_idx == 3:
|
||||||
|
fmt = formats["wrap"]
|
||||||
|
elif col_idx >= 4:
|
||||||
|
fmt = formats["center"]
|
||||||
|
else:
|
||||||
|
fmt = formats["text"]
|
||||||
|
worksheet.write(row, col_idx, value, fmt)
|
||||||
|
rule_column_widths[col_idx] = min(
|
||||||
|
max(rule_column_widths[col_idx], len(str(value)) + 2),
|
||||||
|
90 if col_idx == 3 else 70,
|
||||||
|
)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
for col_idx, width in enumerate(rule_column_widths):
|
||||||
|
worksheet.set_column(col_idx, col_idx, width)
|
||||||
|
|
||||||
|
worksheet.freeze_panes(4 + len(data["model_access"]), 0)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _make_unique_sheet_name(self, base_name, used_names):
|
||||||
|
sanitized = re.sub(r"[\[\]\*\?:\\/]", "", base_name or _("Sheet"))
|
||||||
|
sanitized = sanitized.strip() or _("Sheet")
|
||||||
|
sanitized = sanitized[:31]
|
||||||
|
candidate = sanitized
|
||||||
|
index = 2
|
||||||
|
|
||||||
|
while candidate in used_names:
|
||||||
|
suffix = f" ({index})"
|
||||||
|
candidate = (sanitized[:31 - len(suffix)] + suffix) if len(sanitized) + len(suffix) > 31 else sanitized + suffix
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
return candidate
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _bool_to_str(self, value):
|
||||||
|
return _("Yes") if value else _("No")
|
||||||
Loading…
Reference in New Issue
Block a user