diff --git a/report_substitute/README.rst b/report_substitute/README.rst new file mode 100644 index 0000000000..196cfb7f05 --- /dev/null +++ b/report_substitute/README.rst @@ -0,0 +1,111 @@ +================= +Report Substitute +================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c8dc45c73e2d23ef5504e4ffbae69fe4c62e4e64f8a8b6ef11b13adbb7a7ebb6 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Freporting--engine-lightgray.png?logo=github + :target: https://github.com/OCA/reporting-engine/tree/15.0/report_substitute + :alt: OCA/reporting-engine +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/reporting-engine-15-0/reporting-engine-15-0-report_substitute + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/reporting-engine&target_branch=15.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows you to create substitution rules for report actions. +A typical use case is to replace a standard report by alternative reports +when some conditions are met. For instance, it allows to configure alternate +reports for different companies. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +#. Go to 'Actions' / 'Reports' + +#. Select the desired report you want to 'Substitution Rules' + +#. In the substitutions page add a new line + +#. Select the substitution report action + +#. Set a domain to specify when this substitution should happen + + +When a user calls a report action, the system tries to find the first +substitution in with a domain that matches all records. + +Known issues / Roadmap +====================== + +- The document name result should take the name of the substitution report. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* ACSONE SA/NV + +Contributors +~~~~~~~~~~~~ + +* Bejaoui Souheil + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-sbejaoui| image:: https://github.com/sbejaoui.png?size=40px + :target: https://github.com/sbejaoui + :alt: sbejaoui + +Current `maintainer `__: + +|maintainer-sbejaoui| + +This module is part of the `OCA/reporting-engine `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/report_substitute/__init__.py b/report_substitute/__init__.py new file mode 100644 index 0000000000..aee8895e7a --- /dev/null +++ b/report_substitute/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/report_substitute/__manifest__.py b/report_substitute/__manifest__.py new file mode 100644 index 0000000000..9338dcefc1 --- /dev/null +++ b/report_substitute/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Report Substitute", + "summary": """ + This module allows to create substitution rules for report actions. + """, + "version": "15.0.1.0.0", + "license": "AGPL-3", + "author": "ACSONE SA/NV," "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/reporting-engine", + "depends": ["base", "mail"], + "data": [ + "security/ir_actions_report_substitution_rule.xml", + "views/ir_actions_report.xml", + ], + "demo": ["demo/action_report.xml"], + "assets": { + "web.assets_backend": [ + "report_substitute/static/src/js/action_manager.esm.js", + ], + }, + "maintainers": ["sbejaoui"], +} diff --git a/report_substitute/demo/action_report.xml b/report_substitute/demo/action_report.xml new file mode 100644 index 0000000000..952708c44b --- /dev/null +++ b/report_substitute/demo/action_report.xml @@ -0,0 +1,44 @@ + + + + + Substitution For Technical guide + ir.module.module + qweb-pdf + report_substitute.substitution_report + report_substitute.substitution_report + report + + + + + + + + Substitution 2 For Technical guide + ir.module.module + qweb-pdf + report_substitute.substitution_report_2 + report_substitute.substitution_report_2 + report + + diff --git a/report_substitute/i18n/es.po b/report_substitute/i18n/es.po new file mode 100644 index 0000000000..c6ad3334e3 --- /dev/null +++ b/report_substitute/i18n/es.po @@ -0,0 +1,126 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * report_substitute +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-09-07 16:37+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_ir_actions_report_substitution_rule +msgid "Action Report Substitution Rule" +msgstr "Informe de acción Regla de sustitución" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__domain +msgid "Domain" +msgstr "Dominio" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_mail_thread +msgid "Email Thread" +msgstr "Hilo de correo electrónico" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_mail_compose_message +msgid "Email composition wizard" +msgstr "Asistente de redacción de correo electrónico" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__id +msgid "ID" +msgstr "ID(identificación)" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule____last_update +msgid "Last Modified on" +msgstr "Última Modificación el" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__write_date +msgid "Last Updated on" +msgstr "Ultima Actualización el" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__model +msgid "Model Name" +msgstr "Nombre del modelo" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_ir_actions_report +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__action_report_id +msgid "Report Action" +msgstr "Informar Acción" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__sequence +msgid "Sequence" +msgstr "Secuencia" + +#. module: report_substitute +#: model:ir.actions.report,name:report_substitute.substitution_report_print_2 +msgid "Substitution 2 For Technical guide" +msgstr "Sustitución 2 Para Guía técnica" + +#. module: report_substitute +#: model:ir.actions.report,name:report_substitute.substitution_report_print +msgid "Substitution For Technical guide" +msgstr "Sustitución Para Guía técnica" + +#. module: report_substitute +#: model_terms:ir.ui.view,arch_db:report_substitute.substitution_report +msgid "Substitution Report" +msgstr "Informe de sustitución" + +#. module: report_substitute +#: model_terms:ir.ui.view,arch_db:report_substitute.substitution_report_2 +msgid "Substitution Report 2" +msgstr "Informe de sustitución 2" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__substitution_action_report_id +msgid "Substitution Report Action" +msgstr "Acción del informe de sustitución" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report__action_report_substitution_rule_ids +#: model_terms:ir.ui.view,arch_db:report_substitute.ir_actions_report_form_view +msgid "Substitution Rules" +msgstr "Reglas de sustitución" + +#. module: report_substitute +#. odoo-python +#: code:addons/report_substitute/models/ir_actions_report_substitution_rule.py:0 +#, python-format +msgid "Substitution infinite loop detected" +msgstr "Detectado bucle infinito de sustitución" diff --git a/report_substitute/i18n/it.po b/report_substitute/i18n/it.po new file mode 100644 index 0000000000..5de38eac4e --- /dev/null +++ b/report_substitute/i18n/it.po @@ -0,0 +1,126 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * report_substitute +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-01-18 09:34+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_ir_actions_report_substitution_rule +msgid "Action Report Substitution Rule" +msgstr "Regola sostituzione azione resoconto" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__domain +msgid "Domain" +msgstr "Dominio" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_mail_thread +msgid "Email Thread" +msgstr "Discussione e-mail" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_mail_compose_message +msgid "Email composition wizard" +msgstr "Procedura guidata creazione e-mail" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__id +msgid "ID" +msgstr "ID" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__model +msgid "Model Name" +msgstr "Nome modello" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_ir_actions_report +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__action_report_id +msgid "Report Action" +msgstr "Azione resoconto" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__sequence +msgid "Sequence" +msgstr "Sequenza" + +#. module: report_substitute +#: model:ir.actions.report,name:report_substitute.substitution_report_print_2 +msgid "Substitution 2 For Technical guide" +msgstr "Sostituzione 2 per guida tecnica" + +#. module: report_substitute +#: model:ir.actions.report,name:report_substitute.substitution_report_print +msgid "Substitution For Technical guide" +msgstr "Sostituzione per guida tecnica" + +#. module: report_substitute +#: model_terms:ir.ui.view,arch_db:report_substitute.substitution_report +msgid "Substitution Report" +msgstr "Resoconto sostituzione" + +#. module: report_substitute +#: model_terms:ir.ui.view,arch_db:report_substitute.substitution_report_2 +msgid "Substitution Report 2" +msgstr "Resoconto 2 sostituzione" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__substitution_action_report_id +msgid "Substitution Report Action" +msgstr "Azione resoconto sostituzione" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report__action_report_substitution_rule_ids +#: model_terms:ir.ui.view,arch_db:report_substitute.ir_actions_report_form_view +msgid "Substitution Rules" +msgstr "Regole sostituzione" + +#. module: report_substitute +#. odoo-python +#: code:addons/report_substitute/models/ir_actions_report_substitution_rule.py:0 +#, python-format +msgid "Substitution infinite loop detected" +msgstr "Rilevato ciclo infinito sostituzione" diff --git a/report_substitute/i18n/report_substitute.pot b/report_substitute/i18n/report_substitute.pot new file mode 100644 index 0000000000..163a342053 --- /dev/null +++ b/report_substitute/i18n/report_substitute.pot @@ -0,0 +1,123 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * report_substitute +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_ir_actions_report_substitution_rule +msgid "Action Report Substitution Rule" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__create_uid +msgid "Created by" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__create_date +msgid "Created on" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__display_name +msgid "Display Name" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__domain +msgid "Domain" +msgstr "" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_mail_thread +msgid "Email Thread" +msgstr "" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_mail_compose_message +msgid "Email composition wizard" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__id +msgid "ID" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule____last_update +msgid "Last Modified on" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__write_date +msgid "Last Updated on" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__model +msgid "Model Name" +msgstr "" + +#. module: report_substitute +#: model:ir.model,name:report_substitute.model_ir_actions_report +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__action_report_id +msgid "Report Action" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__sequence +msgid "Sequence" +msgstr "" + +#. module: report_substitute +#: model:ir.actions.report,name:report_substitute.substitution_report_print_2 +msgid "Substitution 2 For Technical guide" +msgstr "" + +#. module: report_substitute +#: model:ir.actions.report,name:report_substitute.substitution_report_print +msgid "Substitution For Technical guide" +msgstr "" + +#. module: report_substitute +#: model_terms:ir.ui.view,arch_db:report_substitute.substitution_report +msgid "Substitution Report" +msgstr "" + +#. module: report_substitute +#: model_terms:ir.ui.view,arch_db:report_substitute.substitution_report_2 +msgid "Substitution Report 2" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report_substitution_rule__substitution_action_report_id +msgid "Substitution Report Action" +msgstr "" + +#. module: report_substitute +#: model:ir.model.fields,field_description:report_substitute.field_ir_actions_report__action_report_substitution_rule_ids +#: model_terms:ir.ui.view,arch_db:report_substitute.ir_actions_report_form_view +msgid "Substitution Rules" +msgstr "" + +#. module: report_substitute +#. odoo-python +#: code:addons/report_substitute/models/ir_actions_report_substitution_rule.py:0 +#, python-format +msgid "Substitution infinite loop detected" +msgstr "" diff --git a/report_substitute/models/__init__.py b/report_substitute/models/__init__.py new file mode 100644 index 0000000000..a93fca4b48 --- /dev/null +++ b/report_substitute/models/__init__.py @@ -0,0 +1,3 @@ +from . import ir_actions_report +from . import ir_actions_report_substitution_rule +from . import mail_thread diff --git a/report_substitute/models/ir_actions_report.py b/report_substitute/models/ir_actions_report.py new file mode 100644 index 0000000000..65a670180b --- /dev/null +++ b/report_substitute/models/ir_actions_report.py @@ -0,0 +1,78 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.tools.safe_eval import safe_eval + + +class IrActionReport(models.Model): + + _inherit = "ir.actions.report" + + action_report_substitution_rule_ids = fields.One2many( + "ir.actions.report.substitution.rule", + "action_report_id", + string="Substitution Rules", + ) + + def _get_substitution_report(self, model, active_ids): + self.ensure_one() + model = self.env[model] + for substitution_report_rule in self.action_report_substitution_rule_ids: + domain = safe_eval(substitution_report_rule.domain) + domain.append(("id", "in", active_ids)) + if set(model.search(domain).ids) == set(active_ids): + return substitution_report_rule.substitution_action_report_id + return False + + def get_substitution_report(self, active_ids): + self.ensure_one() + action_report = self + substitution_report = action_report + while substitution_report: + action_report = substitution_report + substitution_report = action_report._get_substitution_report( + action_report.model, active_ids + ) + return action_report + + @api.model + def get_substitution_report_action(self, action, active_ids): + if action.get("id"): + action_report = self.browse(action["id"]) + substitution_report = action_report + while substitution_report: + action_report = substitution_report + substitution_report = action_report._get_substitution_report( + action_report.model, active_ids + ) + action.update(action_report.read()[0]) + + return action + + def _render(self, res_ids, data=None): + substitution_report = self.get_substitution_report(res_ids) + return super(IrActionReport, substitution_report)._render(res_ids, data) + + def _render_qweb_pdf(self, res_ids=None, data=None): + substitution_report = self.get_substitution_report(res_ids) + return super(IrActionReport, substitution_report)._render_qweb_pdf( + res_ids=res_ids, data=data + ) + + def report_action(self, docids, data=None, config=True): + if docids: + if isinstance(docids, models.Model): + active_ids = docids.ids + elif isinstance(docids, int): + active_ids = [docids] + elif isinstance(docids, list): + active_ids = docids + substitution_report = self.get_substitution_report(active_ids) + return super(IrActionReport, substitution_report).report_action( + docids, data, config + ) + return super().report_action(docids, data, config) + + def get_action_report_substitution_rule_ids(self): + return self.action_report_substitution_rule_ids.ids diff --git a/report_substitute/models/ir_actions_report_substitution_rule.py b/report_substitute/models/ir_actions_report_substitution_rule.py new file mode 100644 index 0000000000..7320d533f3 --- /dev/null +++ b/report_substitute/models/ir_actions_report_substitution_rule.py @@ -0,0 +1,47 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class ActionsReportSubstitutionRule(models.Model): + + _name = "ir.actions.report.substitution.rule" + _description = "Action Report Substitution Rule" + _order = "sequence ASC" + + sequence = fields.Integer(default=10) + action_report_id = fields.Many2one( + comodel_name="ir.actions.report", + string="Report Action", + required=True, + ondelete="cascade", + ) + model = fields.Char(related="action_report_id.model", store=True) + domain = fields.Char(required=True, default="[]") + substitution_action_report_id = fields.Many2one( + comodel_name="ir.actions.report", + string="Substitution Report Action", + required=True, + ondelete="cascade", + domain="[('model', '=', model)]", + ) + + @api.constrains("substitution_action_report_id", "action_report_id") + def _check_substitution_infinite_loop(self): + def _check_infinite_loop(original_report, substitution_report): + if original_report == substitution_report: + raise ValidationError(_("Substitution infinite loop detected")) + for ( + substitution_rule + ) in substitution_report.action_report_substitution_rule_ids: + _check_infinite_loop( + original_report, + substitution_rule.substitution_action_report_id, + ) + + for rec in self: + _check_infinite_loop( + rec.action_report_id, rec.substitution_action_report_id + ) diff --git a/report_substitute/models/mail_thread.py b/report_substitute/models/mail_thread.py new file mode 100644 index 0000000000..35a86ee3cf --- /dev/null +++ b/report_substitute/models/mail_thread.py @@ -0,0 +1,21 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class MailThread(models.AbstractModel): + + _inherit = "mail.thread" + + def message_post_with_template(self, template_id, **kwargs): + template = self.env["mail.template"].browse(template_id) + old_report = False + if template and template.report_template and self.ids: + active_ids = self.ids + old_report = template.report_template + template.report_template = old_report.get_substitution_report(active_ids) + res = super().message_post_with_template(template_id, **kwargs) + if old_report: + template.report_template = old_report + return res diff --git a/report_substitute/readme/CONTRIBUTORS.rst b/report_substitute/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..35c03ffe0f --- /dev/null +++ b/report_substitute/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Bejaoui Souheil diff --git a/report_substitute/readme/DESCRIPTION.rst b/report_substitute/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..55ccac5e86 --- /dev/null +++ b/report_substitute/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +This module allows you to create substitution rules for report actions. +A typical use case is to replace a standard report by alternative reports +when some conditions are met. For instance, it allows to configure alternate +reports for different companies. diff --git a/report_substitute/readme/ROADMAP.rst b/report_substitute/readme/ROADMAP.rst new file mode 100644 index 0000000000..28e27a3eac --- /dev/null +++ b/report_substitute/readme/ROADMAP.rst @@ -0,0 +1 @@ +- The document name result should take the name of the substitution report. diff --git a/report_substitute/readme/USAGE.rst b/report_substitute/readme/USAGE.rst new file mode 100644 index 0000000000..b6f6613970 --- /dev/null +++ b/report_substitute/readme/USAGE.rst @@ -0,0 +1,15 @@ +To use this module, you need to: + +#. Go to 'Actions' / 'Reports' + +#. Select the desired report you want to 'Substitution Rules' + +#. In the substitutions page add a new line + +#. Select the substitution report action + +#. Set a domain to specify when this substitution should happen + + +When a user calls a report action, the system tries to find the first +substitution in with a domain that matches all records. diff --git a/report_substitute/security/ir_actions_report_substitution_rule.xml b/report_substitute/security/ir_actions_report_substitution_rule.xml new file mode 100644 index 0000000000..cf06f95ff9 --- /dev/null +++ b/report_substitute/security/ir_actions_report_substitution_rule.xml @@ -0,0 +1,22 @@ + + + + + action.report.substitution.rule user access + + + + + + + + action.report.substitution.rule manager access + + + + + + + + diff --git a/report_substitute/static/description/icon.png b/report_substitute/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/report_substitute/static/description/icon.png differ diff --git a/report_substitute/static/description/index.html b/report_substitute/static/description/index.html new file mode 100644 index 0000000000..fafd16412b --- /dev/null +++ b/report_substitute/static/description/index.html @@ -0,0 +1,446 @@ + + + + + +Report Substitute + + + +
+

Report Substitute

+ + +

Beta License: AGPL-3 OCA/reporting-engine Translate me on Weblate Try me on Runboat

+

This module allows you to create substitution rules for report actions. +A typical use case is to replace a standard report by alternative reports +when some conditions are met. For instance, it allows to configure alternate +reports for different companies.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  1. Go to ‘Actions’ / ‘Reports’
  2. +
  3. Select the desired report you want to ‘Substitution Rules’
  4. +
  5. In the substitutions page add a new line
  6. +
  7. Select the substitution report action
  8. +
  9. Set a domain to specify when this substitution should happen
  10. +
+

When a user calls a report action, the system tries to find the first +substitution in with a domain that matches all records.

+
+
+

Known issues / Roadmap

+
    +
  • The document name result should take the name of the substitution report.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • ACSONE SA/NV
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

sbejaoui

+

This module is part of the OCA/reporting-engine project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/report_substitute/static/src/js/action_manager.esm.js b/report_substitute/static/src/js/action_manager.esm.js new file mode 100644 index 0000000000..cff852c708 --- /dev/null +++ b/report_substitute/static/src/js/action_manager.esm.js @@ -0,0 +1,29 @@ +/** @odoo-module **/ + +import {registry} from "@web/core/registry"; + +registry + .category("ir.actions.report handlers") + .add("sustitution_handler", async function (action, options, env) { + const orm = env.services.orm; + const action_report_substitution_rule_ids = await orm.call( + "ir.actions.report", + "get_action_report_substitution_rule_ids", + [action.id] + ); + if ( + action.type === "ir.actions.report" && + action.context.active_ids && + action_report_substitution_rule_ids && + action_report_substitution_rule_ids.length !== 0 + ) { + var active_ids = action.context.active_ids; + const substitution = await orm.call( + "ir.actions.report", + "get_substitution_report_action", + [action, active_ids] + ); + Object.assign(action, substitution); + } + return Promise.resolve(false); + }); diff --git a/report_substitute/tests/__init__.py b/report_substitute/tests/__init__.py new file mode 100644 index 0000000000..8c5a3f248f --- /dev/null +++ b/report_substitute/tests/__init__.py @@ -0,0 +1 @@ +from . import test_report_substitute diff --git a/report_substitute/tests/test_report_substitute.py b/report_substitute/tests/test_report_substitute.py new file mode 100644 index 0000000000..0a07fd8646 --- /dev/null +++ b/report_substitute/tests/test_report_substitute.py @@ -0,0 +1,81 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.exceptions import ValidationError +from odoo.tests.common import TransactionCase + + +class TestReportSubstitute(TransactionCase): + def setUp(self): + # In the demo file we create a new report for ir.module.module model + # with a substation rule from the original report action + super(TestReportSubstitute, self).setUp() + self.action_report = self.env.ref("base.ir_module_reference_print") + self.res_ids = self.env.ref("base.module_base").ids + self.substitution_rule = self.env.ref( + "report_substitute.substitution_rule_demo_1" + ) + self.env.company.external_report_layout_id = self.env.ref( + "web.external_layout_standard" + ).id + + def test_substitution(self): + res = str(self.action_report._render(res_ids=self.res_ids)[0]) + self.assertIn('
Substitution Report
', res) + # remove the substation rule + self.substitution_rule.unlink() + res = str(self.action_report._render(res_ids=self.res_ids)[0]) + self.assertNotIn('
Substitution Report
', res) + + def test_recursive_substitution(self): + res = str(self.action_report._render(res_ids=self.res_ids)[0]) + self.assertNotIn('
Substitution Report 2
', res) + self.env["ir.actions.report.substitution.rule"].create( + { + "substitution_action_report_id": self.env.ref( + "report_substitute.substitution_report_print_2" + ).id, + "action_report_id": self.env.ref( + "report_substitute.substitution_report_print" + ).id, + } + ) + res = str(self.action_report._render(res_ids=self.res_ids)[0]) + self.assertIn('
Substitution Report 2
', res) + + def test_substitution_with_domain(self): + self.substitution_rule.write({"domain": "[('name', '=', 'base')]"}) + res = str(self.action_report._render(res_ids=self.res_ids)[0]) + self.assertIn('
Substitution Report
', res) + self.substitution_rule.write({"domain": "[('name', '!=', 'base')]"}) + res = str(self.action_report._render(res_ids=self.res_ids)[0]) + self.assertNotIn('
Substitution Report
', res) + + def test_substitution_with_action_dict(self): + substitution_report_action = self.env[ + "ir.actions.report" + ].get_substitution_report_action(self.action_report.read()[0], self.res_ids) + self.assertEqual( + substitution_report_action["id"], + self.substitution_rule.substitution_action_report_id.id, + ) + + def test_substitution_with_report_action(self): + res = self.action_report.report_action(self.res_ids) + self.assertEqual( + res["report_name"], + self.substitution_rule.substitution_action_report_id.report_name, + ) + + def test_substitution_infinite_loop(self): + with self.assertRaises(ValidationError): + self.env["ir.actions.report.substitution.rule"].create( + { + "action_report_id": self.env.ref( + "report_substitute.substitution_report_print" + ).id, + "substitution_action_report_id": self.env.ref( + "base.ir_module_reference_print" + ).id, + } + ) diff --git a/report_substitute/views/ir_actions_report.xml b/report_substitute/views/ir_actions_report.xml new file mode 100644 index 0000000000..f51b87b940 --- /dev/null +++ b/report_substitute/views/ir_actions_report.xml @@ -0,0 +1,47 @@ + + + + + ir.actions.report.form (in report_substitute) + ir.actions.report + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+
+
diff --git a/report_substitute/wizards/__init__.py b/report_substitute/wizards/__init__.py new file mode 100644 index 0000000000..b528d997d1 --- /dev/null +++ b/report_substitute/wizards/__init__.py @@ -0,0 +1 @@ +from . import mail_compose_message diff --git a/report_substitute/wizards/mail_compose_message.py b/report_substitute/wizards/mail_compose_message.py new file mode 100644 index 0000000000..f7fb800674 --- /dev/null +++ b/report_substitute/wizards/mail_compose_message.py @@ -0,0 +1,34 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class MailComposeMessage(models.TransientModel): + + _inherit = "mail.compose.message" + + @api.onchange("template_id") + def _onchange_template_id_wrapper(self): + if self.template_id: + report_template = self.template_id.report_template + active_ids = [] + if self.env.context.get("active_ids"): + active_ids = self.env.context.get("active_ids") + elif self.env.context.get("default_res_id"): + active_ids = [self.env.context.get("default_res_id")] + if ( + report_template + and report_template.action_report_substitution_rule_ids + and active_ids + ): + old_tmpl = report_template + self.template_id.report_template = old_tmpl.get_substitution_report( + active_ids + ) + onchange_result_with_substituted_report = ( + super()._onchange_template_id_wrapper() + ) + self.template_id.report_template = old_tmpl + return onchange_result_with_substituted_report + return super()._onchange_template_id_wrapper() diff --git a/setup/report_substitute/odoo/addons/report_substitute b/setup/report_substitute/odoo/addons/report_substitute new file mode 120000 index 0000000000..b85da0bad6 --- /dev/null +++ b/setup/report_substitute/odoo/addons/report_substitute @@ -0,0 +1 @@ +../../../../report_substitute \ No newline at end of file diff --git a/setup/report_substitute/setup.py b/setup/report_substitute/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/report_substitute/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)