From cd0c96016f4c1d1d5bd0707ec8ef5e21813ac260 Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Thu, 11 Jul 2019 15:36:43 +0200 Subject: [PATCH 01/11] [ADD] - report substitute This addon give the possibility to substitute a report action by another based on some criteria. --- report_substitute/__init__.py | 1 + report_substitute/__manifest__.py | 20 +++++++++ report_substitute/models/__init__.py | 2 + report_substitute/models/ir_actions_report.py | 40 +++++++++++++++++ ...ir_actions_report_substitution_criteria.py | 28 ++++++++++++ ...r_actions_report_substitution_criteria.xml | 25 +++++++++++ report_substitute/views/ir_actions_report.xml | 43 +++++++++++++++++++ .../odoo/addons/report_substitute | 1 + setup/report_substitute/setup.py | 6 +++ 9 files changed, 166 insertions(+) create mode 100644 report_substitute/__init__.py create mode 100644 report_substitute/__manifest__.py create mode 100644 report_substitute/models/__init__.py create mode 100644 report_substitute/models/ir_actions_report.py create mode 100644 report_substitute/models/ir_actions_report_substitution_criteria.py create mode 100644 report_substitute/security/ir_actions_report_substitution_criteria.xml create mode 100644 report_substitute/views/ir_actions_report.xml create mode 120000 setup/report_substitute/odoo/addons/report_substitute create mode 100644 setup/report_substitute/setup.py diff --git a/report_substitute/__init__.py b/report_substitute/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/report_substitute/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/report_substitute/__manifest__.py b/report_substitute/__manifest__.py new file mode 100644 index 00000000..a9ad3ca7 --- /dev/null +++ b/report_substitute/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Report Substitute', + 'summary': """ + This addon give the possibility to substitute a report action by + another based on some criteria. + """, + 'version': '12.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'ACSONE SA/NV,' + 'Odoo Community Association (OCA)', + 'website': 'https://github.com/acsone/reporting-engine', + 'depends': ['base'], + 'data': [ + 'security/ir_actions_report_substitution_criteria.xml', + 'views/ir_actions_report.xml', + ], +} diff --git a/report_substitute/models/__init__.py b/report_substitute/models/__init__.py new file mode 100644 index 00000000..d1a25e84 --- /dev/null +++ b/report_substitute/models/__init__.py @@ -0,0 +1,2 @@ +from . import ir_actions_report +from . import ir_actions_report_substitution_criteria diff --git a/report_substitute/models/ir_actions_report.py b/report_substitute/models/ir_actions_report.py new file mode 100644 index 00000000..ca2e7ce8 --- /dev/null +++ b/report_substitute/models/ir_actions_report.py @@ -0,0 +1,40 @@ +# 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_criteria_ids = fields.One2many( + comodel_name="ir.actions.report.substitution.criteria", + inverse_name="action_report_id", + string="Substitution Criteria", + ) + + @api.multi + def _get_substitution_report(self, model, active_ids): + self.ensure_one() + model = self.env[model] + for ( + substitution_report_criteria + ) in self.action_report_substitution_criteria_ids: + domain = safe_eval(substitution_report_criteria.domain) + domain.append(('id', 'in', active_ids)) + if set(model.search(domain).ids) == set(active_ids): + return ( + substitution_report_criteria.substitution_action_report_id + ) + return False + + @api.multi + def render(self, res_ids, data=None): + substitution_report = self._get_substitution_report( + self.model, res_ids + ) + if substitution_report: + return substitution_report.render(res_ids) + return super().render(res_ids, data) diff --git a/report_substitute/models/ir_actions_report_substitution_criteria.py b/report_substitute/models/ir_actions_report_substitution_criteria.py new file mode 100644 index 00000000..04430aed --- /dev/null +++ b/report_substitute/models/ir_actions_report_substitution_criteria.py @@ -0,0 +1,28 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ActionsReportSubstitutionCriteria(models.Model): + + _name = 'ir.actions.report.substitution.criteria' + _description = 'Action Report Substitution Criteria' + _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(string="Domain", 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)]" + ) diff --git a/report_substitute/security/ir_actions_report_substitution_criteria.xml b/report_substitute/security/ir_actions_report_substitution_criteria.xml new file mode 100644 index 00000000..b74770b8 --- /dev/null +++ b/report_substitute/security/ir_actions_report_substitution_criteria.xml @@ -0,0 +1,25 @@ + + + + + + + action.report.substitution.criteria user access + + + + + + + + + action.report.substitution.criteria manager access + + + + + + + + diff --git a/report_substitute/views/ir_actions_report.xml b/report_substitute/views/ir_actions_report.xml new file mode 100644 index 00000000..750aeb74 --- /dev/null +++ b/report_substitute/views/ir_actions_report.xml @@ -0,0 +1,43 @@ + + + + + + + ir.actions.report.form (in report_dispatch_base) + + ir.actions.report + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+
+ + +
diff --git a/setup/report_substitute/odoo/addons/report_substitute b/setup/report_substitute/odoo/addons/report_substitute new file mode 120000 index 00000000..b85da0ba --- /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 00000000..28c57bb6 --- /dev/null +++ b/setup/report_substitute/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 255cf1d6b43ee581d7a2495eb480d8dfaafa0573 Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Mon, 22 Jul 2019 14:26:07 +0200 Subject: [PATCH 02/11] [ADD] - Add readme --- report_substitute/README.rst | 100 ++++ report_substitute/__manifest__.py | 3 +- report_substitute/readme/CONTRIBUTORS.rst | 1 + report_substitute/readme/DESCRIPTION.rst | 4 + report_substitute/readme/ROADMAP.rst | 1 + report_substitute/readme/USAGE.rst | 15 + .../static/description/index.html | 443 ++++++++++++++++++ report_substitute/views/ir_actions_report.xml | 2 +- 8 files changed, 566 insertions(+), 3 deletions(-) create mode 100644 report_substitute/README.rst create mode 100644 report_substitute/readme/CONTRIBUTORS.rst create mode 100644 report_substitute/readme/DESCRIPTION.rst create mode 100644 report_substitute/readme/ROADMAP.rst create mode 100644 report_substitute/readme/USAGE.rst create mode 100644 report_substitute/static/description/index.html diff --git a/report_substitute/README.rst b/report_substitute/README.rst new file mode 100644 index 00000000..7739e6f2 --- /dev/null +++ b/report_substitute/README.rst @@ -0,0 +1,100 @@ +================= +Report Substitute +================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/12.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-12-0/reporting-engine-12-0-report_substitute + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/143/12.0 + :alt: Try me on Runbot + +|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 substitute + +#. 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 smashing 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. + +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/__manifest__.py b/report_substitute/__manifest__.py index a9ad3ca7..db268ee1 100644 --- a/report_substitute/__manifest__.py +++ b/report_substitute/__manifest__.py @@ -4,8 +4,7 @@ { 'name': 'Report Substitute', 'summary': """ - This addon give the possibility to substitute a report action by - another based on some criteria. + This module allows to create substitution rules for report actions. """, 'version': '12.0.1.0.0', 'license': 'AGPL-3', diff --git a/report_substitute/readme/CONTRIBUTORS.rst b/report_substitute/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..35c03ffe --- /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 00000000..55ccac5e --- /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 00000000..28e27a3e --- /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 00000000..b91a68d5 --- /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 substitute + +#. 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/static/description/index.html b/report_substitute/static/description/index.html new file mode 100644 index 00000000..e2994a63 --- /dev/null +++ b/report_substitute/static/description/index.html @@ -0,0 +1,443 @@ + + + + + + +Report Substitute + + + +
+

Report Substitute

+ + +

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

+

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 substitute
  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 smashing 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.

+

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/views/ir_actions_report.xml b/report_substitute/views/ir_actions_report.xml index 750aeb74..6d9f4a45 100644 --- a/report_substitute/views/ir_actions_report.xml +++ b/report_substitute/views/ir_actions_report.xml @@ -11,7 +11,7 @@ - + From 6b658a649bf79a25c72bdde7280aa8f98a3edc62 Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Mon, 22 Jul 2019 15:43:57 +0200 Subject: [PATCH 03/11] [ADD] - Add unit tests --- report_substitute/README.rst | 2 +- report_substitute/__init__.py | 1 + report_substitute/__manifest__.py | 3 +- report_substitute/demo/action_report.xml | 29 +++++++++++ report_substitute/models/__init__.py | 2 +- report_substitute/models/ir_actions_report.py | 16 +++---- ...=> ir_actions_report_substitution_rule.py} | 6 +-- report_substitute/readme/USAGE.rst | 2 +- ...> ir_actions_report_substitution_rule.xml} | 12 ++--- .../static/description/index.html | 2 +- report_substitute/tests/__init__.py | 1 + .../tests/test_report_substitute.py | 48 +++++++++++++++++++ report_substitute/views/ir_actions_report.xml | 4 +- 13 files changed, 104 insertions(+), 24 deletions(-) create mode 100644 report_substitute/demo/action_report.xml rename report_substitute/models/{ir_actions_report_substitution_criteria.py => ir_actions_report_substitution_rule.py} (81%) rename report_substitute/security/{ir_actions_report_substitution_criteria.xml => ir_actions_report_substitution_rule.xml} (76%) create mode 100644 report_substitute/tests/__init__.py create mode 100644 report_substitute/tests/test_report_substitute.py diff --git a/report_substitute/README.rst b/report_substitute/README.rst index 7739e6f2..20762b83 100644 --- a/report_substitute/README.rst +++ b/report_substitute/README.rst @@ -42,7 +42,7 @@ To use this module, you need to: #. Go to 'Actions' / 'Reports' -#. Select the desired report you want to substitute +#. Select the desired report you want to 'Substitution Rules' #. In the substitutions page add a new line diff --git a/report_substitute/__init__.py b/report_substitute/__init__.py index 0650744f..0ee8b507 100644 --- a/report_substitute/__init__.py +++ b/report_substitute/__init__.py @@ -1 +1,2 @@ from . import models +from . import tests diff --git a/report_substitute/__manifest__.py b/report_substitute/__manifest__.py index db268ee1..f65a6a02 100644 --- a/report_substitute/__manifest__.py +++ b/report_substitute/__manifest__.py @@ -13,7 +13,8 @@ 'website': 'https://github.com/acsone/reporting-engine', 'depends': ['base'], 'data': [ - 'security/ir_actions_report_substitution_criteria.xml', + 'security/ir_actions_report_substitution_rule.xml', 'views/ir_actions_report.xml', ], + 'demo': ['demo/action_report.xml'], } diff --git a/report_substitute/demo/action_report.xml b/report_substitute/demo/action_report.xml new file mode 100644 index 00000000..e3b85ba2 --- /dev/null +++ b/report_substitute/demo/action_report.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + diff --git a/report_substitute/models/__init__.py b/report_substitute/models/__init__.py index d1a25e84..27b9defe 100644 --- a/report_substitute/models/__init__.py +++ b/report_substitute/models/__init__.py @@ -1,2 +1,2 @@ from . import ir_actions_report -from . import ir_actions_report_substitution_criteria +from . import ir_actions_report_substitution_rule diff --git a/report_substitute/models/ir_actions_report.py b/report_substitute/models/ir_actions_report.py index ca2e7ce8..32c31812 100644 --- a/report_substitute/models/ir_actions_report.py +++ b/report_substitute/models/ir_actions_report.py @@ -1,7 +1,7 @@ # 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 import api, fields, models from odoo.tools.safe_eval import safe_eval @@ -9,10 +9,10 @@ class IrActionReport(models.Model): _inherit = 'ir.actions.report' - action_report_substitution_criteria_ids = fields.One2many( - comodel_name="ir.actions.report.substitution.criteria", + action_report_substitution_rule_ids = fields.One2many( + comodel_name="ir.actions.report.substitution.rule", inverse_name="action_report_id", - string="Substitution Criteria", + string="Substitution Rules", ) @api.multi @@ -20,13 +20,13 @@ class IrActionReport(models.Model): self.ensure_one() model = self.env[model] for ( - substitution_report_criteria - ) in self.action_report_substitution_criteria_ids: - domain = safe_eval(substitution_report_criteria.domain) + 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_criteria.substitution_action_report_id + substitution_report_rule.substitution_action_report_id ) return False diff --git a/report_substitute/models/ir_actions_report_substitution_criteria.py b/report_substitute/models/ir_actions_report_substitution_rule.py similarity index 81% rename from report_substitute/models/ir_actions_report_substitution_criteria.py rename to report_substitute/models/ir_actions_report_substitution_rule.py index 04430aed..4c725071 100644 --- a/report_substitute/models/ir_actions_report_substitution_criteria.py +++ b/report_substitute/models/ir_actions_report_substitution_rule.py @@ -4,10 +4,10 @@ from odoo import fields, models -class ActionsReportSubstitutionCriteria(models.Model): +class ActionsReportSubstitutionRule(models.Model): - _name = 'ir.actions.report.substitution.criteria' - _description = 'Action Report Substitution Criteria' + _name = 'ir.actions.report.substitution.rule' + _description = 'Action Report Substitution Rule' _order = 'sequence ASC' sequence = fields.Integer(default=10) diff --git a/report_substitute/readme/USAGE.rst b/report_substitute/readme/USAGE.rst index b91a68d5..b6f66139 100644 --- a/report_substitute/readme/USAGE.rst +++ b/report_substitute/readme/USAGE.rst @@ -2,7 +2,7 @@ To use this module, you need to: #. Go to 'Actions' / 'Reports' -#. Select the desired report you want to substitute +#. Select the desired report you want to 'Substitution Rules' #. In the substitutions page add a new line diff --git a/report_substitute/security/ir_actions_report_substitution_criteria.xml b/report_substitute/security/ir_actions_report_substitution_rule.xml similarity index 76% rename from report_substitute/security/ir_actions_report_substitution_criteria.xml rename to report_substitute/security/ir_actions_report_substitution_rule.xml index b74770b8..e7af09af 100644 --- a/report_substitute/security/ir_actions_report_substitution_criteria.xml +++ b/report_substitute/security/ir_actions_report_substitution_rule.xml @@ -4,18 +4,18 @@ - - action.report.substitution.criteria user access - + + action.report.substitution.rule user access + - - action.report.substitution.criteria manager access - + + action.report.substitution.rule manager access + diff --git a/report_substitute/static/description/index.html b/report_substitute/static/description/index.html index e2994a63..71e4b31b 100644 --- a/report_substitute/static/description/index.html +++ b/report_substitute/static/description/index.html @@ -391,7 +391,7 @@ reports for different companies.

To use this module, you need to:

  1. Go to ‘Actions’ / ‘Reports’
  2. -
  3. Select the desired report you want to substitute
  4. +
  5. Select the desired report you want to ‘Substitution Rules’
  6. In the substitutions page add a new line
  7. Select the substitution report action
  8. Set a domain to specify when this substitution should happen
  9. diff --git a/report_substitute/tests/__init__.py b/report_substitute/tests/__init__.py new file mode 100644 index 00000000..8c5a3f24 --- /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 00000000..fecb4be6 --- /dev/null +++ b/report_substitute/tests/test_report_substitute.py @@ -0,0 +1,48 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +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' + ) + + 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) diff --git a/report_substitute/views/ir_actions_report.xml b/report_substitute/views/ir_actions_report.xml index 6d9f4a45..bda45ba7 100644 --- a/report_substitute/views/ir_actions_report.xml +++ b/report_substitute/views/ir_actions_report.xml @@ -11,8 +11,8 @@ - - + + From 9f2f1b420f1886de4518895773c45aee959a3ef2 Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Tue, 23 Jul 2019 15:33:01 +0200 Subject: [PATCH 04/11] [FIX] - missing method param --- report_substitute/models/ir_actions_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/report_substitute/models/ir_actions_report.py b/report_substitute/models/ir_actions_report.py index 32c31812..3cf091d1 100644 --- a/report_substitute/models/ir_actions_report.py +++ b/report_substitute/models/ir_actions_report.py @@ -36,5 +36,5 @@ class IrActionReport(models.Model): self.model, res_ids ) if substitution_report: - return substitution_report.render(res_ids) + return substitution_report.render(res_ids, data) return super().render(res_ids, data) From 590377ee3678dc820905e2fd63f1a903ce149d65 Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Tue, 23 Jul 2019 16:24:13 +0200 Subject: [PATCH 05/11] [FIX] - prevent substitution loop --- report_substitute/views/ir_actions_report.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/report_substitute/views/ir_actions_report.xml b/report_substitute/views/ir_actions_report.xml index bda45ba7..0c2c265c 100644 --- a/report_substitute/views/ir_actions_report.xml +++ b/report_substitute/views/ir_actions_report.xml @@ -16,7 +16,8 @@ - + />
    From ea774aab9c2b68e6eb8efb9e0ac9718e33f05e6a Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Thu, 25 Jul 2019 11:07:01 +0200 Subject: [PATCH 06/11] [IMP] - manage subtitute report for multi-action --- report_substitute/__manifest__.py | 1 + report_substitute/models/ir_actions_report.py | 17 +++++++-- .../static/src/js/action_manager.js | 35 +++++++++++++++++++ report_substitute/views/assets_backend.xml | 8 +++++ 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 report_substitute/static/src/js/action_manager.js create mode 100644 report_substitute/views/assets_backend.xml diff --git a/report_substitute/__manifest__.py b/report_substitute/__manifest__.py index f65a6a02..f6061a75 100644 --- a/report_substitute/__manifest__.py +++ b/report_substitute/__manifest__.py @@ -14,6 +14,7 @@ 'depends': ['base'], 'data': [ 'security/ir_actions_report_substitution_rule.xml', + 'views/assets_backend.xml', 'views/ir_actions_report.xml', ], 'demo': ['demo/action_report.xml'], diff --git a/report_substitute/models/ir_actions_report.py b/report_substitute/models/ir_actions_report.py index 3cf091d1..88d7b50b 100644 --- a/report_substitute/models/ir_actions_report.py +++ b/report_substitute/models/ir_actions_report.py @@ -25,11 +25,22 @@ class IrActionReport(models.Model): 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 substitution_report_rule.substitution_action_report_id return False + @api.model + def get_substitution_report_dict(self, action_report_dict, active_ids): + if action_report_dict.get('id'): + action_report = self.browse(action_report_dict['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_report_dict.update(action_report.read()[0]) + return action_report_dict + @api.multi def render(self, res_ids, data=None): substitution_report = self._get_substitution_report( diff --git a/report_substitute/static/src/js/action_manager.js b/report_substitute/static/src/js/action_manager.js new file mode 100644 index 00000000..754dd70d --- /dev/null +++ b/report_substitute/static/src/js/action_manager.js @@ -0,0 +1,35 @@ +// Copyright 2019 ACSONE SA/NV +// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +odoo.define('report_substitute.action_report_substitute', function(require) { + "use strict"; + + var ActionManager = require('web.ActionManager'); + + ActionManager.include({ + /** + * Intercept action handling substitute the report action + * @override + */ + _handleAction: function(action, options) { + if (action.type === 'ir.actions.report' && + action.context.active_ids) { + var active_ids = action.context.active_ids; + var self = this; + var _super = this._super; + var callersArguments = arguments; + return this._rpc({ + model: 'ir.actions.report', + method: 'get_substitution_report_dict', + args: [action, active_ids] + }).then(function(action_id) { + callersArguments[0] = action_id + return _super.apply(self, callersArguments); + }); + + } + return this._super.apply(this, arguments); + }, + + }); + +}); diff --git a/report_substitute/views/assets_backend.xml b/report_substitute/views/assets_backend.xml new file mode 100644 index 00000000..4120d85f --- /dev/null +++ b/report_substitute/views/assets_backend.xml @@ -0,0 +1,8 @@ + + + + From ed669b00a3b205545e084a57da7313cffeca43f7 Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Thu, 25 Jul 2019 13:02:07 +0200 Subject: [PATCH 07/11] [IMP] - manage substitution for mail.thread message_post_with_template --- report_substitute/__init__.py | 1 + report_substitute/__manifest__.py | 2 +- report_substitute/models/__init__.py | 1 + report_substitute/models/ir_actions_report.py | 35 +++++++++++++++---- report_substitute/models/mail_thread.py | 24 +++++++++++++ .../static/src/js/action_manager.js | 16 +++++---- report_substitute/views/ir_actions_report.xml | 6 ++-- report_substitute/wizards/__init__.py | 1 + .../wizards/mail_compose_message.py | 28 +++++++++++++++ 9 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 report_substitute/models/mail_thread.py create mode 100644 report_substitute/wizards/__init__.py create mode 100644 report_substitute/wizards/mail_compose_message.py diff --git a/report_substitute/__init__.py b/report_substitute/__init__.py index 0ee8b507..1c15bc7e 100644 --- a/report_substitute/__init__.py +++ b/report_substitute/__init__.py @@ -1,2 +1,3 @@ from . import models +from . import wizards from . import tests diff --git a/report_substitute/__manifest__.py b/report_substitute/__manifest__.py index f6061a75..82cac363 100644 --- a/report_substitute/__manifest__.py +++ b/report_substitute/__manifest__.py @@ -11,7 +11,7 @@ 'author': 'ACSONE SA/NV,' 'Odoo Community Association (OCA)', 'website': 'https://github.com/acsone/reporting-engine', - 'depends': ['base'], + 'depends': ['base', 'mail'], 'data': [ 'security/ir_actions_report_substitution_rule.xml', 'views/assets_backend.xml', diff --git a/report_substitute/models/__init__.py b/report_substitute/models/__init__.py index 27b9defe..a93fca4b 100644 --- a/report_substitute/models/__init__.py +++ b/report_substitute/models/__init__.py @@ -1,2 +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 index 88d7b50b..e8c45b43 100644 --- a/report_substitute/models/ir_actions_report.py +++ b/report_substitute/models/ir_actions_report.py @@ -28,6 +28,18 @@ class IrActionReport(models.Model): return substitution_report_rule.substitution_action_report_id return False + @api.multi + 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_dict(self, action_report_dict, active_ids): if action_report_dict.get('id'): @@ -43,9 +55,20 @@ class IrActionReport(models.Model): @api.multi def render(self, res_ids, data=None): - substitution_report = self._get_substitution_report( - self.model, res_ids - ) - if substitution_report: - return substitution_report.render(res_ids, data) - return super().render(res_ids, data) + substitution_report = self.get_substitution_report(res_ids) + return super(IrActionReport, substitution_report).render(res_ids, data) + + @api.noguess + 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) diff --git a/report_substitute/models/mail_thread.py b/report_substitute/models/mail_thread.py new file mode 100644 index 00000000..7f1a236f --- /dev/null +++ b/report_substitute/models/mail_thread.py @@ -0,0 +1,24 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class MailThread(models.AbstractModel): + + _inherit = 'mail.thread' + + @api.multi + 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/static/src/js/action_manager.js b/report_substitute/static/src/js/action_manager.js index 754dd70d..3f7bc14f 100644 --- a/report_substitute/static/src/js/action_manager.js +++ b/report_substitute/static/src/js/action_manager.js @@ -1,27 +1,29 @@ // Copyright 2019 ACSONE SA/NV // License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -odoo.define('report_substitute.action_report_substitute', function(require) { +odoo.define("report_substitute.action_report_substitute", function (require) { "use strict"; - var ActionManager = require('web.ActionManager'); + var ActionManager = require("web.ActionManager"); ActionManager.include({ + /** * Intercept action handling substitute the report action * @override */ - _handleAction: function(action, options) { - if (action.type === 'ir.actions.report' && + + _handleAction: function (action, options) { + if (action.type === "ir.actions.report" && action.context.active_ids) { var active_ids = action.context.active_ids; var self = this; var _super = this._super; var callersArguments = arguments; return this._rpc({ - model: 'ir.actions.report', - method: 'get_substitution_report_dict', + model: "ir.actions.report", + method: "get_substitution_report_dict", args: [action, active_ids] - }).then(function(action_id) { + }).then(function (action_id) { callersArguments[0] = action_id return _super.apply(self, callersArguments); }); diff --git a/report_substitute/views/ir_actions_report.xml b/report_substitute/views/ir_actions_report.xml index 0c2c265c..c5f9cb14 100644 --- a/report_substitute/views/ir_actions_report.xml +++ b/report_substitute/views/ir_actions_report.xml @@ -16,8 +16,7 @@ - /> + @@ -26,7 +25,8 @@ invisible="1" readonly="1" required="0"/> - + Date: Fri, 26 Jul 2019 16:15:31 +0200 Subject: [PATCH 08/11] [IMP] - add unit tests --- report_substitute/models/ir_actions_report.py | 10 +++--- .../static/src/js/action_manager.js | 12 ++++--- .../tests/test_report_substitute.py | 18 ++++++++++ .../wizards/mail_compose_message.py | 33 ++++++++++--------- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/report_substitute/models/ir_actions_report.py b/report_substitute/models/ir_actions_report.py index e8c45b43..a987b24f 100644 --- a/report_substitute/models/ir_actions_report.py +++ b/report_substitute/models/ir_actions_report.py @@ -41,17 +41,17 @@ class IrActionReport(models.Model): return action_report @api.model - def get_substitution_report_dict(self, action_report_dict, active_ids): - if action_report_dict.get('id'): - action_report = self.browse(action_report_dict['id']) + 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_report_dict.update(action_report.read()[0]) - return action_report_dict + action.update(action_report.read()[0]) + return action @api.multi def render(self, res_ids, data=None): diff --git a/report_substitute/static/src/js/action_manager.js b/report_substitute/static/src/js/action_manager.js index 3f7bc14f..6e4e8f8d 100644 --- a/report_substitute/static/src/js/action_manager.js +++ b/report_substitute/static/src/js/action_manager.js @@ -14,17 +14,19 @@ odoo.define("report_substitute.action_report_substitute", function (require) { _handleAction: function (action, options) { if (action.type === "ir.actions.report" && - action.context.active_ids) { + action.context.active_ids && + action.action_report_substitution_rule_ids && + action.action_report_substitution_rule_ids != 0) { var active_ids = action.context.active_ids; var self = this; var _super = this._super; var callersArguments = arguments; return this._rpc({ model: "ir.actions.report", - method: "get_substitution_report_dict", + method: "get_substitution_report_action", args: [action, active_ids] - }).then(function (action_id) { - callersArguments[0] = action_id + }).then(function (substitution_action) { + callersArguments[0] = substitution_action return _super.apply(self, callersArguments); }); @@ -34,4 +36,4 @@ odoo.define("report_substitute.action_report_substitute", function (require) { }); -}); +}); \ No newline at end of file diff --git a/report_substitute/tests/test_report_substitute.py b/report_substitute/tests/test_report_substitute.py index fecb4be6..75065bd0 100644 --- a/report_substitute/tests/test_report_substitute.py +++ b/report_substitute/tests/test_report_substitute.py @@ -46,3 +46,21 @@ class TestReportSubstitute(TransactionCase): 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, + ) diff --git a/report_substitute/wizards/mail_compose_message.py b/report_substitute/wizards/mail_compose_message.py index c0d37a70..bdb6f844 100644 --- a/report_substitute/wizards/mail_compose_message.py +++ b/report_substitute/wizards/mail_compose_message.py @@ -11,18 +11,21 @@ class MailComposeMessage(models.TransientModel): @api.multi @api.onchange('template_id') def onchange_template_id_wrapper(self): - old_report_template = False - if ( - self.template_id - and self.template_id.report_template - and self.env.context.get('active_ids') - ): - active_ids = self.env.context.get('active_ids') - old_report_template = self.template_id.report_template - self.template_id.report_template = ( - old_report_template.get_substitution_report(active_ids) - ) - res = super().onchange_template_id_wrapper() - if old_report_template: - self.template_id.report_template = old_report_template - return res + if self.template_id: + report_template = self.template_id.report_template + if ( + report_template + and report_template.action_report_substitution_rule_ids + and self.env.context.get('active_ids') + ): + active_ids = self.env.context.get('active_ids') + old_report_template = report_template + self.template_id.report_template = ( + old_report_template.get_substitution_report(active_ids) + ) + onchange_result_with_substituted_report = ( + super().onchange_template_id_wrapper() + ) + self.template_id.report_template = old_report_template + return onchange_result_with_substituted_report + return super().onchange_template_id_wrapper() From 9bf090afa5ffe9cae4b1af07a6d610cda01e9a4c Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Fri, 2 Aug 2019 11:02:35 +0200 Subject: [PATCH 09/11] [FIX] - manage the case where mail.compose.message is triggered from the chatter --- report_substitute/wizards/mail_compose_message.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/report_substitute/wizards/mail_compose_message.py b/report_substitute/wizards/mail_compose_message.py index bdb6f844..d5ab37ff 100644 --- a/report_substitute/wizards/mail_compose_message.py +++ b/report_substitute/wizards/mail_compose_message.py @@ -13,12 +13,16 @@ class MailComposeMessage(models.TransientModel): 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 self.env.context.get('active_ids') + and active_ids ): - active_ids = self.env.context.get('active_ids') old_report_template = report_template self.template_id.report_template = ( old_report_template.get_substitution_report(active_ids) From 103aecbf419d5b114ccea34d162d9e1aab3b724e Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Tue, 1 Oct 2019 15:42:23 +0200 Subject: [PATCH 10/11] [FIX] - Fix typo --- report_substitute/views/ir_actions_report.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/report_substitute/views/ir_actions_report.xml b/report_substitute/views/ir_actions_report.xml index c5f9cb14..e8ab9991 100644 --- a/report_substitute/views/ir_actions_report.xml +++ b/report_substitute/views/ir_actions_report.xml @@ -5,8 +5,7 @@ - ir.actions.report.form (in report_dispatch_base) - + ir.actions.report.form (in report_substitute) ir.actions.report From 2eb9253fcf8fba50fe1e7c50cb9898e060db5544 Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Tue, 1 Oct 2019 23:35:11 +0200 Subject: [PATCH 11/11] [12.0][IMP] - Add check for substitution infinite loop --- report_substitute/__manifest__.py | 2 +- .../ir_actions_report_substitution_rule.py | 23 +++++++++++++++++-- .../tests/test_report_substitute.py | 14 +++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/report_substitute/__manifest__.py b/report_substitute/__manifest__.py index 82cac363..1dab69dc 100644 --- a/report_substitute/__manifest__.py +++ b/report_substitute/__manifest__.py @@ -10,7 +10,7 @@ 'license': 'AGPL-3', 'author': 'ACSONE SA/NV,' 'Odoo Community Association (OCA)', - 'website': 'https://github.com/acsone/reporting-engine', + 'website': 'https://github.com/OCA/reporting-engine', 'depends': ['base', 'mail'], 'data': [ 'security/ir_actions_report_substitution_rule.xml', diff --git a/report_substitute/models/ir_actions_report_substitution_rule.py b/report_substitute/models/ir_actions_report_substitution_rule.py index 4c725071..1f5d936f 100644 --- a/report_substitute/models/ir_actions_report_substitution_rule.py +++ b/report_substitute/models/ir_actions_report_substitution_rule.py @@ -1,7 +1,8 @@ # Copyright 2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import fields, models, api, _ +from odoo.exceptions import ValidationError class ActionsReportSubstitutionRule(models.Model): @@ -24,5 +25,23 @@ class ActionsReportSubstitutionRule(models.Model): string="Substitution Report Action", required=True, ondelete="cascade", - domain="[('model', '=', model)]" + 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/tests/test_report_substitute.py b/report_substitute/tests/test_report_substitute.py index 75065bd0..3a3d2fbd 100644 --- a/report_substitute/tests/test_report_substitute.py +++ b/report_substitute/tests/test_report_substitute.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo.tests.common import TransactionCase +from odoo.exceptions import ValidationError class TestReportSubstitute(TransactionCase): @@ -64,3 +65,16 @@ class TestReportSubstitute(TransactionCase): 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, + } + )