From 0bcede4a35361035205d74fc5fafac6861177688 Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Fri, 18 Oct 2024 21:51:12 +0200 Subject: [PATCH] [ADD] shopinvader_fastapi_auth_partner_api_unit_member --- ...vader_fastapi_auth_partner_api_unit_member | 1 + .../setup.py | 6 + .../README.rst | 67 +++ .../__init__.py | 3 + .../__manifest__.py | 24 + .../data/email_data.xml | 27 ++ .../models/__init__.py | 2 + .../models/auth_directory.py | 19 + .../models/res_partner.py | 88 ++++ .../readme/CONTRIBUTORS.rst | 3 + .../readme/DESCRIPTION.rst | 6 + .../routers/__init__.py | 1 + .../routers/unit_members.py | 30 ++ .../schemas.py | 23 + .../static/description/index.html | 424 ++++++++++++++++++ .../tests/__init__.py | 1 + .../test_shopinvader_api_unit_members.py | 266 +++++++++++ .../views/auth_directory_view.xml | 14 + 18 files changed, 1005 insertions(+) create mode 120000 setup/shopinvader_fastapi_auth_partner_api_unit_member/odoo/addons/shopinvader_fastapi_auth_partner_api_unit_member create mode 100644 setup/shopinvader_fastapi_auth_partner_api_unit_member/setup.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/README.rst create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/__init__.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/__manifest__.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/data/email_data.xml create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/models/__init__.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/models/auth_directory.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/models/res_partner.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/readme/CONTRIBUTORS.rst create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/readme/DESCRIPTION.rst create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/routers/__init__.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/routers/unit_members.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/schemas.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/static/description/index.html create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/tests/__init__.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/tests/test_shopinvader_api_unit_members.py create mode 100644 shopinvader_fastapi_auth_partner_api_unit_member/views/auth_directory_view.xml diff --git a/setup/shopinvader_fastapi_auth_partner_api_unit_member/odoo/addons/shopinvader_fastapi_auth_partner_api_unit_member b/setup/shopinvader_fastapi_auth_partner_api_unit_member/odoo/addons/shopinvader_fastapi_auth_partner_api_unit_member new file mode 120000 index 0000000000..1d556c8fbe --- /dev/null +++ b/setup/shopinvader_fastapi_auth_partner_api_unit_member/odoo/addons/shopinvader_fastapi_auth_partner_api_unit_member @@ -0,0 +1 @@ +../../../../shopinvader_fastapi_auth_partner_api_unit_member \ No newline at end of file diff --git a/setup/shopinvader_fastapi_auth_partner_api_unit_member/setup.py b/setup/shopinvader_fastapi_auth_partner_api_unit_member/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/shopinvader_fastapi_auth_partner_api_unit_member/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/README.rst b/shopinvader_fastapi_auth_partner_api_unit_member/README.rst new file mode 100644 index 0000000000..33723bfef6 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/README.rst @@ -0,0 +1,67 @@ +================================================ +Shopinvader Fastapi Auth Partner Api Unit Member +================================================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:46bd8663388a1f03209619852bf7bec6c34a32457d19e688254400b19449d058 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-shopinvader%2Fodoo--shopinvader-lightgray.png?logo=github + :target: https://github.com/shopinvader/odoo-shopinvader/tree/16.0/shopinvader_fastapi_auth_partner_api_unit_member + :alt: shopinvader/odoo-shopinvader + +|badge1| |badge2| |badge3| + +This module glues the `shopinvader_fastapi_auth_partner_api` and the `shopinvader_api_unit_member` modules. + +It adds a auth_state field to the unit member and defines this member route: + +- `POST /unit/members/:id/invite` to add an auth.partner to the unit member allowing it to sign in and send an invite email to it. + + +**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 +~~~~~~~ + +* Akretion + +Contributors +~~~~~~~~~~~~ + +* `Akretion `_: + + * Florian Mounier + +Maintainers +~~~~~~~~~~~ + +This module is part of the `shopinvader/odoo-shopinvader `_ project on GitHub. + +You are welcome to contribute. diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/__init__.py b/shopinvader_fastapi_auth_partner_api_unit_member/__init__.py new file mode 100644 index 0000000000..797e7dcb45 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import routers +from . import schemas diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/__manifest__.py b/shopinvader_fastapi_auth_partner_api_unit_member/__manifest__.py new file mode 100644 index 0000000000..ba8ce2b372 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/__manifest__.py @@ -0,0 +1,24 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Shopinvader Fastapi Auth Partner Api Unit Member", + "summary": "This module glues the shopinvader unit member management with " + "the fastapi auth partner api adding an auth state on members and an invite " + "route.", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "author": "Akretion", + "website": "https://github.com/shopinvader/odoo-shopinvader", + "depends": [ + "shopinvader_api_unit_member", + "shopinvader_fastapi_auth_partner", + ], + "data": [ + "data/email_data.xml", + "views/auth_directory_view.xml", + ], + "auto_install": True, + "installable": True, +} diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/data/email_data.xml b/shopinvader_fastapi_auth_partner_api_unit_member/data/email_data.xml new file mode 100644 index 0000000000..12fdefd1d3 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/data/email_data.xml @@ -0,0 +1,27 @@ + + + + Auth Directory: Email Already Existing + noreply@example.org + Someone tried to invite you in an other team + {{object.partner_id.id}} + + + {{object.partner_id.lang}} + +
+ Hi + + + +

+ tried to invite you in the team + but you already have an + other account, please contact if + you want to change your team. +

+
+
+
+ +
diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/models/__init__.py b/shopinvader_fastapi_auth_partner_api_unit_member/models/__init__.py new file mode 100644 index 0000000000..bcb3373f36 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/models/__init__.py @@ -0,0 +1,2 @@ +from . import auth_directory +from . import res_partner diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/models/auth_directory.py b/shopinvader_fastapi_auth_partner_api_unit_member/models/auth_directory.py new file mode 100644 index 0000000000..6180775cba --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/models/auth_directory.py @@ -0,0 +1,19 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class AuthDirectory(models.Model): + _inherit = "auth.directory" + + member_existing_template_id = fields.Many2one( + "mail.template", + "Mail Template Unit Member Already Existing", + required=False, + default=lambda self: self.env.ref( + "shopinvader_fastapi_auth_partner_api_unit_member.email_already_existing", + raise_if_not_found=False, + ), + ) diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/models/res_partner.py b/shopinvader_fastapi_auth_partner_api_unit_member/models/res_partner.py new file mode 100644 index 0000000000..016d8e255b --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/models/res_partner.py @@ -0,0 +1,88 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + +from ..schemas import AuthState + +_logger = logging.getLogger(__name__) + + +class ResPartner(models.Model): + _inherit = "res.partner" + + member_auth_state = fields.Selection( + selection=[ + (AuthState.none.value, "None"), + (AuthState.invited.value, "Invited"), + (AuthState.accepted.value, "Accepted"), + ], + compute="_compute_member_auth_state", + compute_sudo=True, + ) + + @api.depends("auth_partner_ids.password", "auth_partner_ids.encrypted_password") + def _compute_member_auth_state(self): + for record in self: + auth = record.auth_partner_ids + if not auth: + record.member_auth_state = AuthState.none.value + continue + + if len(auth) > 1: + _logger.warning( + "Multiple auth_partner_ids for unit member partner %s, " + "using the first one", + record, + ) + auth = auth[0] + + if auth.password or auth.encrypted_password: + record.member_auth_state = AuthState.accepted.value + else: + record.member_auth_state = AuthState.invited.value + + @api.model + def _invite_shopinvader_unit_member(self, member_id, directory): + self._ensure_manager() + member = self.browse(member_id) + self._ensure_same_unit(member) + if not member.email: + raise UserError(_("Cannot invite a member without an email")) + + auth_with_email = self.env["auth.partner"].search( + [ + ("login", "=", member.email), + ("directory_id", "=", directory.id), + ], + ) + + if auth_with_email: + # If another member with the same email is already in the directory, + if auth_with_email not in member.auth_partner_ids: + directory._send_mail_background( + "member_existing", + auth_with_email, + member=member, + manager=self, + ) + raise UserError( + # Do not leak the information that the email is already in use + _("Something went wrong, please contact the administrator"), + ) + + member_auth = auth_with_email + else: + member_auth = self.env["auth.partner"].create( + { + "partner_id": member.id, + "login": member.email, + "directory_id": directory.id, + } + ) + + member_auth._send_invite() + return member diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/readme/CONTRIBUTORS.rst b/shopinvader_fastapi_auth_partner_api_unit_member/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..a4d0ad9229 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Akretion `_: + + * Florian Mounier diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/readme/DESCRIPTION.rst b/shopinvader_fastapi_auth_partner_api_unit_member/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..4f84ce719c --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This module glues the `shopinvader_fastapi_auth_partner_api` and the `shopinvader_api_unit_member` modules. + +It adds a auth_state field to the unit member and defines this member route: + +- `POST /unit/members/:id/invite` to add an auth.partner to the unit member allowing it to sign in and send an invite email to it. + diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/routers/__init__.py b/shopinvader_fastapi_auth_partner_api_unit_member/routers/__init__.py new file mode 100644 index 0000000000..d7352f9313 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/routers/__init__.py @@ -0,0 +1 @@ +from .unit_members import unit_member_router diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/routers/unit_members.py b/shopinvader_fastapi_auth_partner_api_unit_member/routers/unit_members.py new file mode 100644 index 0000000000..0497a4ebfa --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/routers/unit_members.py @@ -0,0 +1,30 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from typing import Annotated + +from fastapi import Depends + +from odoo.addons.base.models.res_partner import Partner as ResPartner +from odoo.addons.fastapi.dependencies import fastapi_endpoint +from odoo.addons.fastapi.models import FastapiEndpoint +from odoo.addons.shopinvader_api_unit_member.routers import unit_member_router +from odoo.addons.shopinvader_api_unit_member.routers.unit_members import ( + authenticated_manager, +) + +from ..schemas import UnitMember + + +@unit_member_router.post("/unit/members/{member_id}/invite") +async def invite_unit_member( + partner: Annotated[ResPartner, Depends(authenticated_manager)], + endpoint: Annotated[FastapiEndpoint, Depends(fastapi_endpoint)], + member_id: int, +) -> UnitMember: + """ + Invite a unit member to sign in. + """ + member = partner._invite_shopinvader_unit_member(member_id, endpoint.directory_id) + return UnitMember.from_res_partner(member) diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/schemas.py b/shopinvader_fastapi_auth_partner_api_unit_member/schemas.py new file mode 100644 index 0000000000..99c4030edf --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/schemas.py @@ -0,0 +1,23 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from enum import Enum + +from odoo.addons.shopinvader_api_unit_member.schemas import UnitMember as UnitMemberBase + + +class AuthState(str, Enum): + none = "none" + invited = "invited" + accepted = "accepted" + + +class UnitMember(UnitMemberBase, extends=True): + auth_state: AuthState + + @classmethod + def from_res_partner(cls, odoo_rec): + res = super().from_res_partner(odoo_rec) + res.auth_state = odoo_rec.member_auth_state + return res diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/static/description/index.html b/shopinvader_fastapi_auth_partner_api_unit_member/static/description/index.html new file mode 100644 index 0000000000..e76699fe25 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/static/description/index.html @@ -0,0 +1,424 @@ + + + + + + +Shopinvader Fastapi Auth Partner Api Unit Member + + + +
+

Shopinvader Fastapi Auth Partner Api Unit Member

+ + +

Beta License: AGPL-3 shopinvader/odoo-shopinvader

+

This module glues the shopinvader_fastapi_auth_partner_api and the shopinvader_api_unit_member modules.

+

It adds a auth_state field to the unit member and defines this member route:

+
    +
  • POST /unit/members/:id/invite to add an auth.partner to the unit member allowing it to sign in and send an invite email to it.
  • +
+

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

+
    +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the shopinvader/odoo-shopinvader project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/tests/__init__.py b/shopinvader_fastapi_auth_partner_api_unit_member/tests/__init__.py new file mode 100644 index 0000000000..722a620468 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/tests/__init__.py @@ -0,0 +1 @@ +from . import test_shopinvader_api_unit_members diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/tests/test_shopinvader_api_unit_members.py b/shopinvader_fastapi_auth_partner_api_unit_member/tests/test_shopinvader_api_unit_members.py new file mode 100644 index 0000000000..3cd0b6aa83 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/tests/test_shopinvader_api_unit_members.py @@ -0,0 +1,266 @@ +# Copyright 2024 Akretion (http://www.akretion.com). +# @author Florian Mounier +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from contextlib import contextmanager +from functools import partial + +from fastapi import status +from requests import Response + +from odoo.tests.common import tagged +from odoo.tools import mute_logger + +from odoo.addons.auth_partner.tests.common import CommonTestAuthPartner +from odoo.addons.extendable_fastapi.tests.common import FastAPITransactionCase +from odoo.addons.fastapi.dependencies import fastapi_endpoint +from odoo.addons.shopinvader_unit_management.tests.common import ( + TestUnitManagementCommon, +) + +from ..routers import unit_member_router + + +@tagged("post_install", "-at_install") +class TestShopinvaderFastapiAuthPartnerApiUnitMember( + FastAPITransactionCase, TestUnitManagementCommon, CommonTestAuthPartner +): + @classmethod + def setUpClass(cls) -> None: + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, queue_job__no_delay=True)) + cls.directory.member_existing_template_id = cls.env.ref( + "shopinvader_fastapi_auth_partner_api_unit_member.email_already_existing", + ) + # Set emails + managers = [1, 2, 0, 2] + collaborators = [5, 3, 3, 0] + for unit in range(1, 5): + for manager in range(1, 1 + managers[unit - 1]): + mail = f"manager_{unit}_{manager}@example.org" + manager = getattr( + cls, + f"manager_{unit}_{manager}", + ) + manager.email = mail + for collaborator in range(1, 1 + collaborators[unit - 1]): + mail = f"collaborator_{unit}_{collaborator}@example.org" + collaborator = getattr( + cls, + f"collaborator_{unit}_{collaborator}", + ) + collaborator.email = mail + + cls.env["res.users"].create( + { + "name": "Test User", + "login": "test_user", + "groups_id": [ + ( + 6, + 0, + [ + cls.env.ref( + "shopinvader_api_unit_member." + "shopinvader_unit_management_user_group" + ).id + ], + ) + ], + } + ) + + cls.default_fastapi_authenticated_partner = cls.manager_1_1 + cls.default_fastapi_router = unit_member_router + cls.demo_app = cls.env.ref("fastapi_auth_partner.fastapi_endpoint_demo") + cls.default_fastapi_app = cls.demo_app._get_app() + cls.default_fastapi_dependency_overrides = { + fastapi_endpoint: partial(lambda a: a, cls.demo_app) + } + + @contextmanager + def _create_test_client(self, **kwargs): + kwargs.setdefault("raise_server_exceptions", False) + with mute_logger("httpx"), super()._create_test_client(**kwargs) as test_client: + yield test_client + + def test_unit_auth_state(self): + self.default_fastapi_authenticated_partner = self.manager_1_1 + + with self._create_test_client() as test_client: + response: Response = test_client.get("/unit/members") + self.assertEqual( + response.status_code, + status.HTTP_200_OK, + ) + + members = response.json() + self.assertEqual(len(members), 6) + for member in members: + self.assertEqual(member["auth_state"], "none") + + def test_invite_unit_member_as_collaborator(self): + self.default_fastapi_authenticated_partner = self.collaborator_1_1 + + with self._create_test_client() as test_client: + response: Response = test_client.post( + f"/unit/members/{self.collaborator_1_2.id}/invite" + ) + self.assertEqual( + response.status_code, + status.HTTP_403_FORBIDDEN, + ) + + def test_invite_unit_member_as_manager(self): + self.default_fastapi_authenticated_partner = self.manager_1_1 + self.assertFalse(self.collaborator_1_2.auth_partner_ids) + auth_partner_len = self.env["auth.partner"].search_count([]) + with self._create_test_client() as test_client, self.new_mails() as new_mails: + response: Response = test_client.post( + f"/unit/members/{self.collaborator_1_2.id}/invite" + ) + self.assertEqual( + response.status_code, + status.HTTP_200_OK, + ) + self.assertTrue(self.collaborator_1_2.auth_partner_ids) + self.assertEqual( + self.env["auth.partner"].search_count([]), auth_partner_len + 1 + ) + self.assertEqual(len(new_mails), 1) + self.assertEqual( + new_mails.subject, + "Welcome", + ) + self.assertIn( + "your account have been created", + new_mails.body, + ) + + def test_invite_existing_unit_member_as_manager(self): + self.default_fastapi_authenticated_partner = self.manager_1_1 + self.collaborator_1_2.auth_partner_ids = [ + ( + 0, + 0, + { + "login": self.collaborator_1_2.email, + "directory_id": self.directory.id, + }, + ) + ] + + self.assertTrue(self.collaborator_1_2.auth_partner_ids) + auth_partner_len = self.env["auth.partner"].search_count([]) + with self._create_test_client() as test_client, self.new_mails() as new_mails: + response: Response = test_client.post( + f"/unit/members/{self.collaborator_1_2.id}/invite" + ) + self.assertEqual( + response.status_code, + status.HTTP_200_OK, + ) + self.assertTrue(self.collaborator_1_2.auth_partner_ids) + self.assertEqual(self.env["auth.partner"].search_count([]), auth_partner_len) + self.assertEqual(len(new_mails), 1) + self.assertEqual(new_mails.partner_ids, self.collaborator_1_2) + self.assertEqual( + new_mails.subject, + "Welcome", + ) + self.assertIn( + "your account have been created", + new_mails.body, + ) + + def test_invite_existing_other_unit_member_as_manager(self): + self.default_fastapi_authenticated_partner = self.manager_1_1 + self.collaborator_2_1.email = self.collaborator_1_2.email + self.collaborator_2_1.auth_partner_ids = [ + ( + 0, + 0, + { + "login": self.collaborator_2_1.email, + "directory_id": self.directory.id, + }, + ) + ] + + self.assertFalse(self.collaborator_1_2.auth_partner_ids) + auth_partner_len = self.env["auth.partner"].search_count([]) + with self._create_test_client() as test_client, self.new_mails() as new_mails: + response: Response = test_client.post( + f"/unit/members/{self.collaborator_1_2.id}/invite" + ) + self.assertEqual( + response.status_code, + status.HTTP_400_BAD_REQUEST, + ) + self.assertFalse(self.collaborator_1_2.auth_partner_ids) + self.assertEqual(self.env["auth.partner"].search_count([]), auth_partner_len) + self.assertEqual(len(new_mails), 1) + self.assertEqual(new_mails.partner_ids, self.collaborator_2_1) + self.assertEqual( + new_mails.subject, + "Someone tried to invite you in an other team", + ) + self.assertIn( + "Hi Collaborator 2.1", + new_mails.body, + ) + self.assertIn( + "Manager 1.1 tried to invite you in the team", + new_mails.body, + ) + self.assertIn( + "Unit 1 but you already have an", + new_mails.body, + ) + self.assertIn( + "other account, please contact Manager 1.1", + new_mails.body, + ) + + def test_invite_unit_auth_state(self): + self.default_fastapi_authenticated_partner = self.manager_1_1 + + with self._create_test_client() as test_client: + response: Response = test_client.get( + f"/unit/members/{self.collaborator_1_2.id}", + ) + + self.assertEqual( + response.status_code, + status.HTTP_200_OK, + ) + + member = response.json() + self.assertEqual(member["auth_state"], "none") + + with self._create_test_client() as test_client, self.new_mails(): + response: Response = test_client.post( + f"/unit/members/{self.collaborator_1_2.id}/invite" + ) + self.assertEqual( + response.status_code, + status.HTTP_200_OK, + ) + + member = response.json() + self.assertEqual(member["auth_state"], "invited") + + self.collaborator_1_2.auth_partner_ids.write({"password": "test"}) + + with self._create_test_client() as test_client: + response: Response = test_client.get( + f"/unit/members/{self.collaborator_1_2.id}", + ) + + self.assertEqual( + response.status_code, + status.HTTP_200_OK, + ) + + member = response.json() + self.assertEqual(member["auth_state"], "accepted") diff --git a/shopinvader_fastapi_auth_partner_api_unit_member/views/auth_directory_view.xml b/shopinvader_fastapi_auth_partner_api_unit_member/views/auth_directory_view.xml new file mode 100644 index 0000000000..dbb85502b4 --- /dev/null +++ b/shopinvader_fastapi_auth_partner_api_unit_member/views/auth_directory_view.xml @@ -0,0 +1,14 @@ + + + + + auth.directory + + + + + + + + +