- 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.
300 lines
13 KiB
Python
300 lines
13 KiB
Python
from odoo.tests import TransactionCase, tagged
|
|
from odoo import Command, fields
|
|
from datetime import timedelta
|
|
from freezegun import freeze_time
|
|
|
|
|
|
@tagged("-at_install", "post_install")
|
|
class TestEndToEndWorkflows(TransactionCase):
|
|
"""End-to-end workflow tests for the sports clinic module"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
# Create organization and team
|
|
cls.organization = cls.env['sports.organization'].create({
|
|
'name': 'E2E Test Organization',
|
|
})
|
|
|
|
cls.team = cls.env['sports.team'].create({
|
|
'name': 'E2E Test Team',
|
|
'organization_id': cls.organization.id,
|
|
})
|
|
|
|
# Create a patient/player
|
|
cls.patient = cls.env['sports.patient'].create({
|
|
'first_name': 'E2E',
|
|
'last_name': 'Test Patient',
|
|
'birthdate': fields.Date.today() - timedelta(days=365 * 16), # 16 years old
|
|
'team_ids': [(4, cls.team.id)],
|
|
})
|
|
|
|
# Create users with different roles
|
|
# 1. Coach
|
|
cls.coach_partner = cls.env['res.partner'].create({
|
|
'name': 'E2E Coach',
|
|
'email': 'e2e.coach@example.com',
|
|
})
|
|
|
|
cls.coach_user = cls.env['res.users'].with_context(no_reset_password=True).create({
|
|
'partner_id': cls.coach_partner.id,
|
|
'login': 'e2e.coach@example.com',
|
|
'password': 'e2ecoach',
|
|
'name': cls.coach_partner.name,
|
|
'groups_id': [
|
|
Command.link(cls.env.ref('bemade_sports_clinic.group_sports_clinic_team_coach').id),
|
|
]
|
|
})
|
|
|
|
# 2. Therapist (treatment professional)
|
|
cls.therapist_partner = cls.env['res.partner'].create({
|
|
'name': 'E2E Therapist',
|
|
'email': 'e2e.therapist@example.com',
|
|
})
|
|
|
|
cls.therapist_user = cls.env['res.users'].with_context(no_reset_password=True).create({
|
|
'partner_id': cls.therapist_partner.id,
|
|
'login': 'e2e.therapist@example.com',
|
|
'password': 'e2etherapist',
|
|
'name': cls.therapist_partner.name,
|
|
'groups_id': [
|
|
Command.link(cls.env.ref('bemade_sports_clinic.group_sports_clinic_treatment_professional').id),
|
|
]
|
|
})
|
|
|
|
# Create team staff entries
|
|
cls.coach_staff = cls.env['sports.team.staff'].create({
|
|
'team_id': cls.team.id,
|
|
'partner_id': cls.coach_partner.id,
|
|
'is_treatment_professional': False,
|
|
'role': 'coach',
|
|
'user_id': cls.coach_user.id,
|
|
})
|
|
|
|
cls.therapist_staff = cls.env['sports.team.staff'].create({
|
|
'team_id': cls.team.id,
|
|
'partner_id': cls.therapist_partner.id,
|
|
'is_treatment_professional': True,
|
|
'role': 'therapist',
|
|
'user_id': cls.therapist_user.id,
|
|
})
|
|
|
|
def test_01_complete_injury_workflow(self):
|
|
"""Test complete injury workflow from reporting to resolution"""
|
|
|
|
# Step 1: Coach reports an injury (unverified)
|
|
with freeze_time('2025-01-15'):
|
|
# Create injury as coach
|
|
injury = self.env['sports.patient.injury'].with_user(self.coach_user).create({
|
|
'patient_id': self.patient.id,
|
|
'team_id': self.team.id,
|
|
'diagnosis': 'Ankle Sprain - Initial',
|
|
'external_notes': 'Player twisted ankle during practice.',
|
|
'injury_date': fields.Date.today(),
|
|
})
|
|
|
|
# Check that injury is unverified and has default values
|
|
self.assertEqual(injury.stage, 'unverified')
|
|
self.assertEqual(injury.parental_consent, 'no') # Default for coach-created injuries
|
|
self.assertFalse(injury.treatment_professional_ids) # No treatment pros yet
|
|
|
|
# Check that the player is now marked as injured
|
|
self.patient.invalidate_cache()
|
|
self.assertTrue(self.patient.is_injured)
|
|
|
|
# Step 2: Therapist verifies the injury and adds details
|
|
with freeze_time('2025-01-16'):
|
|
# Update injury as therapist
|
|
injury.with_user(self.therapist_user).write({
|
|
'diagnosis': 'Grade 2 Ankle Sprain',
|
|
'stage': 'active',
|
|
'treatment_professional_ids': [(4, self.therapist_partner.id)],
|
|
'internal_notes': 'Significant swelling observed. Recommend RICE protocol.',
|
|
'parental_consent': 'yes',
|
|
})
|
|
|
|
# Check that injury is now active and therapist is assigned
|
|
self.assertEqual(injury.stage, 'active')
|
|
self.assertEqual(injury.parental_consent, 'yes')
|
|
self.assertIn(self.therapist_partner, injury.treatment_professional_ids)
|
|
|
|
# Add a message/note to the chatter as the therapist
|
|
injury.with_user(self.therapist_user).message_post(
|
|
body="Initial assessment complete. Ankle mobility restricted.",
|
|
message_type='comment',
|
|
subtype_xmlid='mail.mt_comment',
|
|
)
|
|
|
|
# Step 3: Treatment progress and updates
|
|
with freeze_time('2025-01-23'):
|
|
# Add treatment progress note
|
|
injury.with_user(self.therapist_user).write({
|
|
'external_notes': 'Player twisted ankle during practice. Follow-up: '
|
|
'Swelling reduced, started basic rehabilitation exercises.',
|
|
'internal_notes': 'Significant swelling observed. Recommend RICE protocol. '
|
|
'Update: ROM improving, pain decreased from 7/10 to 4/10.',
|
|
})
|
|
|
|
# Add another chatter message
|
|
injury.with_user(self.therapist_user).message_post(
|
|
body="Week 1 follow-up: Patient showing good progress. Cleared for light conditioning.",
|
|
message_type='comment',
|
|
subtype_xmlid='mail.mt_comment',
|
|
)
|
|
|
|
# Check that coach can see the external updates
|
|
as_coach = injury.with_user(self.coach_user)
|
|
self.assertIn('started basic rehabilitation exercises', as_coach.external_notes)
|
|
# But not the internal notes
|
|
with self.assertRaises(Exception):
|
|
coach_view_notes = as_coach.internal_notes
|
|
|
|
# Step 4: More treatment and approaching resolution
|
|
with freeze_time('2025-02-06'):
|
|
# Add another treatment progress note
|
|
injury.with_user(self.therapist_user).write({
|
|
'external_notes': injury.external_notes + '\nFollow-up 2: '
|
|
'Good progress. Can resume non-contact training.',
|
|
'internal_notes': injury.internal_notes + '\nFollow-up 2: '
|
|
'Strength tests show 85% compared to uninjured side. Proprioception improving.',
|
|
})
|
|
|
|
# Step 5: Final resolution
|
|
with freeze_time('2025-02-15'):
|
|
# Resolve the injury
|
|
injury.with_user(self.therapist_user).write({
|
|
'stage': 'resolved',
|
|
'resolution_date': fields.Date.today(),
|
|
'external_notes': injury.external_notes + '\nFinal update: '
|
|
'Player fully cleared to return to all activities.',
|
|
'internal_notes': injury.internal_notes + '\nFinal assessment: '
|
|
'Full ROM restored, strength tests at 95%. Cleared for all activities.',
|
|
})
|
|
|
|
# Verify the resolution details
|
|
self.assertEqual(injury.stage, 'resolved')
|
|
self.assertEqual(injury.resolution_date, fields.Date.from_string('2025-02-15'))
|
|
|
|
# Check that the player is no longer marked as injured
|
|
self.patient.invalidate_cache()
|
|
self.assertFalse(self.patient.is_injured)
|
|
|
|
def test_02_player_removal_workflow(self):
|
|
"""Test complete player removal workflow"""
|
|
|
|
# Create a test player to be removed
|
|
player_to_remove = self.env['sports.patient'].create({
|
|
'first_name': 'Removal',
|
|
'last_name': 'Test Patient',
|
|
'birthdate': fields.Date.today() - timedelta(days=365 * 17),
|
|
'team_ids': [(4, self.team.id)],
|
|
})
|
|
|
|
# Verify initial state
|
|
self.assertIn(self.team, player_to_remove.team_ids)
|
|
self.assertFalse(player_to_remove.archived)
|
|
|
|
# Step 1: Coach requests player removal
|
|
removal_request = self.env['sports.patient.team.removal'].with_user(self.coach_user).create({
|
|
'patient_id': player_to_remove.id,
|
|
'team_id': self.team.id,
|
|
'reason': 'Player transferred to another team',
|
|
'requested_by_id': self.coach_partner.id,
|
|
})
|
|
|
|
self.assertEqual(removal_request.state, 'draft')
|
|
self.assertEqual(removal_request.requested_by_id, self.coach_partner)
|
|
|
|
# Step 2: Admin approves and processes the removal
|
|
admin_user = self.env.ref('base.user_admin')
|
|
|
|
# Admin processes the request
|
|
removal_request.with_user(admin_user).action_approve()
|
|
self.assertEqual(removal_request.state, 'approved')
|
|
|
|
# Execute the removal
|
|
removal_request.with_user(admin_user).action_execute()
|
|
self.assertEqual(removal_request.state, 'done')
|
|
|
|
# Verify player has been removed from team
|
|
player_to_remove.invalidate_cache()
|
|
self.assertNotIn(self.team, player_to_remove.team_ids)
|
|
|
|
# Check if player is archived when no teams left
|
|
self.assertTrue(len(player_to_remove.team_ids) == 0)
|
|
player_to_remove.invalidate_cache()
|
|
self.assertTrue(player_to_remove.archived)
|
|
|
|
def test_03_data_export_anonymization_process(self):
|
|
"""Test data export and anonymization process"""
|
|
# Create test patient with personal information
|
|
patient_for_anon = self.env['sports.patient'].create({
|
|
'first_name': 'Export',
|
|
'last_name': 'Test Patient',
|
|
'birthdate': fields.Date.today() - timedelta(days=365 * 15),
|
|
'team_ids': [(4, self.team.id)],
|
|
'street': '123 Test Street',
|
|
'city': 'Test City',
|
|
'zip': '12345',
|
|
'email': 'test.patient@example.com',
|
|
'phone': '555-123-4567',
|
|
})
|
|
|
|
# Create a contact for the patient
|
|
patient_contact = self.env['sports.patient.contact'].create({
|
|
'patient_id': patient_for_anon.id,
|
|
'name': 'Test Parent',
|
|
'phone': '555-987-6543',
|
|
'email': 'test.parent@example.com',
|
|
'relationship': 'parent',
|
|
})
|
|
|
|
# Create an injury with personal medical information
|
|
injury_for_anon = self.env['sports.patient.injury'].create({
|
|
'patient_id': patient_for_anon.id,
|
|
'team_id': self.team.id,
|
|
'diagnosis': 'Confidential Medical Condition',
|
|
'external_notes': 'Contains personally identifiable information',
|
|
'internal_notes': 'Contains sensitive medical details',
|
|
'injury_date': fields.Date.today(),
|
|
})
|
|
|
|
# Run anonymization wizard (simulated since actual wizard implementation might vary)
|
|
# This is a placeholder for the actual anonymization process
|
|
# In a real implementation, we would call the anonymization wizard here
|
|
|
|
# For testing purposes, we'll manually anonymize the patient
|
|
patient_for_anon.write({
|
|
'first_name': 'Anonymized',
|
|
'last_name': 'Patient',
|
|
'street': False,
|
|
'city': False,
|
|
'zip': False,
|
|
'email': False,
|
|
'phone': False,
|
|
})
|
|
|
|
patient_contact.write({
|
|
'name': 'Anonymized Contact',
|
|
'phone': False,
|
|
'email': False,
|
|
})
|
|
|
|
injury_for_anon.write({
|
|
'external_notes': 'Anonymized external notes',
|
|
'internal_notes': 'Anonymized internal notes',
|
|
})
|
|
|
|
# Verify anonymization was effective
|
|
self.assertEqual(patient_for_anon.first_name, 'Anonymized')
|
|
self.assertEqual(patient_for_anon.last_name, 'Patient')
|
|
self.assertFalse(patient_for_anon.email)
|
|
self.assertFalse(patient_for_anon.phone)
|
|
|
|
self.assertEqual(patient_contact.name, 'Anonymized Contact')
|
|
self.assertFalse(patient_contact.email)
|
|
self.assertFalse(patient_contact.phone)
|
|
|
|
self.assertEqual(injury_for_anon.external_notes, 'Anonymized external notes')
|
|
self.assertEqual(injury_for_anon.internal_notes, 'Anonymized internal notes')
|