diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 4b4edc6..6da85e0 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1,2 +1 @@ reporting-engine - diff --git a/privacy_partner_report/__manifest__.py b/privacy_partner_report/__manifest__.py index 0d4d092..a3d98fe 100644 --- a/privacy_partner_report/__manifest__.py +++ b/privacy_partner_report/__manifest__.py @@ -1,20 +1,19 @@ # Copyright 2018 Eficent Business and IT Consulting Services S.L. # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html { - 'name': 'Privacy Partner Report', - 'version': '13.0.1.0.0', - 'category': 'GDPR', - 'summary': 'Show the transactions that a specific partner is involved in.', - 'author': "Eficent, " - "Odoo Community Association (OCA)", - 'website': 'https://github.com/OCA/data-protection/', - 'license': 'AGPL-3', - 'depends': ['privacy', 'report_xlsx'], - 'data': [ - 'wizard/privacy_report_partner_wizard.xml', - 'views/privacy_report.xml', - 'views/privacy_menu_view.xml', + "name": "Privacy Partner Report", + "version": "13.0.1.0.0", + "category": "GDPR", + "summary": "Show the transactions that a specific partner is involved in.", + "author": "Eficent, " "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/data-protection/", + "license": "AGPL-3", + "depends": ["privacy", "report_xlsx"], + "data": [ + "wizard/privacy_report_partner_wizard.xml", + "views/privacy_report.xml", + "views/privacy_menu_view.xml", ], - 'installable': True, - 'maintainers': ['mreficent'], + "installable": True, + "maintainers": ["mreficent"], } diff --git a/privacy_partner_report/report/privacy_partner_xlsx.py b/privacy_partner_report/report/privacy_partner_xlsx.py index 48bdc8b..f7e788d 100644 --- a/privacy_partner_report/report/privacy_partner_xlsx.py +++ b/privacy_partner_report/report/privacy_partner_xlsx.py @@ -2,6 +2,7 @@ # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html import logging + from odoo import models from odoo.tools.translate import _ @@ -9,8 +10,8 @@ _logger = logging.getLogger(__name__) class ReportPartnerXlsx(models.AbstractModel): - _name = 'report.privacy_partner_report.report_partner_xlsx' - _inherit = 'report.report_xlsx.abstract' + _name = "report.privacy_partner_report.report_partner_xlsx" + _inherit = "report.report_xlsx.abstract" def _search_longest_row(self, tables): res = 0 @@ -24,26 +25,26 @@ class ReportPartnerXlsx(models.AbstractModel): def generate_xlsx_report(self, workbook, data, objects): for o in objects: report_data = o.compute_data_for_report(data) - partner = report_data['form'].get('partner_id', False) - partner = self.env['res.partner'].sudo().browse(partner[0]) - workbook.set_properties({ - 'comments': 'Created with Python and XlsxWriter from Odoo'}) - sheet = workbook.add_worksheet(_('Partner Data')) + partner = report_data["form"].get("partner_id", False) + partner = self.env["res.partner"].sudo().browse(partner[0]) + workbook.set_properties( + {"comments": "Created with Python and XlsxWriter from Odoo"} + ) + sheet = workbook.add_worksheet(_("Partner Data")) sheet.set_landscape() sheet.fit_to_pages(1, 0) sheet.set_zoom(75) - sheet.set_column(0, self._search_longest_row( - report_data['tables']), 25) + sheet.set_column(0, self._search_longest_row(report_data["tables"]), 25) title_style = workbook.add_format( - {'bold': True, 'bg_color': '#FFFFCC', 'border': 2}) - sheet.set_row(0, None, None, {'collapsed': 1}) - sheet.write_row(1, 0, ["Partner: " + partner.display_name], - title_style) + {"bold": True, "bg_color": "#FFFFCC", "border": 2} + ) + sheet.set_row(0, None, None, {"collapsed": 1}) + sheet.write_row(1, 0, ["Partner: " + partner.display_name], title_style) i = 3 - first_row = i+2 - for table in sorted(report_data['tables'].keys()): - for model in sorted(report_data['tables'][table].keys()): - rows = len(report_data['tables'][table][model]) + first_row = i + 2 + for table in sorted(report_data["tables"].keys()): + for model in sorted(report_data["tables"][table].keys()): + rows = len(report_data["tables"][table][model]) if rows: style = workbook.add_format() style.set_bold(True) @@ -51,19 +52,18 @@ class ReportPartnerXlsx(models.AbstractModel): sheet.write_row(i, 0, [model], style) i += 1 j = 0 - for column in report_data['tables'][table][model][0]: + for column in report_data["tables"][table][model][0]: style = workbook.add_format() style.set_bold(True) if j == 0: style.set_left(1) - if j == len(report_data['tables'][ - table][model][0]) - 1: + if j == len(report_data["tables"][table][model][0]) - 1: style.set_right(1) style.set_top(1) style.set_bottom(1) sheet.write_row(i, j, [column], style) j += 1 - for row in report_data['tables'][table][model]: + for row in report_data["tables"][table][model]: i += 1 j = 0 for column in row: @@ -77,7 +77,7 @@ class ReportPartnerXlsx(models.AbstractModel): if row[column]: sheet.write_row(i, j, [row[column]], style) else: - sheet.write_row(i, j, [''], style) + sheet.write_row(i, j, [""], style) j += 1 i += 2 - first_row = i+2 + first_row = i + 2 diff --git a/privacy_partner_report/views/privacy_menu_view.xml b/privacy_partner_report/views/privacy_menu_view.xml index 7f22a67..780adf8 100644 --- a/privacy_partner_report/views/privacy_menu_view.xml +++ b/privacy_partner_report/views/privacy_menu_view.xml @@ -1,13 +1,12 @@ - - - diff --git a/privacy_partner_report/wizard/privacy_report_partner.py b/privacy_partner_report/wizard/privacy_report_partner.py index df0d76b..534c092 100644 --- a/privacy_partner_report/wizard/privacy_report_partner.py +++ b/privacy_partner_report/wizard/privacy_report_partner.py @@ -1,77 +1,70 @@ # Copyright 2018 Eficent Business and IT Consulting Services S.L. # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from odoo import api, fields, models, _ -from odoo.exceptions import UserError import ast +from odoo import _, api, fields, models +from odoo.exceptions import UserError + class PrivacyPartnerReport(models.TransientModel): _name = "privacy.partner.report" _description = "Privacy Partner Report" company_id = fields.Many2one( - comodel_name='res.company', - string='Company', + comodel_name="res.company", + string="Company", required=True, default=lambda self: self.env.user.company_id, ) partner_id = fields.Many2one( - comodel_name='res.partner', - string='Partner', - required=True, + comodel_name="res.partner", string="Partner", required=True, ) table_ids = fields.Many2many( - comodel_name='privacy.partner.data', - string='Models with related partner data', + comodel_name="privacy.partner.data", string="Models with related partner data", ) - @api.onchange('partner_id') + @api.onchange("partner_id") def _onchange_partner_id(self): if self.partner_id: data = self._get_tables_from_partner(self.partner_id) names = self._get_table_names(data) - tables = self.env['privacy.partner.data'] + tables = self.env["privacy.partner.data"] for name in sorted(names): vals = self._get_default_table( - name=name, - data=[t for t in data if t[0] == name and not t[5]], + name=name, data=[t for t in data if t[0] == name and not t[5]], ) if vals: - tables |= self.env['privacy.partner.data'].create(vals) + tables |= self.env["privacy.partner.data"].create(vals) self.table_ids = tables else: - self.table_ids = self.env['privacy.partner.data'] + self.table_ids = self.env["privacy.partner.data"] return { - 'domain': { - 'table_ids': [ - ('id', 'in', self.table_ids.ids)], - }, + "domain": {"table_ids": [("id", "in", self.table_ids.ids)]}, } - @api.onchange('company_id') + @api.onchange("company_id") def _onchange_company_id(self): if not self.company_id: self.company_id = self.env.user.company_id return { - 'domain': { - 'partner_id': [ - ('company_id', 'in', [self.company_id.id, False])], + "domain": { + "partner_id": [("company_id", "in", [self.company_id.id, False])], }, } - def button_export_xlsx(self): self.ensure_one() if not self.table_ids: - raise UserError(_('No data for this partner.')) + raise UserError(_("No data for this partner.")) return self.check_report(xlsx_report=True) def _build_contexts(self, data): result = {} - result['partner_id'] = data['form']['partner_id'][0] or False - result['company_id'] = data['form']['company_id'][0] or False - result['table_ids'] = 'table_ids' in data['form'] and \ - data['form']['table_ids'] or False + result["partner_id"] = data["form"]["partner_id"][0] or False + result["company_id"] = data["form"]["company_id"][0] or False + result["table_ids"] = ( + "table_ids" in data["form"] and data["form"]["table_ids"] or False + ) return result @staticmethod @@ -86,54 +79,55 @@ class PrivacyPartnerReport(models.TransientModel): for key, value in row.items(): label = self.env[model]._fields[key].string or key if self.env[model]._fields[key].store: - if 'many2one' == self.env[model]._fields[key].type: + if "many2one" == self.env[model]._fields[key].type: comodel = self.env[model]._fields[key].comodel_name if value: record = self.env[comodel].sudo().browse(value) cleaned_rows[i][label] = record.display_name else: cleaned_rows[i][label] = rows[i][key] - elif 'binary' == self.env[model]._fields[key].type: + elif "binary" == self.env[model]._fields[key].type: binary = self._transform_binary(rows[i][key]) if binary: cleaned_rows[i][label] = binary - elif '2many' not in self.env[model]._fields[key].type: + elif "2many" not in self.env[model]._fields[key].type: cleaned_rows[i][label] = rows[i][key] return cleaned_rows def check_report(self, xlsx_report=False): self.ensure_one() data = {} - data['ids'] = self.env.context.get('active_ids', []) - data['model'] = self.env.context.get('active_model', 'ir.ui.menu') - data['form'] = self.read(['partner_id', 'company_id', 'table_ids'])[0] + data["ids"] = self.env.context.get("active_ids", []) + data["model"] = self.env.context.get("active_model", "ir.ui.menu") + data["form"] = self.read(["partner_id", "company_id", "table_ids"])[0] used_context = self._build_contexts(data) - data['form']['id'] = str(data['form']['id']) - data['form']['used_context'] = dict( - used_context, lang=self.env.context.get('lang', 'en_US')) + data["form"]["id"] = str(data["form"]["id"]) + data["form"]["used_context"] = dict( + used_context, lang=self.env.context.get("lang", "en_US") + ) return self._print_report(data=data, xlsx_report=xlsx_report) def compute_data_for_report(self, data): - if not data.get('form'): + if not data.get("form"): raise UserError( - _("Form content is missing, this report cannot be printed.")) - partner = data['form'].get('partner_id', False) + _("Form content is missing, this report cannot be printed.") + ) + partner = data["form"].get("partner_id", False) if not partner: - raise UserError( - _("No provided partner.")) - partner = self.env['res.partner'].sudo().browse(partner[0]) - tables = data['form'].get('table_ids', False) + raise UserError(_("No provided partner.")) + partner = self.env["res.partner"].sudo().browse(partner[0]) + tables = data["form"].get("table_ids", False) if tables: - tables = self.env['privacy.partner.data'].browse(tables) + tables = self.env["privacy.partner.data"].browse(tables) tables = self._get_rows_from_tables(tables, partner) - data.update({'tables': tables, }) + data.update({"tables": tables}) return data def _exclude_column(self, model, column): # https://github.com/odoo/odoo/issues/24927 - if model in ('mail.compose.message', 'survey.mail.compose.message'): - if column in ('needaction_partner_ids', 'starred_partner_ids'): + if model in ("mail.compose.message", "survey.mail.compose.message"): + if column in ("needaction_partner_ids", "starred_partner_ids"): return True # feel free to add more specific cases meanwhile the issue is not fixed @@ -147,12 +141,14 @@ class PrivacyPartnerReport(models.TransientModel): res |= self.env[t[1]].sudo().browse(t[3]) if res: values = { - 'name': name, - 'model_id': self.env['ir.model'].sudo().search( - [('model', '=', res._name)]).id, - 'count_rows': len(res.ids), - 'field_type': field_type, - 'res_ids': res.ids, + "name": name, + "model_id": self.env["ir.model"] + .sudo() + .search([("model", "=", res._name)]) + .id, + "count_rows": len(res.ids), + "field_type": field_type, + "res_ids": res.ids, } return values return {} @@ -166,12 +162,15 @@ class PrivacyPartnerReport(models.TransientModel): def _get_rows_from_model(self, model, partner): lines = self.env[model.model] - columns = [k for k, v in self.env[model.model]._fields.items() - if v.comodel_name == 'res.partner' and - v.store and not self._exclude_column(model.model, k)] + columns = [ + k + for k, v in self.env[model.model]._fields.items() + if v.comodel_name == "res.partner" + and v.store + and not self._exclude_column(model.model, k) + ] for column in columns: - lines |= self.env[model.model].sudo().search( - [(column, '=', partner.id)]) + lines |= self.env[model.model].sudo().search([(column, "=", partner.id)]) rows = lines.sudo().read(load=False) rows = self._clean_data(model.model, rows) return rows @@ -191,15 +190,30 @@ class PrivacyPartnerReport(models.TransientModel): return names def _get_tables_from_partner(self, partner): - tables = [t[0] for t in [ - [[self.env[m]._table, m, k, self.env[m].sudo().search( - [(k, '=', partner.id)]).ids, v.type, self.env[m]._transient] - for k, v in self.env[m]._fields.items() - if v.comodel_name == 'res.partner' and self.env[m]._auto and - v.store and not self._exclude_column(m, k)] - for m in [x for x in self.env.registry.keys()]] if t] + tables = [ + t[0] + for t in [ + [ + [ + self.env[m]._table, + m, + k, + self.env[m].sudo().search([(k, "=", partner.id)]).ids, + v.type, + self.env[m]._transient, + ] + for k, v in self.env[m]._fields.items() + if v.comodel_name == "res.partner" + and self.env[m]._auto + and v.store + and not self._exclude_column(m, k) + ] + for m in [x for x in self.env.registry.keys()] + ] + if t + ] for i, t in enumerate(tables): - if t[4] == 'many2many': + if t[4] == "many2many": if t[3]: relation = self.env[t[1]]._fields[t[2]].relation if relation: @@ -207,44 +221,38 @@ class PrivacyPartnerReport(models.TransientModel): return tables def _print_report(self, data, xlsx_report=False): - records = self.env[data['model']].sudo().browse(data.get('ids', [])) + records = self.env[data["model"]].sudo().browse(data.get("ids", [])) if xlsx_report: - return self.env.ref('privacy_partner_report.report_partner_xlsx').\ - with_context(landscape=True).report_action( - records, data=data) + return ( + self.env.ref("privacy_partner_report.report_partner_xlsx") + .with_context(landscape=True) + .report_action(records, data=data) + ) class PrivacyPartnerData(models.TransientModel): _name = "privacy.partner.data" _description = "Privacy Partner Data" - name = fields.Char( - string='Database Table', - ) + name = fields.Char(string="Database Table",) model_id = fields.Many2one( - comodel_name='ir.model', - ondelete='cascade', - string='Models', - ) - field_type = fields.Char( - string="Type", oldname='type', + comodel_name="ir.model", ondelete="cascade", string="Models", ) - count_rows = fields.Integer( - default=0, - string='Number of lines', + field_type = fields.Char(string="Type", oldname="type",) + count_rows = fields.Integer(default=0, string="Number of lines",) + res_ids = fields.Char( + "Related Document IDs", index=True, help="List of Related Document IDs" ) - res_ids = fields.Char('Related Document IDs', index=True, - help='List of Related Document IDs') def action_view_records(self): self.ensure_one() response = { - 'name': self.model_id.display_name, - 'type': 'ir.actions.act_window', - 'res_model': self.model_id.model, - 'view_mode': 'tree,form', - 'domain': [('id', 'in', ast.literal_eval(self.res_ids))], - 'target': 'current', - 'context': {'delete': True}, + "name": self.model_id.display_name, + "type": "ir.actions.act_window", + "res_model": self.model_id.model, + "view_mode": "tree,form", + "domain": [("id", "in", ast.literal_eval(self.res_ids))], + "target": "current", + "context": {"delete": True}, } return response diff --git a/privacy_partner_report/wizard/privacy_report_partner_wizard.xml b/privacy_partner_report/wizard/privacy_report_partner_wizard.xml index 215e3fe..c96863c 100644 --- a/privacy_partner_report/wizard/privacy_report_partner_wizard.xml +++ b/privacy_partner_report/wizard/privacy_report_partner_wizard.xml @@ -1,49 +1,75 @@ - Partner Report privacy.partner.report -
-
-
- - - Select a company
- + +
+
+ + + Select a company +
+ +
-
- - Select a partner
- + + Select a partner +
+ +
+
+ + + + + +