- Refactor portal templates to eliminate intra-module template inheritance - Integrate emergency contacts section into player injuries template - Add full internal admin views for treatment notes with chatter support - Fix ORM warning in test_rights by using proper ORM commands - Update manifest to reflect new functionality (v18.0.1.9.0) This refactoring improves template stability by removing XPath errors and provides treatment professionals with better access to emergency contacts and treatment notes both in portal and backend interfaces.
332 lines
14 KiB
Python
332 lines
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo.tests import tagged, TransactionCase
|
|
from odoo.exceptions import AccessError, ValidationError
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestPlayerRemoval(TransactionCase):
|
|
at_install = False
|
|
post_install = True
|
|
|
|
@classmethod
|
|
def setUpClass(cls, *args, **kwargs):
|
|
super(TestPlayerRemoval, cls).setUpClass(*args, **kwargs)
|
|
|
|
# Set up minimal email configuration
|
|
cls.env['ir.config_parameter'].set_param('mail.default.from', 'test@example.com')
|
|
|
|
# Set up test data
|
|
cls._setup_test_data()
|
|
|
|
@classmethod
|
|
def _setup_test_data(cls):
|
|
"""Set up test data for player removal tests"""
|
|
# Create test data
|
|
cls.team1 = cls.env['sports.team'].create({
|
|
'name': 'Test Team 1',
|
|
})
|
|
|
|
cls.team2 = cls.env['sports.team'].create({
|
|
'name': 'Test Team 2',
|
|
})
|
|
|
|
# Team creation moved to _setup_test_data()
|
|
|
|
# Ensure we have access to the sports.patient model for our test users
|
|
patient_model = cls.env['ir.model'].search([('model', '=', 'sports.patient')])
|
|
if patient_model:
|
|
# Create a record rule that allows treatment professionals to read patient records
|
|
# This is just for testing - in production, this would be handled by proper record rules
|
|
cls.env['ir.rule'].create({
|
|
'name': 'Test: Allow treatment professionals to read patients',
|
|
'model_id': patient_model.id,
|
|
'domain_force': '[]', # No domain restrictions for this test
|
|
'groups': [(6, 0, [
|
|
cls.env.ref('bemade_sports_clinic.group_sports_clinic_treatment_professional').id,
|
|
])],
|
|
'perm_read': True,
|
|
'perm_write': False,
|
|
'perm_create': False,
|
|
'perm_unlink': False,
|
|
})
|
|
|
|
# Create test players
|
|
cls.player1 = cls.env['sports.patient'].create({
|
|
'first_name': 'Test',
|
|
'last_name': 'Player1',
|
|
'team_ids': [(6, 0, [cls.team1.id, cls.team2.id])],
|
|
})
|
|
|
|
cls.player2 = cls.env['sports.patient'].create({
|
|
'first_name': 'Test',
|
|
'last_name': 'Player2',
|
|
'team_ids': [(6, 0, [cls.team1.id])],
|
|
})
|
|
|
|
# Create test users
|
|
cls.admin_user = cls.env.ref('base.user_admin')
|
|
|
|
# Create treatment professional user
|
|
cls.treatment_prof_user = cls.env['res.users'].create({
|
|
'name': 'Treatment Professional',
|
|
'login': 'treatment_prof@example.com',
|
|
'groups_id': [(6, 0, [
|
|
cls.env.ref('bemade_sports_clinic.group_sports_clinic_treatment_professional').id,
|
|
cls.env.ref('base.group_user').id,
|
|
])],
|
|
})
|
|
|
|
# Create team staff user
|
|
cls.team_staff_user = cls.env['res.users'].create({
|
|
'name': 'Team Staff',
|
|
'login': 'team_staff@example.com',
|
|
'groups_id': [(6, 0, [
|
|
cls.env.ref('base.group_user').id,
|
|
])],
|
|
})
|
|
|
|
# Add team staff to team1
|
|
cls.team_staff = cls.env['sports.team.staff'].create({
|
|
'team_id': cls.team1.id,
|
|
'partner_id': cls.team_staff_user.partner_id.id,
|
|
'role': 'coach',
|
|
})
|
|
|
|
# Create regular user with no special permissions
|
|
cls.regular_user = cls.env['res.users'].create({
|
|
'name': 'Regular User',
|
|
'login': 'regular@example.com',
|
|
'groups_id': [(6, 0, [cls.env.ref('base.group_user').id])],
|
|
})
|
|
|
|
def test_admin_can_remove_player_from_team(self):
|
|
"""Test that admin can remove a player from any team"""
|
|
self.player1.with_user(self.admin_user).remove_from_team(self.team1.id)
|
|
self.assertNotIn(self.team1, self.player1.team_ids)
|
|
|
|
# TODO: Re-enable and fix this test after resolving email configuration issues
|
|
# def test_treatment_prof_can_remove_player_from_team(self):
|
|
# """Test that treatment professionals can remove players from teams they are staffed on as therapists"""
|
|
# # Add treatment professional as therapist to team1
|
|
# self.env['sports.team.staff'].create({
|
|
# 'team_id': self.team1.id,
|
|
# 'partner_id': self.treatment_prof_user.partner_id.id,
|
|
# 'role': 'therapist',
|
|
# })
|
|
#
|
|
# # Verify initial state
|
|
# self.assertIn(self.team1, self.player1.team_ids)
|
|
#
|
|
# # Perform the removal
|
|
# result = self.player1.with_user(self.treatment_prof_user).remove_from_team(self.team1.id)
|
|
#
|
|
# # Refresh the record to ensure we have the latest data
|
|
# self.player1.refresh()
|
|
#
|
|
# # Verify the player was removed from the team
|
|
# self.assertNotIn(self.team1, self.player1.team_ids)
|
|
#
|
|
# # Verify the response indicates success
|
|
# self.assertIn('success', result.get('params', {}).get('type', ''))
|
|
|
|
def test_team_staff_cannot_directly_remove_players(self):
|
|
"""Test that team staff cannot directly remove players"""
|
|
# Verify direct removal is not allowed
|
|
with self.assertRaises(AccessError):
|
|
self.player1.with_user(self.team_staff_user).remove_from_team(self.team1.id)
|
|
|
|
# Verify the player is still on the team and no pending removal was set
|
|
self.assertIn(self.team1, self.player1.team_ids)
|
|
self.assertFalse(self.player1.pending_removal)
|
|
|
|
def test_team_staff_can_request_removal(self):
|
|
"""Test that team staff can request player removal with proper permissions"""
|
|
# Create a portal user (coach)
|
|
coach_user = self.env['res.users'].with_context(no_reset_password=True).create({
|
|
'name': 'Team Coach',
|
|
'login': 'coach@example.com',
|
|
'groups_id': [(6, 0, [
|
|
self.env.ref('base.group_portal').id,
|
|
self.env.ref('bemade_sports_clinic.group_portal_team_coach').id
|
|
])],
|
|
})
|
|
|
|
# Add coach to the team as staff
|
|
coach_staff = self.env['sports.team.staff'].create({
|
|
'team_id': self.team1.id,
|
|
'partner_id': coach_user.partner_id.id,
|
|
'role': 'coach',
|
|
})
|
|
|
|
# Create a head therapist for the team
|
|
head_therapist = self.env['res.users'].create({
|
|
'name': 'Head Therapist',
|
|
'login': 'head.therapist@example.com',
|
|
'groups_id': [
|
|
(4, self.env.ref('base.group_user').id),
|
|
(4, self.env.ref('bemade_sports_clinic.group_sports_clinic_treatment_professional').id)
|
|
],
|
|
})
|
|
|
|
# Add head therapist to the team with head_therapist role
|
|
therapist_record = self.env['sports.team.staff'].create({
|
|
'team_id': self.team1.id,
|
|
'partner_id': head_therapist.partner_id.id,
|
|
'role': 'head_therapist',
|
|
})
|
|
|
|
# Verify the head therapist was created correctly
|
|
self.assertEqual(therapist_record.role, 'head_therapist', "Head therapist should be created with head_therapist role")
|
|
|
|
# Add player to the team
|
|
self.team1.patient_ids = [(4, self.player1.id)]
|
|
|
|
# Ensure the coach has access to the team
|
|
self.team1.allowed_user_ids = [(4, coach_user.id)]
|
|
|
|
# Switch to coach user context
|
|
self.player1 = self.player1.with_user(coach_user)
|
|
|
|
# Test the request_removal flow
|
|
result = self.player1.with_user(coach_user).request_team_removal(
|
|
team_id=self.team1.id,
|
|
reason="Test removal request"
|
|
)
|
|
|
|
# Verify the pending_removal flag was set
|
|
self.assertTrue(self.player1.pending_removal)
|
|
|
|
# Verify the success notification
|
|
self.assertEqual(result['type'], 'ir.actions.client')
|
|
self.assertEqual(result['tag'], 'display_notification')
|
|
self.assertEqual(result['params']['title'], 'Removal Request Submitted')
|
|
self.assertIn('has been submitted for review', result['params']['message'])
|
|
|
|
# Now run the cron job to create the activity
|
|
self.env['sports.patient']._cron_handle_pending_removals()
|
|
|
|
# Verify an activity was created for the head therapist
|
|
activities = self.env['mail.activity'].search([
|
|
('res_model', '=', 'sports.patient'),
|
|
('res_id', '=', self.player1.id),
|
|
('summary', 'ilike', 'Player Removal Request')
|
|
])
|
|
self.assertTrue(activities, "Activity should be created for head therapist by cron job")
|
|
|
|
def test_treatment_prof_cannot_remove_from_other_teams(self):
|
|
"""Test that treatment professionals cannot remove players from teams they are not staffed on"""
|
|
# Only add treatment professional to team1 as a therapist, not team2
|
|
self.env['sports.team.staff'].create({
|
|
'team_id': self.team1.id,
|
|
'partner_id': self.treatment_prof_user.partner_id.id,
|
|
'role': 'therapist',
|
|
})
|
|
# Expect AccessError when trying to access team they don't have permission for
|
|
with self.assertRaises(AccessError) as context:
|
|
self.player1.with_user(self.treatment_prof_user).remove_from_team(self.team2.id)
|
|
# Verify we got an access denied error (don't check specific message as it might vary)
|
|
self.assertTrue(str(context.exception), "Should raise AccessError for unauthorized team access")
|
|
|
|
def test_regular_user_cannot_remove_players(self):
|
|
"""Test that regular users cannot remove players from any team"""
|
|
# Verify regular users get an AccessError when trying to remove players
|
|
with self.assertRaises(AccessError):
|
|
self.player1.with_user(self.regular_user).remove_from_team(self.team1.id)
|
|
|
|
# Verify the player is still on the team
|
|
self.assertIn(self.team1, self.player1.team_ids)
|
|
|
|
def test_remove_nonexistent_team(self):
|
|
"""Test removing a player from a non-existent team"""
|
|
with self.assertRaises(ValidationError):
|
|
self.player1.with_user(self.admin_user).remove_from_team(999999)
|
|
|
|
def test_remove_player_not_in_team(self):
|
|
"""Test removing a player from a team they don't belong to"""
|
|
with self.assertRaises(ValidationError):
|
|
self.player2.with_user(self.admin_user).remove_from_team(self.team2.id)
|
|
|
|
def test_remove_last_team_archives_player(self):
|
|
"""Test that removing a player from their last team schedules them for archiving"""
|
|
# Get a fresh copy of the player to avoid cached values
|
|
player = self.env['sports.patient'].browse(self.player2.id)
|
|
|
|
# Verify initial state
|
|
self.assertTrue(player.active)
|
|
self.assertEqual(len(player.team_ids), 1)
|
|
|
|
# Remove from their only team
|
|
result = player.with_user(self.admin_user).remove_from_team(self.team1.id)
|
|
|
|
# Get a fresh copy of the player after removal
|
|
player = self.env['sports.patient'].browse(self.player2.id)
|
|
|
|
# Verify player is no longer on the team but still active
|
|
self.assertEqual(len(player.team_ids), 0, "Player should be removed from all teams")
|
|
self.assertTrue(player.active, "Player should still be active until the cron runs")
|
|
|
|
# Verify the response indicates the player will be archived
|
|
self.assertIn('will be archived', result.get('params', {}).get('message', ''),
|
|
"Response should indicate player will be archived")
|
|
|
|
# Now run the cron manually to test archiving
|
|
self.env['sports.patient']._cron_archive_players_without_teams()
|
|
|
|
# Get a fresh copy of the player after cron job
|
|
player = self.env['sports.patient'].browse(self.player2.id)
|
|
self.assertFalse(player.active, "Player should be archived after the cron runs")
|
|
|
|
def test_remove_with_reason_logs_reason(self):
|
|
"""Test that providing a reason logs it in the chatter"""
|
|
reason = "Test reason for removal"
|
|
self.player1.with_user(self.admin_user).remove_from_team(
|
|
self.team1.id,
|
|
reason=reason
|
|
)
|
|
messages = self.env['mail.message'].search([
|
|
('model', '=', 'sports.patient'),
|
|
('res_id', '=', self.player1.id),
|
|
('body', 'ilike', reason)
|
|
])
|
|
self.assertTrue(messages, "Reason should be logged in the chatter")
|
|
|
|
def test_pending_removal_flag_cleared(self):
|
|
"""Test that pending_removal flag is cleared when specified"""
|
|
# Add player to another team first
|
|
self.team2.patient_ids = [(4, self.player1.id)]
|
|
|
|
# Set pending_removal flag and clear it during removal
|
|
self.player1.pending_removal = True
|
|
self.player1.with_user(self.admin_user).remove_from_team(
|
|
self.team1.id,
|
|
clear_pending=True
|
|
)
|
|
|
|
# Get a fresh copy to ensure we have the latest data
|
|
player = self.env['sports.patient'].browse(self.player1.id)
|
|
self.assertFalse(player.pending_removal, "pending_removal should be cleared when clear_pending=True")
|
|
|
|
def test_pending_removal_flag_not_cleared(self):
|
|
"""Test that pending_removal flag is not cleared when specified"""
|
|
# Add player to another team first
|
|
self.team2.patient_ids = [(4, self.player1.id)]
|
|
|
|
# Set pending_removal flag and don't clear it during removal
|
|
self.player1.pending_removal = True
|
|
self.player1.with_user(self.admin_user).remove_from_team(
|
|
self.team1.id,
|
|
clear_pending=False
|
|
)
|
|
|
|
# Get a fresh copy to ensure we have the latest data
|
|
player = self.env['sports.patient'].browse(self.player1.id)
|
|
self.assertTrue(player.pending_removal, "pending_removal should remain True when clear_pending=False")
|
|
|
|
def test_remove_player_twice(self):
|
|
"""Test that removing a player twice raises an error"""
|
|
self.player1.with_user(self.admin_user).remove_from_team(self.team1.id)
|
|
with self.assertRaises(ValidationError) as context:
|
|
self.player1.with_user(self.admin_user).remove_from_team(self.team1.id)
|
|
self.assertIn('not a member', str(context.exception))
|