[ADD] membership_status_in_portal: portal membership status badge

Add a generic module displaying the connected portal user's membership
status in the account sidebar, below the "Edit information" link.

The native Odoo membership label (``res.partner.membership_state`` from
the ``membership`` module) is shown as a colored badge:
- green (``text-bg-success``) for free / paid members;
- yellow (``text-bg-warning``) for waiting / invoiced members;
- red (``text-bg-danger``) otherwise.

Color, icon and label are computed in the QWeb template; no model or
field is added. Includes HttpCase tests covering member and non-member
rendering and asserting no action button is rendered.
This commit is contained in:
Stéphan Sainléger
2026-09-21 14:26:43 +02:00
parent ccdd1db968
commit 34f70b342c
7 changed files with 180 additions and 0 deletions

View File

@@ -0,0 +1 @@
from . import test_portal_sidebar

View File

@@ -0,0 +1,66 @@
import re
from odoo.tests import HttpCase, tagged
@tagged("post_install", "-at_install")
class MembershipStatusPortalCase(HttpCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.portal_group = cls.env.ref("base.group_portal")
def _create_portal_user(self, login, **partner_vals):
partner = self.env["res.partner"].create(
{
"name": login,
"lang": self.env.lang or "en_US",
**partner_vals,
}
)
return self.env["res.users"].create(
{
"name": login,
"login": login,
"password": login,
"partner_id": partner.id,
"groups_id": [(6, 0, [self.portal_group.id])],
}
)
def _membership_label(self, partner):
selection = self.env["res.partner"].fields_get(["membership_state"])[
"membership_state"
]["selection"]
return dict(selection)[partner.membership_state]
def _open_my_home(self, user):
self.authenticate(user.login, user.password)
response = self.url_open("/my/home")
self.assertEqual(response.status_code, 200)
return response.text
def _extract_membership_block(self, html):
match = re.search(r'<div class="o_membership_state.*?</div>', html, re.DOTALL)
self.assertIsNotNone(match, "membership status block not found on /my/home")
return match.group(0)
def test_non_member_sees_status_without_button(self):
user = self._create_portal_user("nonmember")
self.assertEqual(user.partner_id.membership_state, "none")
block = self._extract_membership_block(self._open_my_home(user))
self.assertIn(self._membership_label(user.partner_id), block)
self.assertNotIn("<button", block)
self.assertNotIn("<a ", block)
def test_member_sees_status_without_button(self):
user = self._create_portal_user("member", free_member=True)
self.assertEqual(user.partner_id.membership_state, "free")
block = self._extract_membership_block(self._open_my_home(user))
self.assertIn(self._membership_label(user.partner_id), block)
self.assertNotIn("<button", block)
self.assertNotIn("<a ", block)