diff --git a/base_preset_param/README.rst b/base_preset_param/README.rst new file mode 100644 index 0000000000..be4b2c059c --- /dev/null +++ b/base_preset_param/README.rst @@ -0,0 +1,133 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================= +Base Preset Param +================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:257bddc3f6dfccea7700af174ca5d4d234807c30ddf3e10eeeb9e24e9d4d4a03 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/14.0/base_preset_param + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-14-0/social-14-0-base_preset_param + :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/social&target_branch=14.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Many Odoo modules retrieve system parameter information embedded in +other functions that make it difficult to adapt the place information is +retrieved from. + +A point in case is the configuration of Client ID and Client Secret +Value, where both the fetchmail_outlook module and the microsoft_outlook +module just assume that an Odoo database can only be connected to one +Microsoft profile, and therefore have the information in a system +parameter. + +This module makes it possible to preset the value to be retrieved from a +system parameter in the context, making no assumptions of where the +alternative values will come from, allowing other modules to override +the standard methods. + +How to use +========== + +Suppose there is some Odoo method that has retrieving system parameter +information without using an overridable function for this: + +:: + + def _compute_outlook_uri(self): + Config = self.env['ir.config_parameter'].sudo() + microsoft_outlook_client_id = Config.get_param("microsoft_outlook_client_id") + ... + for record in self: + # More code + +However we have multiple connections with each their own value for +client id. + +With help of this module we can do: + +:: + + class IrMailServer(models.Model): + _inherit = "ir.mail_server" + + microsoft_outlook_client_identifier = fields.Char( + "Outlook Client Id", + help="Specific client_id for this server", + ) + ..... + def _compute_outlook_uri(self): + self.ensure_one() + preset_self = self.with_context( + preset_microsoft_outlook_client_id=self.microsoft_outlook_client_identifier, + ) + uri = super(IrMailServer, preset_self)._compute_outlook_uri() + .... + +**Table of contents** + +.. contents:: + :local: + +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 +------- + +* Therp BV + +Contributors +------------ + +- ``Therp BV ``\ \_: + + - Ronald Portier + +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/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_preset_param/__init__.py b/base_preset_param/__init__.py new file mode 100644 index 0000000000..83e553ac46 --- /dev/null +++ b/base_preset_param/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import models diff --git a/base_preset_param/__manifest__.py b/base_preset_param/__manifest__.py new file mode 100644 index 0000000000..9828a27f6d --- /dev/null +++ b/base_preset_param/__manifest__.py @@ -0,0 +1,14 @@ +# Copyright 2026 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Base Preset Param", + "version": "14.0.1.0.0", + "author": "Therp BV, Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Tools", + "website": "https://github.com/OCA/social", + "depends": [ + "base", + ], + "installable": True, +} diff --git a/base_preset_param/models/__init__.py b/base_preset_param/models/__init__.py new file mode 100644 index 0000000000..a9fd6e096d --- /dev/null +++ b/base_preset_param/models/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import ir_config_parameter diff --git a/base_preset_param/models/ir_config_parameter.py b/base_preset_param/models/ir_config_parameter.py new file mode 100644 index 0000000000..4dc953084d --- /dev/null +++ b/base_preset_param/models/ir_config_parameter.py @@ -0,0 +1,29 @@ +# Copyright 2026 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +"""Patch get_param for microsoft client_id and client_secret. + +Might actually be used for any system parameter. + +To avoid having to re-implement a lot of the standard functions that assume +client_id and client_secret are defined in ir.config_parameter, we will patch get_param +to return these values if they have already been set in the context. +""" +from odoo import api, models + + +class IrConfigParameter(models.Model): + _inherit = "ir.config_parameter" + + @api.model + def get_param(self, key, default=False): + """Retrieve the value for a given key. + + Override to return value from context, if set there. This is to help + modules to override functions that expect a value from a system parameter + but already have the value set in some other way. + """ + preset_key = f"preset_{key}" + preset_value = self.env.context.get(preset_key, False) + if preset_value: + return preset_value + return super().get_param(key, default=default) diff --git a/base_preset_param/readme/CONTRIBUTORS.md b/base_preset_param/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..9527288e83 --- /dev/null +++ b/base_preset_param/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +* `Therp BV `_: + + * Ronald Portier diff --git a/base_preset_param/readme/DESCRIPTION.md b/base_preset_param/readme/DESCRIPTION.md new file mode 100644 index 0000000000..439ee78b15 --- /dev/null +++ b/base_preset_param/readme/DESCRIPTION.md @@ -0,0 +1,49 @@ +Many Odoo modules retrieve system parameter information embedded in other +functions that make it difficult to adapt the place information is retrieved +from. + +A point in case is the configuration of Client ID and Client Secret Value, where +both the fetchmail_outlook module and the microsoft_outlook module just assume +that an Odoo database can only be connected to one Microsoft profile, and therefore +have the information in a system parameter. + +This module makes it possible to preset the value to be retrieved from a system +parameter in the context, making no assumptions of where the alternative values +will come from, allowing other modules to override the standard methods. + +How to use +========== + +Suppose there is some Odoo method that has retrieving system parameter information +without using an overridable function for this: + +``` + def _compute_outlook_uri(self): + Config = self.env['ir.config_parameter'].sudo() + microsoft_outlook_client_id = Config.get_param("microsoft_outlook_client_id") + ... + for record in self: + # More code +``` + +However we have multiple connections with each their own value for client id. + +With help of this module we can do: + +``` +class IrMailServer(models.Model): + _inherit = "ir.mail_server" + + microsoft_outlook_client_identifier = fields.Char( + "Outlook Client Id", + help="Specific client_id for this server", + ) + ..... + def _compute_outlook_uri(self): + self.ensure_one() + preset_self = self.with_context( + preset_microsoft_outlook_client_id=self.microsoft_outlook_client_identifier, + ) + uri = super(IrMailServer, preset_self)._compute_outlook_uri() + .... +``` diff --git a/base_preset_param/static/description/icon.png b/base_preset_param/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/base_preset_param/static/description/icon.png differ diff --git a/base_preset_param/static/description/index.html b/base_preset_param/static/description/index.html new file mode 100644 index 0000000000..d509c6cffa --- /dev/null +++ b/base_preset_param/static/description/index.html @@ -0,0 +1,460 @@ + + + + + +README.rst + + + +
+ + +Odoo Community Association +
+

Base Preset Param

+ +

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

+

Many Odoo modules retrieve system parameter information embedded in +other functions that make it difficult to adapt the place information is +retrieved from.

+

A point in case is the configuration of Client ID and Client Secret +Value, where both the fetchmail_outlook module and the microsoft_outlook +module just assume that an Odoo database can only be connected to one +Microsoft profile, and therefore have the information in a system +parameter.

+

This module makes it possible to preset the value to be retrieved from a +system parameter in the context, making no assumptions of where the +alternative values will come from, allowing other modules to override +the standard methods.

+
+

How to use

+

Suppose there is some Odoo method that has retrieving system parameter +information without using an overridable function for this:

+
+def _compute_outlook_uri(self):
+    Config = self.env['ir.config_parameter'].sudo()
+    microsoft_outlook_client_id = Config.get_param("microsoft_outlook_client_id")
+    ...
+    for record in self:
+       # More code
+
+

However we have multiple connections with each their own value for +client id.

+

With help of this module we can do:

+
+class IrMailServer(models.Model):
+    _inherit = "ir.mail_server"
+
+    microsoft_outlook_client_identifier = fields.Char(
+        "Outlook Client Id",
+        help="Specific client_id for this server",
+    )
+    .....
+    def _compute_outlook_uri(self):
+        self.ensure_one()
+        preset_self = self.with_context(
+            preset_microsoft_outlook_client_id=self.microsoft_outlook_client_identifier,
+        )
+        uri = super(IrMailServer, preset_self)._compute_outlook_uri()
+        ....
+
+

Table of contents

+
+
+

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

+
    +
  • Therp BV
  • +
+
+
+

Contributors

+
    +
  • Therp BV <https://therp.nl>_:
      +
    • Ronald Portier
    • +
    +
  • +
+
+
+

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

+

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

+
+
+
+
+ + diff --git a/base_preset_param/tests/__init__.py b/base_preset_param/tests/__init__.py new file mode 100644 index 0000000000..e149cff82e --- /dev/null +++ b/base_preset_param/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from . import test_ir_config_parameter diff --git a/base_preset_param/tests/test_ir_config_parameter.py b/base_preset_param/tests/test_ir_config_parameter.py new file mode 100644 index 0000000000..d5c24691ac --- /dev/null +++ b/base_preset_param/tests/test_ir_config_parameter.py @@ -0,0 +1,17 @@ +# Copyright 2026 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo.tests.common import SavepointCase + + +class TestIrConfigParameter(SavepointCase): + def test_override_get_param(self): + PARAM = "microsoft_outlook_client_identifier" + ICP = self.env["ir.config_parameter"].sudo() + ICP.set_param(PARAM, "test_client_id") + no_preset_value = ICP.get_param(PARAM) + self.assertEqual(no_preset_value, "test_client_id") + preset_value = ICP.with_context( + preset_microsoft_outlook_client_identifier="another_client_id", + ).get_param(PARAM) + self.assertEqual(preset_value, "another_client_id") diff --git a/microsoft_outlook_multi_client/README.rst b/microsoft_outlook_multi_client/README.rst new file mode 100644 index 0000000000..929472840f --- /dev/null +++ b/microsoft_outlook_multi_client/README.rst @@ -0,0 +1,97 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================================= +Microsoft Outlook Multiple CLient +================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:be7d5ec85fb784ac58326e6dd4074d751f779676dfd9f05fe045435b71399353 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/14.0/microsoft_outlook_multi_client + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-14-0/social-14-0-microsoft_outlook_multi_client + :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/social&target_branch=14.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +By default Odoo only supports a single Outlook connection in the +outgoing mail servers, because the connection information is set +in the system parameters, instead of on the mail server itself. + +This module allows you to configure a mail server with the +connection info on the server itself, thereby enabling to +use a multitude of Outlook accounts in multiple domains. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +* Follow the normal Odoo configuration on how to get a client ID and client Secret + on Microsoft Azure for the Outlook client; +* Create a new mail server, set it to use Outlook, instead of using + the buttons to define system parameters, fill in the ID and secret + directly. + +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 +~~~~~~~ + +* Therp BV + +Contributors +~~~~~~~~~~~~ + +* `Therp BV `_: + + * Ronald Portier + +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/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/microsoft_outlook_multi_client/__init__.py b/microsoft_outlook_multi_client/__init__.py new file mode 100644 index 0000000000..83e553ac46 --- /dev/null +++ b/microsoft_outlook_multi_client/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import models diff --git a/microsoft_outlook_multi_client/__manifest__.py b/microsoft_outlook_multi_client/__manifest__.py new file mode 100644 index 0000000000..894acac40f --- /dev/null +++ b/microsoft_outlook_multi_client/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2026 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Microsoft Outlook Multiple CLient", + "version": "14.0.1.0.0", + "author": "Therp BV, Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Tools", + "website": "https://github.com/OCA/social", + "depends": [ + "microsoft_outlook", + "base_preset_param", + ], + "data": ["views/ir_mail_server_views.xml"], + "installable": True, +} diff --git a/microsoft_outlook_multi_client/models/__init__.py b/microsoft_outlook_multi_client/models/__init__.py new file mode 100644 index 0000000000..a78adaf1bd --- /dev/null +++ b/microsoft_outlook_multi_client/models/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import microsoft_outlook_mixin +from . import ir_mail_server diff --git a/microsoft_outlook_multi_client/models/ir_mail_server.py b/microsoft_outlook_multi_client/models/ir_mail_server.py new file mode 100644 index 0000000000..7096ee029e --- /dev/null +++ b/microsoft_outlook_multi_client/models/ir_mail_server.py @@ -0,0 +1,15 @@ +# Copyright 2026 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import models + + +class IrMailServer(models.Model): + # Inherit mixin again to get enhanced version. + _name = "ir.mail_server" + _inherit = ["ir.mail_server", "microsoft.outlook.mixin"] + + def _smtp_login(self, connection, smtp_user, smtp_password): + record = self._get_preset_record() if self._has_id_and_secret() else self + return super(IrMailServer, record)._smtp_login( + connection, smtp_user, smtp_password + ) diff --git a/microsoft_outlook_multi_client/models/microsoft_outlook_mixin.py b/microsoft_outlook_multi_client/models/microsoft_outlook_mixin.py new file mode 100644 index 0000000000..c94944ab5f --- /dev/null +++ b/microsoft_outlook_multi_client/models/microsoft_outlook_mixin.py @@ -0,0 +1,96 @@ +# Copyright 2026 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +"""We override the standard functionality that only supports one Outlook connection. + +To support each outgoing or incoming mail server to have its own connection to Outlook, +we provide for the connection parameters to be part of the server definition itself. + +If there is no connection information in the server record, we will still fall back to +the default configuration using system parameters. +""" + +from odoo import api, fields, models + + +class MicrosoftOutlookMixin(models.AbstractModel): + """Extend standard mixin, that will also be used for fetchmail.""" + + _inherit = "microsoft.outlook.mixin" + + microsoft_outlook_client_identifier = fields.Char( + "Outlook Client Id", + help="Specific client_id for this server", + ) + microsoft_outlook_client_secret = fields.Char( + "Outlook Client Secret", + help="Specific client_secret for this server", + ) + + @api.depends( + "use_microsoft_outlook_service", + "microsoft_outlook_client_identifier", + "microsoft_outlook_client_secret", + ) + def _compute_is_microsoft_outlook_configured(self): + """Check using this record, else fallback to default. + + The field microsoft_outlook_configured should be set to + true when client identifier and secret have been set. The + values for these fields must be given by the person who + registered the Odoo tenant in the Azure directory. + """ + # Cannot depend on trick to override get_param as original + # method might fail on Singleton error. + for this in self: + if not this.use_microsoft_outlook_service: + # The super method also returns True for any record, whether configured + # to use Outlook or not, if system parameters for outlook have been set. + # This is pure madness, so we override this. + self.is_microsoft_outlook_configured = False + elif this._has_id_and_secret(): + self.is_microsoft_outlook_configured = True + else: + super( + MicrosoftOutlookMixin, this + )._compute_is_microsoft_outlook_configured() + + @api.depends( + "use_microsoft_outlook_service", + "microsoft_outlook_client_identifier", + "microsoft_outlook_client_secret", + ) + def _compute_outlook_uri(self): + """The outlook_uri contains the information that is needed activate the client. + + After client id and secret have been set, the user must click on the + "Connect Your Outlook account" button. This will open a Microsoft + webpage containing the needed information. Microsoft will then + generate an authorization token and call back a controller that sets + this token, and a refresh token and expiration on the server record. + """ + record = self._get_preset_record() if self._has_id_and_secret() else self + return super(MicrosoftOutlookMixin, record)._compute_outlook_uri() + + def _fetch_outlook_refresh_token(self, authorization_code): + record = self._get_preset_record() if self._has_id_and_secret() else self + return super(MicrosoftOutlookMixin, record)._fetch_outlook_refresh_token( + authorization_code + ) + + def _has_id_and_secret(self): + """Check whether called on record with specific client ID and Secret.""" + if ( + len(self) == 1 + and self.microsoft_outlook_client_identifier + and self.microsoft_outlook_client_secret + ): + return True + return False + + def _get_preset_record(self): + """Return record with context filled with preset client id and secret.""" + self.ensure_one() + return self.with_context( + preset_microsoft_outlook_client_id=self.microsoft_outlook_client_identifier, + preset_microsoft_outlook_client_secret=self.microsoft_outlook_client_secret, + ) diff --git a/microsoft_outlook_multi_client/readme/CONFIGURE.rst b/microsoft_outlook_multi_client/readme/CONFIGURE.rst new file mode 100644 index 0000000000..e7215af9b7 --- /dev/null +++ b/microsoft_outlook_multi_client/readme/CONFIGURE.rst @@ -0,0 +1,5 @@ +* Follow the normal Odoo configuration on how to get a client ID and client Secret + on Microsoft Azure for the Outlook client; +* Create a new mail server, set it to use Outlook, instead of using + the buttons to define system parameters, fill in the ID and secret + directly. diff --git a/microsoft_outlook_multi_client/readme/CONTRIBUTORS.rst b/microsoft_outlook_multi_client/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..9527288e83 --- /dev/null +++ b/microsoft_outlook_multi_client/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Therp BV `_: + + * Ronald Portier diff --git a/microsoft_outlook_multi_client/readme/DESCRIPTION.rst b/microsoft_outlook_multi_client/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..2287262d85 --- /dev/null +++ b/microsoft_outlook_multi_client/readme/DESCRIPTION.rst @@ -0,0 +1,7 @@ +By default Odoo only supports a single Outlook connection in the +outgoing mail servers, because the connection information is set +in the system parameters, instead of on the mail server itself. + +This module allows you to configure a mail server with the +connection info on the server itself, thereby enabling to +use a multitude of Outlook accounts in multiple domains. diff --git a/microsoft_outlook_multi_client/static/description/icon.png b/microsoft_outlook_multi_client/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/microsoft_outlook_multi_client/static/description/icon.png differ diff --git a/microsoft_outlook_multi_client/static/description/index.html b/microsoft_outlook_multi_client/static/description/index.html new file mode 100644 index 0000000000..c22019f531 --- /dev/null +++ b/microsoft_outlook_multi_client/static/description/index.html @@ -0,0 +1,448 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Microsoft Outlook Multiple CLient

+ +

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

+

By default Odoo only supports a single Outlook connection in the +outgoing mail servers, because the connection information is set +in the system parameters, instead of on the mail server itself.

+

This module allows you to configure a mail server with the +connection info on the server itself, thereby enabling to +use a multitude of Outlook accounts in multiple domains.

+

Table of contents

+ +
+

Configuration

+
    +
  • Follow the normal Odoo configuration on how to get a client ID and client Secret +on Microsoft Azure for the Outlook client;
  • +
  • Create a new mail server, set it to use Outlook, instead of using +the buttons to define system parameters, fill in the ID and secret +directly.
  • +
+
+
+

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

+
    +
  • Therp BV
  • +
+
+
+

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

+

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

+
+
+
+
+ + diff --git a/microsoft_outlook_multi_client/tests/__init__.py b/microsoft_outlook_multi_client/tests/__init__.py new file mode 100644 index 0000000000..6f51195a2a --- /dev/null +++ b/microsoft_outlook_multi_client/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from . import test_ir_mail_server diff --git a/microsoft_outlook_multi_client/tests/test_ir_mail_server.py b/microsoft_outlook_multi_client/tests/test_ir_mail_server.py new file mode 100644 index 0000000000..992bbacd77 --- /dev/null +++ b/microsoft_outlook_multi_client/tests/test_ir_mail_server.py @@ -0,0 +1,91 @@ +# Copyright 2026 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +from mock import MagicMock, patch + +from odoo.tests.common import SavepointCase + +OUTLOOK_MIXIN = "odoo.addons.microsoft_outlook.models.microsoft_outlook_mixin" + + +class TestIrMailServer(SavepointCase): + @classmethod + def setUpClass(cls): + super(TestIrMailServer, cls).setUpClass() + cls.MailServer = cls.env["ir.mail_server"] + cls.outlook_server = cls.MailServer.create( + { + "name": "Test Outlook Server", + "smtp_host": "smtp.outlook.com", + "smtp_port": 587, + "smtp_encryption": "starttls", + "smtp_user": "user1@somemail.com", + "use_microsoft_outlook_service": True, + "microsoft_outlook_client_identifier": "test_client_id", + "microsoft_outlook_client_secret": "test_secret", + } + ) + + def test_multi_outlook_configured(self): + # Now test the outlook server, with client id and secret on the server record. + self.assertTrue(self.outlook_server.is_microsoft_outlook_configured) + self.assertIn( + self.outlook_server.microsoft_outlook_client_identifier, + self.outlook_server.microsoft_outlook_uri, + ) + + def test_outlook_not_configured(self): + # Server not using outlook should not have outlook configured. + example_server = self.MailServer.create( + { + "name": "Test Outlook Server", + "smtp_host": "smtp.example.com", + "smtp_port": 587, + "smtp_encryption": "starttls", + "smtp_user": "user1@example.com", + "use_microsoft_outlook_service": False, + "microsoft_outlook_client_identifier": False, + "microsoft_outlook_client_secret": False, + } + ) + self.assertFalse(example_server.is_microsoft_outlook_configured) + self.assertFalse(example_server.microsoft_outlook_uri) + + def test_system_outlook_configured(self): + # Now test an outlook server, getting client id and secret from system parms. + self.env["res.config.settings"].create( + { + "microsoft_outlook_client_identifier": "test_system_client_id", + "microsoft_outlook_client_secret": "test_system_secret", + } + ).set_values() + system_outlook_server = self.MailServer.create( + { + "name": "Test System Outlook Server", + "smtp_host": "smtp.outlook.com", + "smtp_port": 587, + "smtp_encryption": "starttls", + "smtp_user": "user1@example.com", + "use_microsoft_outlook_service": True, + "microsoft_outlook_client_identifier": False, + "microsoft_outlook_client_secret": False, + } + ) + self.assertTrue(system_outlook_server.is_microsoft_outlook_configured) + self.assertIn( + "test_system_client_id", + system_outlook_server.microsoft_outlook_uri, + ) + + @patch(OUTLOOK_MIXIN + ".requests") + def test_fetch_outlook_refresh_token(self, mock_request): + # mock the response + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = { + "refresh_token": "abc", + "access_token": "xyz", + "expires_in": 90, + } + mock_request.post.return_value = mock_response + result = self.outlook_server._fetch_outlook_refresh_token("dummy_code") + self.assertEqual(result[0], "abc") diff --git a/microsoft_outlook_multi_client/views/ir_mail_server_views.xml b/microsoft_outlook_multi_client/views/ir_mail_server_views.xml new file mode 100644 index 0000000000..2261b9057e --- /dev/null +++ b/microsoft_outlook_multi_client/views/ir_mail_server_views.xml @@ -0,0 +1,40 @@ + + + + ir.mail_server.view.form - microsoft_outlook_multi_client + ir.mail_server + + + + client_id and client_secret for this mail server +
+
+
+
+
+
+
+
diff --git a/setup/base_preset_param/odoo/addons/base_preset_param b/setup/base_preset_param/odoo/addons/base_preset_param new file mode 120000 index 0000000000..1b4babd15a --- /dev/null +++ b/setup/base_preset_param/odoo/addons/base_preset_param @@ -0,0 +1 @@ +../../../../base_preset_param \ No newline at end of file diff --git a/setup/base_preset_param/setup.py b/setup/base_preset_param/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/base_preset_param/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/microsoft_outlook_multi_client/odoo/addons/microsoft_outlook_multi_client b/setup/microsoft_outlook_multi_client/odoo/addons/microsoft_outlook_multi_client new file mode 120000 index 0000000000..2df41c40b4 --- /dev/null +++ b/setup/microsoft_outlook_multi_client/odoo/addons/microsoft_outlook_multi_client @@ -0,0 +1 @@ +../../../../microsoft_outlook_multi_client \ No newline at end of file diff --git a/setup/microsoft_outlook_multi_client/setup.py b/setup/microsoft_outlook_multi_client/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/microsoft_outlook_multi_client/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)