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