From 60d3513f78a065baf77d9e66081eb99d3db3c561 Mon Sep 17 00:00:00 2001 From: Cyril VINH-TUNG Date: Tue, 26 May 2026 16:01:37 -1000 Subject: [PATCH] [ADD] website_event_company_parent_visibility: This module allows visibility of sub-companies events in parent company's websites - helped by Claude Opus-4.7 --- .../README.rst | 126 +++++ .../__init__.py | 1 + .../__manifest__.py | 20 + .../models/__init__.py | 3 + .../models/event_event.py | 27 + .../models/res_company.py | 13 + .../models/res_config_settings.py | 13 + .../pyproject.toml | 3 + .../readme/CONTRIBUTORS.md | 1 + .../readme/DESCRIPTION.md | 6 + .../readme/INSTALL.md | 8 + .../readme/ROADMAP.md | 6 + .../readme/USAGE.md | 10 + .../security/event_security.xml | 37 ++ .../static/description/index.html | 474 ++++++++++++++++++ .../tests/__init__.py | 1 + ...website_event_company_parent_visibility.py | 263 ++++++++++ .../views/res_config_settings_views.xml | 23 + 18 files changed, 1035 insertions(+) create mode 100644 website_event_company_parent_visibility/README.rst create mode 100644 website_event_company_parent_visibility/__init__.py create mode 100644 website_event_company_parent_visibility/__manifest__.py create mode 100644 website_event_company_parent_visibility/models/__init__.py create mode 100644 website_event_company_parent_visibility/models/event_event.py create mode 100644 website_event_company_parent_visibility/models/res_company.py create mode 100644 website_event_company_parent_visibility/models/res_config_settings.py create mode 100644 website_event_company_parent_visibility/pyproject.toml create mode 100644 website_event_company_parent_visibility/readme/CONTRIBUTORS.md create mode 100644 website_event_company_parent_visibility/readme/DESCRIPTION.md create mode 100644 website_event_company_parent_visibility/readme/INSTALL.md create mode 100644 website_event_company_parent_visibility/readme/ROADMAP.md create mode 100644 website_event_company_parent_visibility/readme/USAGE.md create mode 100644 website_event_company_parent_visibility/security/event_security.xml create mode 100644 website_event_company_parent_visibility/static/description/index.html create mode 100644 website_event_company_parent_visibility/tests/__init__.py create mode 100644 website_event_company_parent_visibility/tests/test_website_event_company_parent_visibility.py create mode 100644 website_event_company_parent_visibility/views/res_config_settings_views.xml diff --git a/website_event_company_parent_visibility/README.rst b/website_event_company_parent_visibility/README.rst new file mode 100644 index 000000000..71f5ae8d9 --- /dev/null +++ b/website_event_company_parent_visibility/README.rst @@ -0,0 +1,126 @@ +======================================= +Website Event Company Parent Visibility +======================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:baa71b9bf1305a2ffb17b2b4d0e4948b55c0ed7b9a998e56a5252c6b1f7fde97 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fevent-lightgray.png?logo=github + :target: https://github.com/OCA/event/tree/18.0/website_event_company_parent_visibility + :alt: OCA/event +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/event-18-0/event-18-0-website_event_company_parent_visibility + :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/event&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Lets events of subsidiary companies be displayed on parent companies' +websites (and to parent companies' backend users), with an opt-in flag +per subsidiary. + +Visibility follows ``res.company.parent_id``. Same pattern as Odoo's own +``product`` module, with the opposite direction (``child_of`` instead of +``parent_of``). + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +Depends on ``website_event`` only. + +The three event ir.rules (``event_event_company_rule``, +``event_registration_company_rule``, +``ir_rule_event_event_ticket_company``) are overridden in place via +``security/event_security.xml``. + +Uninstall does **not** auto-revert these rules. Run ``-u event`` +afterwards to restore the upstream domains. + +Usage +===== + +1. Set ``Parent Company`` on each subsidiary in *Settings → Companies*. +2. In *Settings → Events → Events*, toggle **Share events with parent + companies** for the active company. Default is on; switch the active + company to configure another one. + +Result: + +- The parent's website lists events of the parent + every opted-in + descendant + + - global (no-company) events. + +- A subsidiary's website only lists its own events and global ones. +- Sibling subsidiaries never see each other's events. + +Known issues / Roadmap +====================== + +- Tree only: an event can't be shared between two siblings without going + through their common parent. For arbitrary M2M sharing, use + ``event_multi_company``. +- The opt-in flag does not apply to events with no company (always + visible). +- Backend visibility broadens too: parent company users see opted-in + descendants' events in the backend, not just on the public website. +- ``-u event`` resets the rules to upstream; re-update this module to + re-apply. + +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 +------- + +* INVITU + +Contributors +------------ + +- INVITU (https://www.invitu.com) + +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/event `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/website_event_company_parent_visibility/__init__.py b/website_event_company_parent_visibility/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/website_event_company_parent_visibility/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/website_event_company_parent_visibility/__manifest__.py b/website_event_company_parent_visibility/__manifest__.py new file mode 100644 index 000000000..b645e6bb6 --- /dev/null +++ b/website_event_company_parent_visibility/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2026 INVITU () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Website Event Company Parent Visibility", + "summary": "Subsidiaries' events shown on parent companies' websites " + "(opt-in per subsidiary).", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "author": "INVITU, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/event", + "category": "Marketing", + "depends": ["website_event"], + "data": [ + "security/event_security.xml", + "views/res_config_settings_views.xml", + ], + "installable": True, + "application": False, +} diff --git a/website_event_company_parent_visibility/models/__init__.py b/website_event_company_parent_visibility/models/__init__.py new file mode 100644 index 000000000..2de761b51 --- /dev/null +++ b/website_event_company_parent_visibility/models/__init__.py @@ -0,0 +1,3 @@ +from . import event_event +from . import res_company +from . import res_config_settings diff --git a/website_event_company_parent_visibility/models/event_event.py b/website_event_company_parent_visibility/models/event_event.py new file mode 100644 index 000000000..ecb954ca2 --- /dev/null +++ b/website_event_company_parent_visibility/models/event_event.py @@ -0,0 +1,27 @@ +# Copyright 2026 INVITU () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class EventEvent(models.Model): + _inherit = "event.event" + + def _search_get_detail(self, website, order, options): + detail = super()._search_get_detail(website, order, options) + if website and website.company_id: + company = website.company_id + extra_domain = [ + "|", + ("company_id", "=", False), + "|", + ("company_id", "=", company.id), + "&", + ("company_id", "child_of", company.ids), + ("company_id.share_events_with_parents", "=", True), + ] + detail["base_domain"] = detail.get("base_domain", []) + [extra_domain] + for key in ("no_date_domain", "no_country_domain"): + if key in detail: + detail[key] = detail[key] + [extra_domain] + return detail diff --git a/website_event_company_parent_visibility/models/res_company.py b/website_event_company_parent_visibility/models/res_company.py new file mode 100644 index 000000000..5bda4e586 --- /dev/null +++ b/website_event_company_parent_visibility/models/res_company.py @@ -0,0 +1,13 @@ +# Copyright 2026 INVITU () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + share_events_with_parents = fields.Boolean( + string="Share events with parent companies", + default=True, + ) diff --git a/website_event_company_parent_visibility/models/res_config_settings.py b/website_event_company_parent_visibility/models/res_config_settings.py new file mode 100644 index 000000000..f8d13f02d --- /dev/null +++ b/website_event_company_parent_visibility/models/res_config_settings.py @@ -0,0 +1,13 @@ +# Copyright 2026 INVITU () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + share_events_with_parents = fields.Boolean( + related="company_id.share_events_with_parents", + readonly=False, + ) diff --git a/website_event_company_parent_visibility/pyproject.toml b/website_event_company_parent_visibility/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/website_event_company_parent_visibility/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/website_event_company_parent_visibility/readme/CONTRIBUTORS.md b/website_event_company_parent_visibility/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..0447f1538 --- /dev/null +++ b/website_event_company_parent_visibility/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +* INVITU () diff --git a/website_event_company_parent_visibility/readme/DESCRIPTION.md b/website_event_company_parent_visibility/readme/DESCRIPTION.md new file mode 100644 index 000000000..c52e85828 --- /dev/null +++ b/website_event_company_parent_visibility/readme/DESCRIPTION.md @@ -0,0 +1,6 @@ +Lets events of subsidiary companies be displayed on parent companies' websites +(and to parent companies' backend users), with an opt-in flag per subsidiary. + +Visibility follows ``res.company.parent_id``. Same pattern as Odoo's own +``product`` module, with the opposite direction (``child_of`` instead of +``parent_of``). diff --git a/website_event_company_parent_visibility/readme/INSTALL.md b/website_event_company_parent_visibility/readme/INSTALL.md new file mode 100644 index 000000000..2024186f4 --- /dev/null +++ b/website_event_company_parent_visibility/readme/INSTALL.md @@ -0,0 +1,8 @@ +Depends on ``website_event`` only. + +The three event ir.rules (``event_event_company_rule``, +``event_registration_company_rule``, ``ir_rule_event_event_ticket_company``) +are overridden in place via ``security/event_security.xml``. + +Uninstall does **not** auto-revert these rules. Run ``-u event`` afterwards +to restore the upstream domains. diff --git a/website_event_company_parent_visibility/readme/ROADMAP.md b/website_event_company_parent_visibility/readme/ROADMAP.md new file mode 100644 index 000000000..c6ed60d2f --- /dev/null +++ b/website_event_company_parent_visibility/readme/ROADMAP.md @@ -0,0 +1,6 @@ +* Tree only: an event can't be shared between two siblings without going through + their common parent. For arbitrary M2M sharing, use ``event_multi_company``. +* The opt-in flag does not apply to events with no company (always visible). +* Backend visibility broadens too: parent company users see opted-in descendants' + events in the backend, not just on the public website. +* ``-u event`` resets the rules to upstream; re-update this module to re-apply. diff --git a/website_event_company_parent_visibility/readme/USAGE.md b/website_event_company_parent_visibility/readme/USAGE.md new file mode 100644 index 000000000..906a55589 --- /dev/null +++ b/website_event_company_parent_visibility/readme/USAGE.md @@ -0,0 +1,10 @@ +1. Set ``Parent Company`` on each subsidiary in *Settings → Companies*. +2. In *Settings → Events → Events*, toggle **Share events with parent companies** + for the active company. Default is on; switch the active company to configure + another one. + +Result: +* The parent's website lists events of the parent + every opted-in descendant + + global (no-company) events. +* A subsidiary's website only lists its own events and global ones. +* Sibling subsidiaries never see each other's events. diff --git a/website_event_company_parent_visibility/security/event_security.xml b/website_event_company_parent_visibility/security/event_security.xml new file mode 100644 index 000000000..94e3ee550 --- /dev/null +++ b/website_event_company_parent_visibility/security/event_security.xml @@ -0,0 +1,37 @@ + + + + + Event: multi-company (parent-visibility) + + [ + '|', ('company_id', '=', False), + '|', ('company_id', 'in', company_ids), + '&', ('company_id', 'child_of', company_ids), + ('company_id.share_events_with_parents', '=', True) + ] + + + + Event/Registration: multi-company (parent-visibility) + + [ + '|', ('company_id', '=', False), + '|', ('company_id', 'in', company_ids), + '&', ('company_id', 'child_of', company_ids), + ('company_id.share_events_with_parents', '=', True) + ] + + + + Event/Ticket: multi-company (parent-visibility) + + [ + '|', ('event_id.company_id', '=', False), + '|', ('event_id.company_id', 'in', company_ids), + '&', ('event_id.company_id', 'child_of', company_ids), + ('event_id.company_id.share_events_with_parents', '=', True) + ] + + diff --git a/website_event_company_parent_visibility/static/description/index.html b/website_event_company_parent_visibility/static/description/index.html new file mode 100644 index 000000000..a4b873516 --- /dev/null +++ b/website_event_company_parent_visibility/static/description/index.html @@ -0,0 +1,474 @@ + + + + + +Website Event Company Parent Visibility + + + +
+

Website Event Company Parent Visibility

+ + +

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

+

Lets events of subsidiary companies be displayed on parent companies’ +websites (and to parent companies’ backend users), with an opt-in flag +per subsidiary.

+

Visibility follows res.company.parent_id. Same pattern as Odoo’s own +product module, with the opposite direction (child_of instead of +parent_of).

+

Table of contents

+ +
+

Installation

+

Depends on website_event only.

+

The three event ir.rules (event_event_company_rule, +event_registration_company_rule, +ir_rule_event_event_ticket_company) are overridden in place via +security/event_security.xml.

+

Uninstall does not auto-revert these rules. Run -u event +afterwards to restore the upstream domains.

+
+
+

Usage

+
    +
  1. Set Parent Company on each subsidiary in Settings → Companies.
  2. +
  3. In Settings → Events → Events, toggle Share events with parent +companies for the active company. Default is on; switch the active +company to configure another one.
  4. +
+

Result:

+
    +
  • The parent’s website lists events of the parent + every opted-in +descendant
      +
    • global (no-company) events.
    • +
    +
  • +
  • A subsidiary’s website only lists its own events and global ones.
  • +
  • Sibling subsidiaries never see each other’s events.
  • +
+
+
+

Known issues / Roadmap

+
    +
  • Tree only: an event can’t be shared between two siblings without going +through their common parent. For arbitrary M2M sharing, use +event_multi_company.
  • +
  • The opt-in flag does not apply to events with no company (always +visible).
  • +
  • Backend visibility broadens too: parent company users see opted-in +descendants’ events in the backend, not just on the public website.
  • +
  • -u event resets the rules to upstream; re-update this module to +re-apply.
  • +
+
+
+

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

+
    +
  • INVITU
  • +
+
+ +
+

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/event project on GitHub.

+

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

+
+
+
+ + diff --git a/website_event_company_parent_visibility/tests/__init__.py b/website_event_company_parent_visibility/tests/__init__.py new file mode 100644 index 000000000..bf0e22e1c --- /dev/null +++ b/website_event_company_parent_visibility/tests/__init__.py @@ -0,0 +1 @@ +from . import test_website_event_company_parent_visibility diff --git a/website_event_company_parent_visibility/tests/test_website_event_company_parent_visibility.py b/website_event_company_parent_visibility/tests/test_website_event_company_parent_visibility.py new file mode 100644 index 000000000..6b2ad194d --- /dev/null +++ b/website_event_company_parent_visibility/tests/test_website_event_company_parent_visibility.py @@ -0,0 +1,263 @@ +# Copyright 2026 INVITU () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import timedelta + +from odoo import Command, fields +from odoo.exceptions import AccessError +from odoo.tests import common, tagged + + +@tagged("post_install", "-at_install") +class TestWebsiteEventCompanyParentVisibility(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.event_group = cls.env.ref("event.group_event_manager") + + Company = cls.env["res.company"] + cls.company_hq = Company.create({"name": "HQ"}) + cls.company_sub1 = Company.create( + {"name": "Sub1", "parent_id": cls.company_hq.id} + ) + cls.company_sub2 = Company.create( + {"name": "Sub2", "parent_id": cls.company_hq.id} + ) + (cls.company_hq + cls.company_sub1 + cls.company_sub2).invalidate_recordset( + ["parent_path"] + ) + + cls._create_events() + cls._create_users() + + @classmethod + def _make_event_vals(cls, name, company=None): + today = fields.Datetime.now() + vals = { + "name": name, + "date_begin": today + timedelta(days=10), + "date_end": today + timedelta(days=11), + "date_tz": "UTC", + } + if company is not None: + vals["company_id"] = company.id if company else False + return vals + + @classmethod + def _create_events(cls): + E = cls.env["event.event"] + cls.event_hq = E.with_company(cls.company_hq).create( + cls._make_event_vals("Event HQ", cls.company_hq) + ) + cls.event_sub1 = E.with_company(cls.company_sub1).create( + cls._make_event_vals("Event Sub1", cls.company_sub1) + ) + cls.event_sub2 = E.with_company(cls.company_sub2).create( + cls._make_event_vals("Event Sub2", cls.company_sub2) + ) + cls.event_global = E.create(cls._make_event_vals("Event global", company=False)) + + @classmethod + def _make_user(cls, login, company): + return cls.env["res.users"].create( + { + "name": login, + "login": login, + "groups_id": [Command.set(cls.event_group.ids)], + "company_id": company.id, + "company_ids": [Command.set(company.ids)], + } + ) + + @classmethod + def _create_users(cls): + cls.user_hq = cls._make_user("event_user_hq", cls.company_hq) + cls.user_sub1 = cls._make_user("event_user_sub1", cls.company_sub1) + cls.user_sub2 = cls._make_user("event_user_sub2", cls.company_sub2) + + def test_share_default_true(self): + self.assertTrue(self.company_sub1.share_events_with_parents) + self.assertTrue(self.company_sub2.share_events_with_parents) + + def test_hq_user_sees_descendants_when_opted_in(self): + visible = ( + self.env["event.event"] + .with_user(self.user_hq) + .search( + [("id", "in", (self.event_hq + self.event_sub1 + self.event_sub2).ids)] + ) + ) + self.assertEqual(visible, self.event_hq + self.event_sub1 + self.event_sub2) + + def test_hq_user_does_not_see_opted_out_descendant(self): + self.company_sub1.share_events_with_parents = False + visible = ( + self.env["event.event"] + .with_user(self.user_hq) + .search( + [("id", "in", (self.event_hq + self.event_sub1 + self.event_sub2).ids)] + ) + ) + self.assertEqual(visible, self.event_hq + self.event_sub2) + with self.assertRaises(AccessError): + self.event_sub1.with_user(self.user_hq).name = "Bad" + + def test_sub1_user_always_sees_own_events_regardless_of_flag(self): + self.company_sub1.share_events_with_parents = False + visible = ( + self.env["event.event"] + .with_user(self.user_sub1) + .search([("id", "=", self.event_sub1.id)]) + ) + self.assertEqual(visible, self.event_sub1) + + def test_sub1_user_does_not_see_hq_or_sibling(self): + visible = ( + self.env["event.event"] + .with_user(self.user_sub1) + .search( + [("id", "in", (self.event_hq + self.event_sub1 + self.event_sub2).ids)] + ) + ) + self.assertEqual(visible, self.event_sub1) + with self.assertRaises(AccessError): + self.event_hq.with_user(self.user_sub1).name = "Bad" + with self.assertRaises(AccessError): + self.event_sub2.with_user(self.user_sub1).name = "Bad" + + def test_global_event_visible_to_all(self): + for user in (self.user_hq, self.user_sub1, self.user_sub2): + visible = ( + self.env["event.event"] + .with_user(user) + .search([("id", "=", self.event_global.id)]) + ) + self.assertEqual(visible, self.event_global) + + def _website_visible_event_ids(self, website): + clause = [ + "|", + ("company_id", "=", False), + "|", + ("company_id", "=", website.company_id.id), + "&", + ("company_id", "child_of", website.company_id.ids), + ("company_id.share_events_with_parents", "=", True), + ] + ids = self.env["event.event"].search(clause).ids + relevant = self.event_hq + self.event_sub1 + self.event_sub2 + self.event_global + return [eid for eid in ids if eid in relevant.ids] + + def test_website_hq_shows_descendants_when_opted_in(self): + site_hq = self.env["website"].create( + {"name": "Site HQ", "company_id": self.company_hq.id} + ) + self.assertEqual( + set(self._website_visible_event_ids(site_hq)), + { + self.event_hq.id, + self.event_sub1.id, + self.event_sub2.id, + self.event_global.id, + }, + ) + + def test_website_hq_hides_opted_out_descendant(self): + self.company_sub1.share_events_with_parents = False + site_hq = self.env["website"].create( + {"name": "Site HQ", "company_id": self.company_hq.id} + ) + self.assertEqual( + set(self._website_visible_event_ids(site_hq)), + {self.event_hq.id, self.event_sub2.id, self.event_global.id}, + ) + + def test_website_sub1_keeps_seeing_own_events_when_opted_out(self): + self.company_sub1.share_events_with_parents = False + site_sub1 = self.env["website"].create( + {"name": "Site Sub1", "company_id": self.company_sub1.id} + ) + visible = set(self._website_visible_event_ids(site_sub1)) + self.assertIn(self.event_sub1.id, visible) + self.assertIn(self.event_global.id, visible) + self.assertNotIn(self.event_hq.id, visible) + self.assertNotIn(self.event_sub2.id, visible) + + def test_website_sub1_does_not_show_sibling(self): + site_sub1 = self.env["website"].create( + {"name": "Site Sub1", "company_id": self.company_sub1.id} + ) + self.assertNotIn( + self.event_sub2.id, set(self._website_visible_event_ids(site_sub1)) + ) + + def test_search_get_detail_injects_clause(self): + site_hq = self.env["website"].create( + {"name": "Site HQ detail", "company_id": self.company_hq.id} + ) + detail = self.env["event.event"]._search_get_detail( + website=site_hq, + order="date_begin", + options={"displayDescription": False, "displayDetail": False}, + ) + expected_clause = [ + "|", + ("company_id", "=", False), + "|", + ("company_id", "=", self.company_hq.id), + "&", + ("company_id", "child_of", self.company_hq.ids), + ("company_id.share_events_with_parents", "=", True), + ] + self.assertTrue(any(expected_clause == sub for sub in detail["base_domain"])) + + def test_res_config_settings_related_field(self): + Settings = self.env["res.config.settings"] + Settings.with_company(self.company_hq).create( + {"share_events_with_parents": False} + ).execute() + self.assertFalse(self.company_hq.share_events_with_parents) + Settings.with_company(self.company_hq).create( + {"share_events_with_parents": True} + ).execute() + self.assertTrue(self.company_hq.share_events_with_parents) + + def test_registration_rule_follows_hierarchy_and_opt_in(self): + reg = self.env["event.registration"].create( + { + "event_id": self.event_sub1.id, + "name": "Test attendee", + "company_id": self.company_sub1.id, + } + ) + visible = ( + self.env["event.registration"] + .with_user(self.user_hq) + .search([("id", "=", reg.id)]) + ) + self.assertEqual(visible, reg) + self.company_sub1.share_events_with_parents = False + visible = ( + self.env["event.registration"] + .with_user(self.user_hq) + .search([("id", "=", reg.id)]) + ) + self.assertFalse(visible) + self.company_sub1.share_events_with_parents = True + visible = ( + self.env["event.registration"] + .with_user(self.user_sub2) + .search([("id", "=", reg.id)]) + ) + self.assertFalse(visible) + + def test_rule_domain_contains_share_check(self): + for xml_id in ( + "event.event_event_company_rule", + "event.event_registration_company_rule", + "event.ir_rule_event_event_ticket_company", + ): + rule = self.env.ref(xml_id) + self.assertIn("share_events_with_parents", rule.domain_force or "") + self.assertIn("child_of", rule.domain_force or "") diff --git a/website_event_company_parent_visibility/views/res_config_settings_views.xml b/website_event_company_parent_visibility/views/res_config_settings_views.xml new file mode 100644 index 000000000..b0f93f416 --- /dev/null +++ b/website_event_company_parent_visibility/views/res_config_settings_views.xml @@ -0,0 +1,23 @@ + + + + res.config.settings.view.form.inherit.website_event_company_parent_visibility + res.config.settings + + + + + + + + + +