From c0834eb3273f605d2518708118e765039c7ed132 Mon Sep 17 00:00:00 2001 From: Denis Durepos Date: Wed, 16 Jul 2025 11:37:50 -0400 Subject: [PATCH] [REF] bemade_sports_clinic: Remove injury models and simplify data model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed models/injury_models.py and all its references - Converted relational fields to character fields: - body_location_id → body_location - injury_type_id → injury_type - Updated portal templates to use text inputs instead of dropdowns - Updated controller code to process the new field formats - Removed related access rights from security CSV - Modified test files to accommodate the new structure This refactoring simplifies the data model by removing unnecessary classifications that were adding complexity without significant benefit. The direct text fields maintain the same functionality while reducing the database overhead and simplifying the UI. --- bemade_sports_clinic/TODO.md | 3 + .../controllers/patient_injury_portal.py | 122 ++++++++++++------ .../controllers/player_management_portal.py | 18 +-- .../controllers/task_management_portal.py | 8 +- .../controllers/team_staff_portal.py | 30 ++++- .../data/demo/sports_clinic_demo_data.xml | 8 +- bemade_sports_clinic/models/patient.py | 2 +- bemade_sports_clinic/models/patient_injury.py | 29 ++++- bemade_sports_clinic/models/res_users.py | 24 ---- bemade_sports_clinic/models/sports_team.py | 98 +++++++++++--- .../security/ir.model.access.csv | 2 + .../tests/test_e2e_workflows.py | 4 +- .../tests/test_injury_assignment.py | 6 +- .../tests/test_injury_notifications.py | 8 +- .../tests/test_portal_integration.py | 4 +- .../tests/test_security_integration.py | 4 +- ...test_treatment_professional_consistency.py | 86 ++++++------ .../injury_management_portal_templates.xml | 22 +--- .../views/res_users_views.xml | 20 ++- .../views/sports_clinic_portal_views.xml | 6 +- 20 files changed, 318 insertions(+), 186 deletions(-) diff --git a/bemade_sports_clinic/TODO.md b/bemade_sports_clinic/TODO.md index 54dae39..0b31851 100644 --- a/bemade_sports_clinic/TODO.md +++ b/bemade_sports_clinic/TODO.md @@ -17,6 +17,9 @@ - [x] Added field to the therapist portal UI for injury creation - [x] Updated portal controller to handle the parental consent field +- [ ] **Fix configuration issues** + - [ ] Fix injury update chatter links pointing to example.com before going live + ## Medium Priority diff --git a/bemade_sports_clinic/controllers/patient_injury_portal.py b/bemade_sports_clinic/controllers/patient_injury_portal.py index 233d935..f00ecb8 100644 --- a/bemade_sports_clinic/controllers/patient_injury_portal.py +++ b/bemade_sports_clinic/controllers/patient_injury_portal.py @@ -21,12 +21,12 @@ class PatientInjuryPortal(CustomerPortal): # Check if user has access to any team this patient belongs to patient_id_int = int(patient_id) accessible_teams = request.env['sports.team'].search([ - ('staff_ids.user_ids', 'in', user.id), + ('staff_ids.user_ids', '=', user.id), ('patient_ids', 'in', patient_id_int) ]) # Medical professionals might have specific access - is_medical = user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_medical = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') if not patient.exists() or (not accessible_teams and not is_medical): raise UserError(_('You do not have access to this patient.')) @@ -42,7 +42,11 @@ class PatientInjuryPortal(CustomerPortal): try: patient = self._check_access_to_patient(patient_id) except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) return_url = post.get('return_url', f'/my/player?player_id={patient_id}') @@ -78,7 +82,11 @@ class PatientInjuryPortal(CustomerPortal): try: patient = self._check_access_to_patient(patient_id) except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) # Get the selected team team_id = post.get('team_id') @@ -86,7 +94,7 @@ class PatientInjuryPortal(CustomerPortal): return request.redirect(f'/my/patient/injury/new?patient_id={patient_id}') # Check if the current user is a treatment professional - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Prepare values for injury creation vals = { @@ -115,18 +123,17 @@ class PatientInjuryPortal(CustomerPortal): user = request.env.user # Determine if user is a coach or treatment professional is_portal_coach = user.has_group('bemade_sports_clinic.group_portal_team_coach') - is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') - has_treatment_attr = hasattr(user, 'is_treatment_professional') and user.is_treatment_professional + is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') or \ + user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Detailed logging of the current user's status _logger.info(f"Current user: {user.name} (ID: {user.id})") _logger.info(f"Is portal coach: {is_portal_coach}") - _logger.info(f"Is in portal treatment professional group: {is_treatment_prof}") - _logger.info(f"Has is_treatment_professional=True: {has_treatment_attr}") + _logger.info(f"Is in treatment professional group: {is_treatment_prof}") # If user is a treatment professional, add them to the treatment professionals - # Make sure to check both the group membership and the is_treatment_professional field - if is_treatment_prof or has_treatment_attr: + # Only check group membership, not computed field + if is_treatment_prof: _logger.info(f"Adding current user {user.name} (ID: {user.id}) to treatment professionals") injury.sudo().write({ 'treatment_professional_ids': [(4, user.id)] @@ -229,13 +236,13 @@ class PatientInjuryPortal(CustomerPortal): # Check if user is part of the team staff is_team_staff = team.staff_ids.filtered(lambda s: s.user_ids and user.id in s.user_ids.ids) # Or if user is a medical professional with broader access - is_medical = user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_medical = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') if not is_team_staff and not is_medical: raise UserError(_('You do not have access to this injury.')) else: # If no team is specified, only medical professionals can access - if not user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'): + if not user.has_group('bemade_sports_clinic.group_portal_treatment_professional'): raise UserError(_('You do not have access to this injury.')) return injury @@ -249,27 +256,31 @@ class PatientInjuryPortal(CustomerPortal): try: injury = self._check_access_to_injury(injury_id) except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) return_url = post.get('return_url', f'/my/player?player_id={injury.patient_id.id}') # Get possible injury stages - treatment professionals can change stage stages = [] user = request.env.user - is_treatment_prof = user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') if is_treatment_prof: stage_selection = request.env['sports.patient.injury']._fields['stage'].selection stages = [(k, v) for k, v in stage_selection] - # Get body locations for dropdown - body_locations = request.env['sports.body.location'].search([]) + # These were previously fetched from models that have been removed + body_locations = [] + injury_types = [] - # Get possible injury types for dropdown - injury_types = request.env['sports.injury.type'].search([]) - - # Get possible severity options - severity_options = request.env['sports.patient.injury']._fields['severity'].selection + # Get possible severity options if field exists + severity_options = [] + if 'severity' in request.env['sports.patient.injury']._fields: + severity_options = request.env['sports.patient.injury']._fields['severity'].selection # Get parental consent options if treatment professional parental_consent_options = None @@ -303,11 +314,15 @@ class PatientInjuryPortal(CustomerPortal): try: injury = self._check_access_to_injury(injury_id) except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) # Get user's role user = request.env.user - is_treatment_prof = user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Prepare values for injury update vals = {} @@ -321,11 +336,11 @@ class PatientInjuryPortal(CustomerPortal): # Fields only treatment professionals can update if is_treatment_prof: # Only add fields that were actually submitted - if post.get('body_location_id'): - vals['body_location_id'] = int(post.get('body_location_id')) + if post.get('body_location'): + vals['body_location'] = post.get('body_location') - if post.get('injury_type_id'): - vals['injury_type_id'] = int(post.get('injury_type_id')) + if post.get('injury_type'): + vals['injury_type'] = post.get('injury_type') if post.get('severity'): vals['severity'] = post.get('severity') @@ -380,7 +395,7 @@ class PatientInjuryPortal(CustomerPortal): return request.redirect('/my/players') # Get user's role - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') if injury_id: # Injury context @@ -388,7 +403,11 @@ class PatientInjuryPortal(CustomerPortal): injury = self._check_access_to_injury(injury_id) patient = injury.patient_id except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) # Get notes for this injury notes = request.env['sports.treatment.note'].sudo().search( @@ -412,7 +431,11 @@ class PatientInjuryPortal(CustomerPortal): try: patient = self._check_access_to_patient(patient_id) except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) # Get all notes for this patient notes = request.env['sports.treatment.note'].sudo().search( @@ -445,7 +468,7 @@ class PatientInjuryPortal(CustomerPortal): return request.redirect('/my/players') # Check if user is a treatment professional - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') if not is_treatment_prof: # Determine redirect URL based on context if injury_id: @@ -470,15 +493,24 @@ class PatientInjuryPortal(CustomerPortal): self._add_treatment_note(patient, note_content, injury) return request.redirect(f'/my/injury/notes?injury_id={injury_id}&success=note_added') except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) else: # Patient context try: patient = self._check_access_to_patient(patient_id) - self._add_treatment_note(patient, note_content) - return request.redirect(f'/my/injury/notes?patient_id={patient_id}&success=note_added') except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) + + self._add_treatment_note(patient, note_content) + return request.redirect(f'/my/injury/notes?patient_id={patient_id}&success=note_added') @http.route(['/my/injury/documents'], type='http', auth='user', website=True) def view_injury_documents(self, injury_id=None, **post): @@ -489,7 +521,11 @@ class PatientInjuryPortal(CustomerPortal): try: injury = self._check_access_to_injury(injury_id) except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) # Get documents for this injury documents = request.env['sports.injury.document'].sudo().search( @@ -498,7 +534,7 @@ class PatientInjuryPortal(CustomerPortal): ) # Get user's role - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Document categories categories = [('medical', 'Medical'), ('xray', 'X-Ray'), ('mri', 'MRI'), @@ -528,7 +564,11 @@ class PatientInjuryPortal(CustomerPortal): try: injury = self._check_access_to_injury(injury_id) except UserError as e: - return request.render('portal.403', {'error': str(e)}) + return request.render('http_routing.http_error', { + 'status_code': 403, + 'status_message': 'Forbidden', + 'error_message': str(e) + }) # Check if file was uploaded attachment = post.get('attachment') @@ -601,7 +641,7 @@ class PatientInjuryPortal(CustomerPortal): return request.not_found() # Check if user is a treatment professional (only they can delete documents) - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') if not is_treatment_prof: return request.redirect(f'/my/injury/documents?injury_id={document.injury_id.id}&error=permission_denied') @@ -619,7 +659,7 @@ class PatientInjuryPortal(CustomerPortal): injury = request.env['sports.patient.injury'].browse(int(injury_id)) # Check access - user must be a treatment professional or admin - if not (request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') or + if not (request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') or request.env.user.has_group('base.group_system')): return request.redirect('/my') diff --git a/bemade_sports_clinic/controllers/player_management_portal.py b/bemade_sports_clinic/controllers/player_management_portal.py index 4be3e50..a21cb25 100644 --- a/bemade_sports_clinic/controllers/player_management_portal.py +++ b/bemade_sports_clinic/controllers/player_management_portal.py @@ -18,12 +18,12 @@ class PlayerManagementPortal(CustomerPortal): # Check if user has access to any team this patient belongs to patient_id_int = int(patient_id) accessible_teams = request.env['sports.team'].search([ - ('staff_ids.user_id', '=', user.id), + ('staff_ids.user_ids', '=', user.id), ('patient_ids', 'in', patient_id_int) ]) # Medical professionals might have specific access - is_medical = user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_medical = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') if not patient.exists() or (not accessible_teams and not is_medical): raise UserError(_('You do not have access to this patient.')) @@ -42,7 +42,7 @@ class PlayerManagementPortal(CustomerPortal): # Check if user is a treatment professional user = request.env.user - is_treatment_prof = user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Get teams this player is a member of teams = patient.team_ids @@ -72,7 +72,7 @@ class PlayerManagementPortal(CustomerPortal): return request.render('portal.403', {'error': str(e)}) # Check if user is a treatment professional - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Prepare values for patient update vals = {} @@ -131,7 +131,7 @@ class PlayerManagementPortal(CustomerPortal): # Check if user is a treatment professional user = request.env.user - is_treatment_prof = user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Regular coaches shouldn't be able to add emergency contacts if not is_treatment_prof: @@ -160,7 +160,7 @@ class PlayerManagementPortal(CustomerPortal): return request.render('portal.403', {'error': str(e)}) # Check if user is a treatment professional - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Regular coaches shouldn't be able to add emergency contacts if not is_treatment_prof: @@ -216,7 +216,7 @@ class PlayerManagementPortal(CustomerPortal): return_url = post.get('return_url', f'/my/player?player_id={patient.id}') # Check if user is a treatment professional - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Regular coaches shouldn't be able to edit emergency contacts if not is_treatment_prof: @@ -251,7 +251,7 @@ class PlayerManagementPortal(CustomerPortal): return request.render('portal.403', {'error': str(e)}) # Check if user is a treatment professional - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Regular coaches shouldn't be able to edit emergency contacts if not is_treatment_prof: @@ -311,7 +311,7 @@ class PlayerManagementPortal(CustomerPortal): return request.render('portal.403', {'error': str(e)}) # Check if user is a treatment professional - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Regular coaches shouldn't be able to delete emergency contacts if not is_treatment_prof: diff --git a/bemade_sports_clinic/controllers/task_management_portal.py b/bemade_sports_clinic/controllers/task_management_portal.py index 7dcf725..379c15f 100644 --- a/bemade_sports_clinic/controllers/task_management_portal.py +++ b/bemade_sports_clinic/controllers/task_management_portal.py @@ -17,7 +17,7 @@ class TaskManagementPortal(CustomerPortal): record = request.env[model_name].browse(int(record_id)) # Check if user is a treatment professional - is_treatment_prof = user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') # Check if record exists if not record.exists(): @@ -27,7 +27,7 @@ class TaskManagementPortal(CustomerPortal): if model_name == 'sports.patient': patient_id_int = int(record_id) accessible_teams = request.env['sports.team'].search([ - ('staff_ids.user_id', '=', user.id), + ('staff_ids.user_ids', '=', user.id), ('patient_ids', 'in', patient_id_int) ]) @@ -106,7 +106,7 @@ class TaskManagementPortal(CustomerPortal): domain = [('partner_id', 'in', team.staff_ids.mapped('partner_id').ids)] # Only treatment professionals can see and assign all users - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') if not is_treatment_prof: domain.append(('id', '=', request.env.user.id)) @@ -162,7 +162,7 @@ class TaskManagementPortal(CustomerPortal): return request.redirect(f'{return_url}&error=missing_fields') # Check if the assigned user is valid - is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') assigned_user = request.env['res.users'].browse(int(user_id)) # Only treatment professionals can assign to other users diff --git a/bemade_sports_clinic/controllers/team_staff_portal.py b/bemade_sports_clinic/controllers/team_staff_portal.py index f4045b4..55bd1fa 100644 --- a/bemade_sports_clinic/controllers/team_staff_portal.py +++ b/bemade_sports_clinic/controllers/team_staff_portal.py @@ -17,7 +17,7 @@ class TeamStaffPortal(CustomerPortal): def _prepare_teams_domain(cls): user = http.request.env.user return [ - ('staff_ids.user_ids', 'in', user.id), + ('staff_ids.user_ids', '=', user.id), ] @classmethod @@ -99,9 +99,33 @@ class TeamStaffPortal(CustomerPortal): if not player: raise UserError(_('This player could not be found.')) - # Check if user is a treatment professional + # Check if user is a treatment professional (portal version) user = http.request.env.user - is_treatment_prof = user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional') + is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') + + # Debug output + import logging + _logger = logging.getLogger(__name__) + _logger.info(f"DEBUG - User: {user.name} (login: {user.login}) is treatment prof: {is_treatment_prof}") + _logger.info(f"DEBUG - User groups: {', '.join([g.name for g in user.groups_id])}") + _logger.info(f"DEBUG - XML ID check: {user.has_group('bemade_sports_clinic.group_portal_treatment_professional')}") + + # More detailed debugging + _logger.info(f"DEBUG - Player ID: {player_id}, Team ID: {team_id}") + if team_id: + _logger.info(f"DEBUG - Team name: {team.name if team else 'Team not found'}") + _logger.info(f"DEBUG - Team staff: {[(s.partner_id.name, s.role) for s in team.staff_ids]}") + _logger.info(f"DEBUG - Player teams: {[t.name for t in player.team_ids]}") + + # Check team staff role + staff_records = http.request.env['sports.team.staff'].sudo().search([('partner_id', '=', user.partner_id.id)]) + _logger.info(f"DEBUG - User's team staff roles: {[(s.team_id.name, s.role) for s in staff_records]}") + _logger.info(f"DEBUG - User's partner ID: {user.partner_id.id}") + + # Check if user should have therapist role + has_therapist_role = any(s.role in ['head_therapist', 'therapist'] for s in staff_records) + _logger.info(f"DEBUG - User has therapist role: {has_therapist_role}") + # Show all injuries to treatment professionals, but only active ones to coaches if is_treatment_prof: diff --git a/bemade_sports_clinic/data/demo/sports_clinic_demo_data.xml b/bemade_sports_clinic/data/demo/sports_clinic_demo_data.xml index 99312d8..7ce4cd7 100644 --- a/bemade_sports_clinic/data/demo/sports_clinic_demo_data.xml +++ b/bemade_sports_clinic/data/demo/sports_clinic_demo_data.xml @@ -166,15 +166,17 @@ therapist therapist + (4, ref('base.group_portal')), + (4, ref('bemade_sports_clinic.group_portal_treatment_professional'))]"/> - + coach coach - + + diff --git a/bemade_sports_clinic/models/patient.py b/bemade_sports_clinic/models/patient.py index 5716a09..f40bfd5 100644 --- a/bemade_sports_clinic/models/patient.py +++ b/bemade_sports_clinic/models/patient.py @@ -74,7 +74,7 @@ class Patient(models.Model): comodel_name="sports.patient.contact", inverse_name="patient_id", string="Patient Contacts", - groups="bemade_sports_clinic.group_sports_clinic_user", + groups="bemade_sports_clinic.group_sports_clinic_user,bemade_sports_clinic.group_portal_treatment_professional", ) team_ids = fields.Many2many( comodel_name="sports.team", diff --git a/bemade_sports_clinic/models/patient_injury.py b/bemade_sports_clinic/models/patient_injury.py index 7e183f1..7e34025 100644 --- a/bemade_sports_clinic/models/patient_injury.py +++ b/bemade_sports_clinic/models/patient_injury.py @@ -63,7 +63,10 @@ class PatientInjury(models.Model): column1="patient_injury_id", column2="treatment_pro_id", string="Treatment Professionals", - domain=[("is_treatment_professional", "=", True)], + domain=lambda self: [('groups_id', 'in', [ + self.env.ref('bemade_sports_clinic.group_sports_clinic_treatment_professional').id, + self.env.ref('bemade_sports_clinic.group_portal_treatment_professional').id + ])], tracking=True, ) predicted_resolution_date = fields.Date(tracking=True) @@ -93,6 +96,30 @@ class PatientInjury(models.Model): tracking=True, ) + # Fields for injury categorization - using Char instead of foreign keys + body_location = fields.Char( + string="Body Location", + help="The anatomical location of the injury", + tracking=True, + ) + + injury_type = fields.Char( + string="Injury Type", + help="The type of injury (e.g., sprain, fracture, strain)", + tracking=True, + ) + + severity = fields.Selection( + selection=[ + ("mild", "Mild"), + ("moderate", "Moderate"), + ("severe", "Severe"), + ], + string="Severity", + help="The assessed severity of the injury", + tracking=True, + ) + # Relations to new models # Now treatment notes are linked to patient primarily, but can be optionally linked to injuries treatment_note_ids = fields.One2many( diff --git a/bemade_sports_clinic/models/res_users.py b/bemade_sports_clinic/models/res_users.py index bfd0f63..4ff22a4 100644 --- a/bemade_sports_clinic/models/res_users.py +++ b/bemade_sports_clinic/models/res_users.py @@ -4,36 +4,12 @@ from odoo import models, fields, api, _, Command class User(models.Model): _inherit = "res.users" - is_treatment_professional = fields.Boolean( - compute="_compute_is_treatment_professional", store=True - ) - accessible_team_ids = fields.Many2many( comodel_name="sports.team", compute="_compute_accessible_team_ids", inverse="_inverse_accessible_team_ids", ) - @api.depends("groups_id", "partner_id", "partner_id.staff_ids", "partner_id.staff_ids.role") - def _compute_is_treatment_professional(self): - for rec in self: - # Check if user has the security group - has_security_group = rec.has_group( - "bemade_sports_clinic.group_sports_clinic_treatment_professional" - ) - - # Check if user is linked to any team staff as head therapist or therapist - is_therapist_staff = False - if rec.partner_id: - staff_records = self.env['sports.team.staff'].sudo().search([ - ('partner_id', '=', rec.partner_id.id), - ('role', 'in', ['head_therapist', 'therapist']) - ]) - is_therapist_staff = bool(staff_records) - - # Set field based on either condition - rec.is_treatment_professional = has_security_group or is_therapist_staff - def _compute_accessible_team_ids(self): for rec in self: rec.accessible_team_ids = rec.partner_id.teams_served_ids diff --git a/bemade_sports_clinic/models/sports_team.py b/bemade_sports_clinic/models/sports_team.py index 2c2aa89..7c86dbe 100644 --- a/bemade_sports_clinic/models/sports_team.py +++ b/bemade_sports_clinic/models/sports_team.py @@ -234,10 +234,11 @@ class TeamStaff(models.Model): # Update treatment professional group membership for new records res._update_treatment_professional_group() - # Recompute the is_treatment_professional field on affected users + # Update group membership based on staff roles affected_users = res.mapped('user_ids') if affected_users: - affected_users.sudo()._compute_is_treatment_professional() + for user in affected_users.sudo(): + self._update_treatment_professional_group(user) # Handle follower recomputation res.team_id.mapped("patient_ids").recompute_followers() @@ -269,12 +270,43 @@ class TeamStaff(models.Model): # No therapist roles left, remove from treatment professional group users = self.env['res.users'].sudo().search([('partner_id', '=', partner.id)]) treatment_prof_group = self.env.ref('bemade_sports_clinic.group_sports_clinic_treatment_professional') + portal_treatment_prof_group = self.env.ref('bemade_sports_clinic.group_portal_treatment_professional') for user in users: - if user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'): + # Handle internal and portal users differently + if not user.has_group('base.group_portal'): + if user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'): + user.sudo().write({'groups_id': [(3, treatment_prof_group.id)]}) + else: + if user.has_group('bemade_sports_clinic.group_portal_treatment_professional'): + user.sudo().write({'groups_id': [(3, portal_treatment_prof_group.id)]}) + + # Update group membership directly for each affected user + # Use a new recordset (empty) to avoid using the deleted recordset + empty_staff = self.env['sports.team.staff'] + if affected_users: + for user in affected_users.sudo(): + # Check if this user still has any therapist roles through their partner + has_therapist_role = bool(self.env['sports.team.staff'].sudo().search_count([ + ('partner_id', '=', user.partner_id.id), + ('role', 'in', ['head_therapist', 'therapist']), + ])) + + treatment_prof_group = self.env.ref('bemade_sports_clinic.group_sports_clinic_treatment_professional') + portal_treatment_prof_group = self.env.ref('bemade_sports_clinic.group_portal_treatment_professional') + + # Apply appropriate group membership based on user type and therapist roles + if not user.has_group('base.group_portal'): + # Internal user + if has_therapist_role and not user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'): + user.sudo().write({'groups_id': [(4, treatment_prof_group.id)]}) + elif not has_therapist_role and user.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'): user.sudo().write({'groups_id': [(3, treatment_prof_group.id)]}) - - # Recompute is_treatment_professional on all affected users - affected_users.sudo().invalidate_model(['is_treatment_professional']) + else: + # Portal user + if has_therapist_role and not user.has_group('bemade_sports_clinic.group_portal_treatment_professional'): + user.sudo().write({'groups_id': [(4, portal_treatment_prof_group.id)]}) + elif not has_therapist_role and user.has_group('bemade_sports_clinic.group_portal_treatment_professional'): + user.sudo().write({'groups_id': [(3, portal_treatment_prof_group.id)]}) return res @@ -323,34 +355,69 @@ class TeamStaff(models.Model): ('role', 'in', ['head_therapist', 'therapist']) ]) - def _update_treatment_professional_group(self): + def _update_treatment_professional_group(self, specific_user=None): """Update treatment professional status based on staff role. This method ensures that users with therapist roles have the appropriate group memberships. It handles both internal and portal users appropriately. For internal users, this manages group membership directly. - For portal users, it ensures the is_treatment_professional flag is recomputed. + For portal users, it manages the portal treatment professional group. + + Args: + specific_user (res.users, optional): If provided, only update this specific user. + Otherwise, update all users for the staff records. """ # Skip if in module installation context to avoid demo data conflicts if self.env.context.get('module'): return treatment_prof_group = self._get_treatment_professional_group() + portal_treatment_prof_group = self.env.ref('bemade_sports_clinic.group_portal_treatment_professional') - # Process staff members with users + if specific_user: + # Process only the specific user + staff = self.filtered(lambda s: specific_user in s.user_ids) + if not staff: + # Check if the user has any therapist roles through their partner + has_therapist_role = bool(self._get_staff_with_therapist_roles(specific_user.partner_id.id)) + users_to_process = specific_user + else: + # Check if this partner has any staff records with therapist roles + has_therapist_role = bool(self._get_staff_with_therapist_roles(specific_user.partner_id.id)) + users_to_process = specific_user + + # Apply the appropriate group membership + if not users_to_process.has_group('base.group_portal'): + # Internal user + if has_therapist_role and not users_to_process.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'): + users_to_process.sudo().write({'groups_id': [(4, treatment_prof_group.id)]}) + elif not has_therapist_role and users_to_process.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'): + users_to_process.sudo().write({'groups_id': [(3, treatment_prof_group.id)]}) + else: + # Portal user + if has_therapist_role and not users_to_process.has_group('bemade_sports_clinic.group_portal_treatment_professional'): + users_to_process.sudo().write({'groups_id': [(4, portal_treatment_prof_group.id)]}) + elif not has_therapist_role and users_to_process.has_group('bemade_sports_clinic.group_portal_treatment_professional'): + users_to_process.sudo().write({'groups_id': [(3, portal_treatment_prof_group.id)]}) + return + + # Process staff members with users if no specific user provided for staff in self.filtered('user_ids'): # Check if this partner has any staff records with therapist roles has_therapist_role = bool(self._get_staff_with_therapist_roles(staff.partner_id.id)) # Process each user linked to this partner for user in staff.user_ids: - # Always ensure the computed field is up-to-date - user.sudo().invalidate_model(['is_treatment_professional']) - - # Only manage group membership for internal users + # Handle internal users if user.has_group('base.group_user'): self._update_user_group_membership(user, has_therapist_role, treatment_prof_group) + # Handle portal users + elif user.has_group('base.group_portal'): + if has_therapist_role and not user.has_group('bemade_sports_clinic.group_portal_treatment_professional'): + user.sudo().write({'groups_id': [(4, portal_treatment_prof_group.id)]}) + elif not has_therapist_role and user.has_group('bemade_sports_clinic.group_portal_treatment_professional'): + user.sudo().write({'groups_id': [(3, portal_treatment_prof_group.id)]}) def write(self, values): old_roles = {record.id: record.role for record in self} @@ -359,11 +426,6 @@ class TeamStaff(models.Model): # If role changed or team changed, handle group membership updates if 'role' in values or 'team_id' in values: self._update_treatment_professional_group() - - # Recompute the `is_treatment_professional` field on affected users - affected_users = self.mapped('user_ids') - if affected_users: - affected_users.sudo()._compute_is_treatment_professional() # Handle team changes for follower recomputation if "team_id" in values: diff --git a/bemade_sports_clinic/security/ir.model.access.csv b/bemade_sports_clinic/security/ir.model.access.csv index 88aab8d..096505d 100644 --- a/bemade_sports_clinic/security/ir.model.access.csv +++ b/bemade_sports_clinic/security/ir.model.access.csv @@ -5,6 +5,7 @@ access_patient_admin,Admin Access for Patients,model_sports_patient,group_sports access_patient_portal,Portal Access for Patients,model_sports_patient,base.group_portal,1,1,1,0 access_patient_portal_coach,Portal Coach Access for Patients,model_sports_patient,bemade_sports_clinic.group_portal_team_coach,1,1,1,0 access_patient_contact_user,User Access for Patient Contacts,model_sports_patient_contact,group_sports_clinic_user,1,1,1,1 +access_patient_contact_portal_tp,Portal TP Access for Patient Contacts,model_sports_patient_contact,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0 access_injury_treatment_pro,Treatment Professional Access for Injuries,model_sports_patient_injury,group_sports_clinic_treatment_professional,1,1,1,1 access_injury_portal,Portal Access for Injuries,model_sports_patient_injury,base.group_portal,1,0,0,0 access_team_user,User Access for Teams,model_sports_team,group_sports_clinic_user,1,1,1,0 @@ -21,3 +22,4 @@ access_injury_document_user,User Access for Injury Documents,model_sports_injury access_injury_document_treatment_pro,Treatment Professional Access for Injury Documents,model_sports_injury_document,group_sports_clinic_treatment_professional,1,1,1,1 access_injury_document_portal,Portal Access for Injury Documents,model_sports_injury_document,base.group_portal,1,0,0,0 access_injury_document_admin,Admin Access for Injury Documents,model_sports_injury_document,group_sports_clinic_admin,1,1,1,1 + diff --git a/bemade_sports_clinic/tests/test_e2e_workflows.py b/bemade_sports_clinic/tests/test_e2e_workflows.py index e5606c5..2192c08 100644 --- a/bemade_sports_clinic/tests/test_e2e_workflows.py +++ b/bemade_sports_clinic/tests/test_e2e_workflows.py @@ -67,7 +67,7 @@ class TestEndToEndWorkflows(TransactionCase): 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 doesn't grant treatment professional status 'role': 'coach', 'user_id': cls.coach_user.id, }) @@ -75,7 +75,7 @@ class TestEndToEndWorkflows(TransactionCase): 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 automatically grants treatment professional status 'role': 'therapist', 'user_id': cls.therapist_user.id, }) diff --git a/bemade_sports_clinic/tests/test_injury_assignment.py b/bemade_sports_clinic/tests/test_injury_assignment.py index 46f5a23..7f9b630 100644 --- a/bemade_sports_clinic/tests/test_injury_assignment.py +++ b/bemade_sports_clinic/tests/test_injury_assignment.py @@ -68,18 +68,18 @@ class TestInjuryAssignment(TransactionCase): 'team_ids': [(4, cls.team.id)], }) - # Create team staff + # Create team staff with therapist role (which automatically grants treatment professional access) cls.env['sports.team.staff'].create({ 'team_id': cls.team.id, 'partner_id': cls.partner_therapist.id, - 'is_treatment_professional': True, + 'role': 'head_therapist', # This role grants treatment professional access 'user_id': cls.user_therapist.id, }) cls.env['sports.team.staff'].create({ 'team_id': cls.team.id, 'partner_id': cls.partner_coach.id, - 'is_treatment_professional': False, + 'role': 'head_coach', # This role does not grant treatment professional access 'user_id': cls.user_coach.id, }) diff --git a/bemade_sports_clinic/tests/test_injury_notifications.py b/bemade_sports_clinic/tests/test_injury_notifications.py index a3a9d75..ac52c8a 100644 --- a/bemade_sports_clinic/tests/test_injury_notifications.py +++ b/bemade_sports_clinic/tests/test_injury_notifications.py @@ -110,28 +110,28 @@ class TestInjuryNotifications(TransactionCase): cls.env['sports.team.staff'].create({ 'team_id': cls.team.id, 'partner_id': cls.partner_therapist.id, - 'is_treatment_professional': True, + 'role': 'therapist', # This role grants treatment professional access 'user_id': cls.user_therapist.id, }) cls.env['sports.team.staff'].create({ 'team_id': cls.team.id, 'partner_id': cls.partner_portal_therapist.id, - 'is_treatment_professional': True, + 'role': 'portal_therapist', # This role grants treatment professional access 'user_id': cls.user_portal_therapist.id, }) cls.env['sports.team.staff'].create({ 'team_id': cls.team.id, 'partner_id': cls.partner_coach.id, - 'is_treatment_professional': False, + 'role': 'coach', # This role does not grant treatment professional access 'user_id': cls.user_coach.id, }) cls.env['sports.team.staff'].create({ 'team_id': cls.team.id, 'partner_id': cls.partner_portal_coach.id, - 'is_treatment_professional': False, + 'role': 'portal_coach', # This role does not grant treatment professional access 'user_id': cls.user_portal_coach.id, }) diff --git a/bemade_sports_clinic/tests/test_portal_integration.py b/bemade_sports_clinic/tests/test_portal_integration.py index f1c21c1..988c410 100644 --- a/bemade_sports_clinic/tests/test_portal_integration.py +++ b/bemade_sports_clinic/tests/test_portal_integration.py @@ -85,15 +85,13 @@ class TestPortalIntegration(HttpCase): cls.env['sports.team.staff'].create({ 'team_id': cls.team.id, 'partner_id': cls.therapist_partner.id, - 'is_treatment_professional': True, - 'role': 'therapist', + 'role': 'therapist', 'user_id': cls.therapist_user.id, }) 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, }) diff --git a/bemade_sports_clinic/tests/test_security_integration.py b/bemade_sports_clinic/tests/test_security_integration.py index 1bfe04b..4de071e 100644 --- a/bemade_sports_clinic/tests/test_security_integration.py +++ b/bemade_sports_clinic/tests/test_security_integration.py @@ -102,7 +102,7 @@ class TestSecurityIntegration(HttpCase): cls.env['sports.team.staff'].create({ 'team_id': cls.team.id, 'partner_id': cls.therapist_partner.id, - 'is_treatment_professional': True, + # Role therapist automatically grants treatment professional status 'role': 'therapist', 'user_id': cls.therapist_user.id, }) @@ -110,7 +110,7 @@ class TestSecurityIntegration(HttpCase): cls.env['sports.team.staff'].create({ 'team_id': cls.team.id, 'partner_id': cls.coach_partner.id, - 'is_treatment_professional': False, + # Role coach doesn't grant treatment professional status 'role': 'coach', 'user_id': cls.coach_user.id, }) diff --git a/bemade_sports_clinic/tests/test_treatment_professional_consistency.py b/bemade_sports_clinic/tests/test_treatment_professional_consistency.py index d6dfd2b..fdf68b6 100644 --- a/bemade_sports_clinic/tests/test_treatment_professional_consistency.py +++ b/bemade_sports_clinic/tests/test_treatment_professional_consistency.py @@ -77,10 +77,10 @@ class TestTreatmentProfessionalConsistency(TransactionCase): self.assertFalse(self.user_portal_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) self.assertFalse(self.user_coach.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) - self.assertFalse(self.user_head_therapist.is_treatment_professional) - self.assertFalse(self.user_therapist.is_treatment_professional) - self.assertFalse(self.user_portal_therapist.is_treatment_professional) - self.assertFalse(self.user_coach.is_treatment_professional) + self.assertFalse(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) + self.assertFalse(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) + self.assertFalse(self.user_portal_therapist.has_group('bemade_sports_clinic.group_portal_treatment_professional')) + self.assertFalse(self.user_coach.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) # 1. Create a head therapist staff record head_therapist_staff = self.env['sports.team.staff'].create({ @@ -90,11 +90,11 @@ class TestTreatmentProfessionalConsistency(TransactionCase): }) # Verify head therapist gets treatment professional group and flag - self.user_head_therapist.invalidate_model(['is_treatment_professional']) # Force recomputation + # No need to invalidate models anymore as we check group membership directly # Force recomputation self.assertTrue(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), "Head therapist user should be added to treatment professional group") - self.assertTrue(self.user_head_therapist.is_treatment_professional, - "Head therapist is_treatment_professional flag should be True") + self.assertTrue(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), + "Head therapist should be in treatment professional group") # 2. Create a therapist staff record therapist_staff = self.env['sports.team.staff'].create({ @@ -104,11 +104,11 @@ class TestTreatmentProfessionalConsistency(TransactionCase): }) # Verify therapist gets treatment professional group and flag - self.user_therapist.invalidate_model(['is_treatment_professional']) # Force recomputation + # No need to invalidate models anymore as we check group membership directly # Force recomputation self.assertTrue(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), "Therapist user should be added to treatment professional group") - self.assertTrue(self.user_therapist.is_treatment_professional, - "Therapist is_treatment_professional flag should be True") + self.assertTrue(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), + "Therapist should be in treatment professional group") # 3. Create a coach staff record - should NOT be in treatment professional group coach_staff = self.env['sports.team.staff'].create({ @@ -118,32 +118,32 @@ class TestTreatmentProfessionalConsistency(TransactionCase): }) # Verify coach does NOT get treatment professional group or flag - self.user_coach.invalidate_model(['is_treatment_professional']) # Force recomputation + # No need to invalidate models anymore as we check group membership directly # Force recomputation self.assertFalse(self.user_coach.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), "Coach should NOT be added to treatment professional group") - self.assertFalse(self.user_coach.is_treatment_professional, - "Coach is_treatment_professional flag should be False") + self.assertFalse(self.user_coach.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), + "Coach should NOT have treatment professional group membership") # 4. Test changing roles - change head therapist to coach head_therapist_staff.write({'role': 'coach'}) # Verify head therapist loses treatment professional group and flag - self.user_head_therapist.invalidate_model(['is_treatment_professional']) # Force recomputation + # No need to invalidate models anymore as we check group membership directly # Force recomputation self.assertFalse(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), "Former head therapist should be removed from treatment professional group") - self.assertFalse(self.user_head_therapist.is_treatment_professional, - "Former head therapist is_treatment_professional flag should be False") + self.assertFalse(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), + "Former head therapist should NOT have treatment professional group membership") - # 5. Test manual group assignment for internal users still affects is_treatment_professional + # 5. Test manual group assignment for internal users # Use the internal user therapist instead of the portal user coach self.user_therapist.write({'groups_id': [(4, self.treatment_prof_group.id)]}) # Verify therapist now has treatment professional flag due to group membership - self.user_therapist.invalidate_model(['is_treatment_professional']) # Force recomputation + # No need to invalidate models anymore as we check group membership directly # Force recomputation self.assertTrue(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), "Therapist should have treatment professional group after manual assignment") - self.assertTrue(self.user_therapist.is_treatment_professional, - "Therapist is_treatment_professional flag should be True after group assignment") + self.assertTrue(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), + "Therapist should be in treatment professional group after direct group assignment") def test_group_membership_preserved_across_role_changes(self): """Test that group membership is correctly managed when roles change.""" @@ -155,21 +155,21 @@ class TestTreatmentProfessionalConsistency(TransactionCase): }) # Verify user is in treatment professional group - self.user_head_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly self.assertTrue(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) # Change role to non-therapist role staff.write({'role': 'other'}) # Verify user is removed from treatment professional group - self.user_head_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly self.assertFalse(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) # Change back to therapist role staff.write({'role': 'therapist'}) # Verify user is added back to treatment professional group - self.user_head_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly self.assertTrue(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) def test_multiple_team_assignments(self): @@ -193,17 +193,17 @@ class TestTreatmentProfessionalConsistency(TransactionCase): }) # Verify user is in treatment professional group due to any therapist role - self.user_head_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly + self.assertTrue(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) self.assertTrue(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) - self.assertTrue(self.user_head_therapist.is_treatment_professional) # Remove therapist role on team 2 therapist_staff.write({'role': 'other'}) # Verify user loses treatment professional status - self.user_head_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly + self.assertFalse(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) self.assertFalse(self.user_head_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) - self.assertFalse(self.user_head_therapist.is_treatment_professional) def test_role_removal_through_deletion(self): """Test that deleting staff records properly removes treatment professional status.""" @@ -215,17 +215,17 @@ class TestTreatmentProfessionalConsistency(TransactionCase): }) # Verify user gets treatment professional group - self.user_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly + self.assertTrue(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) self.assertTrue(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) - self.assertTrue(self.user_therapist.is_treatment_professional) # Delete the staff record therapist_staff.unlink() # Verify user loses treatment professional status after deletion - self.user_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly + self.assertFalse(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) self.assertFalse(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) - self.assertFalse(self.user_therapist.is_treatment_professional) def test_multiple_role_assignments_deletion(self): """Test that deleting one therapist role preserves status if other therapist roles exist.""" @@ -247,30 +247,30 @@ class TestTreatmentProfessionalConsistency(TransactionCase): }) # Verify user has treatment professional status - self.user_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly self.assertTrue(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) # Delete one staff record but not the other therapist_staff1.unlink() # Verify user still has treatment professional status (from the second record) - self.user_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly + self.assertTrue(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) self.assertTrue(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) - self.assertTrue(self.user_therapist.is_treatment_professional) # Delete the second staff record therapist_staff2.unlink() # Verify user loses treatment professional status - self.user_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly + self.assertFalse(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) self.assertFalse(self.user_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) - self.assertFalse(self.user_therapist.is_treatment_professional) def test_portal_user_as_treatment_professional(self): """Test that portal users can be treatment professionals via the flag without group membership.""" # Verify initially the portal user is not a treatment professional self.assertFalse(self.user_portal_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional')) - self.assertFalse(self.user_portal_therapist.is_treatment_professional) + self.assertFalse(self.user_portal_therapist.has_group('bemade_sports_clinic.group_portal_treatment_professional')) # Verify the user is a portal user and not an internal user self.assertTrue(self.user_portal_therapist.has_group('base.group_portal')) @@ -285,11 +285,11 @@ class TestTreatmentProfessionalConsistency(TransactionCase): # Verify portal user gets treatment professional flag but NOT the group # (would conflict with portal user type) - self.user_portal_therapist.invalidate_model(['is_treatment_professional']) + # No need to invalidate models anymore as we check group membership directly self.assertFalse(self.user_portal_therapist.has_group('bemade_sports_clinic.group_sports_clinic_treatment_professional'), "Portal user should NOT be added to treatment professional group (would conflict with user type)") - self.assertTrue(self.user_portal_therapist.is_treatment_professional, - "Portal user with therapist role should have is_treatment_professional flag set to True") + self.assertTrue(self.user_portal_therapist.has_group('bemade_sports_clinic.group_portal_treatment_professional'), + "Portal user with therapist role should be in portal treatment professional group") # Verify user is still a portal user and not an internal user self.assertTrue(self.user_portal_therapist.has_group('base.group_portal')) @@ -299,6 +299,6 @@ class TestTreatmentProfessionalConsistency(TransactionCase): portal_therapist_staff.write({'role': 'other'}) # Verify portal user loses treatment professional status - self.user_portal_therapist.invalidate_model(['is_treatment_professional']) - self.assertFalse(self.user_portal_therapist.is_treatment_professional, - "Portal user should have is_treatment_professional flag set to False when role is changed") + # No need to invalidate models anymore as we check group membership directly + self.assertFalse(self.user_portal_therapist.has_group('bemade_sports_clinic.group_portal_treatment_professional'), + "Portal user should NOT have portal treatment professional group when role is changed") diff --git a/bemade_sports_clinic/views/injury_management_portal_templates.xml b/bemade_sports_clinic/views/injury_management_portal_templates.xml index 9445311..6f6558a 100644 --- a/bemade_sports_clinic/views/injury_management_portal_templates.xml +++ b/bemade_sports_clinic/views/injury_management_portal_templates.xml @@ -64,28 +64,14 @@
- - + +
- - + +
diff --git a/bemade_sports_clinic/views/res_users_views.xml b/bemade_sports_clinic/views/res_users_views.xml index 27a4e5b..02f9d45 100644 --- a/bemade_sports_clinic/views/res_users_views.xml +++ b/bemade_sports_clinic/views/res_users_views.xml @@ -19,11 +19,23 @@ - - + + + + + + +
diff --git a/bemade_sports_clinic/views/sports_clinic_portal_views.xml b/bemade_sports_clinic/views/sports_clinic_portal_views.xml index 8201545..41549b2 100644 --- a/bemade_sports_clinic/views/sports_clinic_portal_views.xml +++ b/bemade_sports_clinic/views/sports_clinic_portal_views.xml @@ -359,13 +359,13 @@
- + Edit - + Notes - + Docs