2025-07-11 21:47:50 -04:00
|
|
|
import logging
|
|
|
|
|
import base64
|
|
|
|
|
import io
|
|
|
|
|
from odoo import http, fields, _
|
|
|
|
|
from odoo.exceptions import UserError, ValidationError
|
|
|
|
|
from odoo.http import request
|
|
|
|
|
from odoo.addons.portal.controllers.portal import CustomerPortal, pager
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PatientInjuryPortal(CustomerPortal):
|
|
|
|
|
"""Controller for all injury reporting functionality in the portal"""
|
|
|
|
|
|
|
|
|
|
def _check_access_to_patient(self, patient_id):
|
|
|
|
|
"""Verify the user has access to this patient"""
|
|
|
|
|
user = request.env.user
|
|
|
|
|
patient = request.env['sports.patient'].browse(int(patient_id))
|
|
|
|
|
|
|
|
|
|
# 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([
|
2025-07-16 11:37:50 -04:00
|
|
|
('staff_ids.user_ids', '=', user.id),
|
2025-07-11 21:47:50 -04:00
|
|
|
('patient_ids', 'in', patient_id_int)
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
# Medical professionals might have specific access
|
2025-07-16 11:37:50 -04:00
|
|
|
is_medical = user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
if not patient.exists() or (not accessible_teams and not is_medical):
|
|
|
|
|
raise UserError(_('You do not have access to this patient.'))
|
|
|
|
|
|
|
|
|
|
return patient
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/patient/injury/new'], type='http', auth='user', website=True)
|
|
|
|
|
def create_injury_form(self, patient_id=None, **post):
|
|
|
|
|
"""Show form to create a new injury report"""
|
|
|
|
|
if not patient_id:
|
|
|
|
|
return request.redirect('/my/players')
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
patient = self._check_access_to_patient(patient_id)
|
|
|
|
|
except UserError as e:
|
2025-07-16 11:37:50 -04:00
|
|
|
return request.render('http_routing.http_error', {
|
|
|
|
|
'status_code': 403,
|
|
|
|
|
'status_message': 'Forbidden',
|
|
|
|
|
'error_message': str(e)
|
|
|
|
|
})
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
return_url = post.get('return_url', f'/my/player?player_id={patient_id}')
|
|
|
|
|
|
|
|
|
|
# Get patient's teams for the dropdown
|
|
|
|
|
teams = patient.team_ids
|
|
|
|
|
|
|
|
|
|
# Pre-selected team ID (when player is only on one team)
|
|
|
|
|
default_team_id = teams[0].id if len(teams) == 1 else None
|
|
|
|
|
|
|
|
|
|
# Check if user is a treatment professional
|
|
|
|
|
user = request.env.user
|
|
|
|
|
is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
|
|
|
|
|
|
|
|
|
values = {
|
|
|
|
|
'patient': patient,
|
|
|
|
|
'teams': teams, # Pass teams to the template for dropdown
|
|
|
|
|
'default_team_id': default_team_id, # Will be None if multiple teams
|
|
|
|
|
'return_url': return_url,
|
|
|
|
|
'page_name': 'report_injury',
|
|
|
|
|
'is_treatment_prof': is_treatment_prof, # Pass flag to template for conditional display
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return request.render('bemade_sports_clinic.portal_create_injury', values)
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/patient/injury/create'], type='http', auth='user', website=True, methods=['POST'])
|
|
|
|
|
def create_injury_submit(self, **post):
|
|
|
|
|
"""Process the form submission to create a new injury"""
|
|
|
|
|
patient_id = post.get('patient_id')
|
|
|
|
|
|
|
|
|
|
if not patient_id:
|
|
|
|
|
return request.redirect('/my/players')
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
patient = self._check_access_to_patient(patient_id)
|
|
|
|
|
except UserError as e:
|
2025-07-16 11:37:50 -04:00
|
|
|
return request.render('http_routing.http_error', {
|
|
|
|
|
'status_code': 403,
|
|
|
|
|
'status_message': 'Forbidden',
|
|
|
|
|
'error_message': str(e)
|
|
|
|
|
})
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Get the selected team
|
|
|
|
|
team_id = post.get('team_id')
|
|
|
|
|
if not team_id:
|
|
|
|
|
return request.redirect(f'/my/patient/injury/new?patient_id={patient_id}')
|
|
|
|
|
|
|
|
|
|
# Check if the current user is a treatment professional
|
2025-07-16 11:37:50 -04:00
|
|
|
is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Prepare values for injury creation
|
|
|
|
|
vals = {
|
|
|
|
|
'patient_id': int(patient_id),
|
|
|
|
|
'diagnosis': post.get('diagnosis'),
|
|
|
|
|
'external_notes': post.get('external_notes'),
|
|
|
|
|
# Set stage based on user role - treatment professionals create active injuries
|
|
|
|
|
'stage': 'active' if is_treatment_prof else 'unverified',
|
|
|
|
|
'patient_name': patient.name,
|
|
|
|
|
# Store which team the injury was reported for
|
|
|
|
|
'team_id': int(team_id),
|
|
|
|
|
# Store parental consent value
|
|
|
|
|
'parental_consent': post.get('parental_consent', 'no'),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Handle dates
|
|
|
|
|
if post.get('injury_date'):
|
|
|
|
|
vals['injury_date'] = post.get('injury_date')
|
|
|
|
|
|
|
|
|
|
if post.get('predicted_resolution_date'):
|
|
|
|
|
vals['predicted_resolution_date'] = post.get('predicted_resolution_date')
|
|
|
|
|
|
2025-07-22 21:07:51 -04:00
|
|
|
# Create the injury record - portal users now have create permission
|
|
|
|
|
injury = request.env['sports.patient.injury'].create(vals)
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
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')
|
2025-07-16 11:37:50 -04:00
|
|
|
is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional') or \
|
|
|
|
|
user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# 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}")
|
2025-07-16 11:37:50 -04:00
|
|
|
_logger.info(f"Is in treatment professional group: {is_treatment_prof}")
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# If user is a treatment professional, add them to the treatment professionals
|
2025-07-16 11:37:50 -04:00
|
|
|
# Only check group membership, not computed field
|
|
|
|
|
if is_treatment_prof:
|
2025-07-11 21:47:50 -04:00
|
|
|
_logger.info(f"Adding current user {user.name} (ID: {user.id}) to treatment professionals")
|
2025-07-22 21:07:51 -04:00
|
|
|
injury.write({
|
2025-07-11 21:47:50 -04:00
|
|
|
'treatment_professional_ids': [(4, user.id)]
|
|
|
|
|
})
|
|
|
|
|
else:
|
|
|
|
|
_logger.info(f"User {user.name} is not identified as a treatment professional, not adding to injury")
|
|
|
|
|
|
|
|
|
|
# Double-check who's assigned after our additions
|
2025-07-22 21:07:51 -04:00
|
|
|
treatment_profs = injury.treatment_professional_ids
|
2025-07-11 21:47:50 -04:00
|
|
|
_logger.info(f"Treatment professionals after assignment: {[u.name for u in treatment_profs]} (IDs: {treatment_profs.ids})")
|
|
|
|
|
|
|
|
|
|
# Always try to assign team therapists regardless of who created the injury
|
|
|
|
|
if True:
|
|
|
|
|
# Use the selected team_id from the form
|
|
|
|
|
selected_team_id = int(team_id)
|
|
|
|
|
|
|
|
|
|
# Find therapists specifically for this team
|
|
|
|
|
team_staff = request.env['sports.team.staff'].sudo().search([
|
|
|
|
|
('team_id', '=', selected_team_id), # Only from selected team
|
|
|
|
|
('role', 'in', ['head_therapist', 'therapist'])
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
# Log debug info
|
|
|
|
|
_logger.info(f"Found {len(team_staff)} therapists/head therapists for team {selected_team_id}")
|
|
|
|
|
for staff in team_staff:
|
|
|
|
|
_logger.info(f"Staff: {staff.partner_id.name}, Role: {staff.role}, User IDs: {[u.id for u in staff.user_ids]}")
|
|
|
|
|
if not staff.user_ids:
|
|
|
|
|
_logger.info(f" - WARNING: No user_ids for {staff.partner_id.name}")
|
|
|
|
|
# Try to find a user directly associated with this partner
|
|
|
|
|
users = request.env['res.users'].sudo().search([('partner_id', '=', staff.partner_id.id)])
|
|
|
|
|
_logger.info(f" - Found direct users: {[u.id for u in users]}")
|
|
|
|
|
|
|
|
|
|
# Filter by role
|
|
|
|
|
head_therapists = team_staff.filtered(lambda s: s.role == 'head_therapist')
|
|
|
|
|
therapists = team_staff.filtered(lambda s: s.role == 'therapist')
|
|
|
|
|
|
|
|
|
|
_logger.info(f"Found {len(head_therapists)} head therapists and {len(therapists)} therapists")
|
|
|
|
|
|
|
|
|
|
# First try to assign head therapist, then any therapist from the selected team
|
|
|
|
|
treatment_pros_assigned = False
|
|
|
|
|
|
|
|
|
|
# Try to assign head therapist first
|
|
|
|
|
if head_therapists:
|
|
|
|
|
# Find users associated directly with the head therapist partner
|
|
|
|
|
head_therapist = head_therapists[0]
|
2025-07-22 21:07:51 -04:00
|
|
|
users = request.env['res.users'].search([('partner_id', '=', head_therapist.partner_id.id)])
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
if users:
|
|
|
|
|
_logger.info(f"Assigning head therapist: {head_therapist.partner_id.name} with user ID {users[0].id}")
|
2025-07-22 21:07:51 -04:00
|
|
|
injury.write({
|
2025-07-11 21:47:50 -04:00
|
|
|
'treatment_professional_ids': [(4, users[0].id)]
|
|
|
|
|
})
|
|
|
|
|
treatment_pros_assigned = True
|
|
|
|
|
else:
|
|
|
|
|
_logger.info(f"No user account found for head therapist: {head_therapist.partner_id.name}")
|
|
|
|
|
|
|
|
|
|
# Try to assign regular therapist if no head therapist was assigned
|
|
|
|
|
if not treatment_pros_assigned and therapists:
|
|
|
|
|
# Find users associated directly with the therapist partner
|
|
|
|
|
therapist = therapists[0]
|
|
|
|
|
users = request.env['res.users'].sudo().search([('partner_id', '=', therapist.partner_id.id)])
|
|
|
|
|
|
|
|
|
|
if users:
|
|
|
|
|
_logger.info(f"Assigning therapist: {therapist.partner_id.name} with user ID {users[0].id}")
|
2025-07-22 21:07:51 -04:00
|
|
|
injury.write({
|
2025-07-11 21:47:50 -04:00
|
|
|
'treatment_professional_ids': [(4, users[0].id)]
|
|
|
|
|
})
|
|
|
|
|
treatment_pros_assigned = True
|
|
|
|
|
else:
|
|
|
|
|
_logger.info(f"No user account found for therapist: {therapist.partner_id.name}")
|
|
|
|
|
|
|
|
|
|
# If no therapist was assigned, log a warning
|
|
|
|
|
if not treatment_pros_assigned:
|
|
|
|
|
_logger.warning("No valid therapists found to assign to the injury")
|
|
|
|
|
|
|
|
|
|
# Trigger recomputation of patient status based on the injury
|
2025-07-22 21:07:51 -04:00
|
|
|
patient._compute_is_injured()
|
|
|
|
|
patient._compute_stage()
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
return_url = f'/my/player?player_id={patient_id}'
|
|
|
|
|
values = {
|
|
|
|
|
'return_url': return_url,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return request.render('bemade_sports_clinic.portal_injury_created', values)
|
|
|
|
|
|
|
|
|
|
def _check_access_to_injury(self, injury_id):
|
|
|
|
|
"""Verify the user has access to this injury"""
|
|
|
|
|
user = request.env.user
|
|
|
|
|
injury = request.env['sports.patient.injury'].browse(int(injury_id))
|
|
|
|
|
|
|
|
|
|
# Check if user has access to the team this injury is associated with
|
|
|
|
|
if not injury.exists():
|
|
|
|
|
raise UserError(_('Injury not found.'))
|
|
|
|
|
|
|
|
|
|
# Get the team from the injury
|
|
|
|
|
team = injury.team_id
|
|
|
|
|
|
|
|
|
|
if team:
|
|
|
|
|
# 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
|
2025-07-16 11:37:50 -04:00
|
|
|
is_medical = user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
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
|
2025-07-16 11:37:50 -04:00
|
|
|
if not user.has_group('bemade_sports_clinic.group_portal_treatment_professional'):
|
2025-07-11 21:47:50 -04:00
|
|
|
raise UserError(_('You do not have access to this injury.'))
|
|
|
|
|
|
|
|
|
|
return injury
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/injury/edit'], type='http', auth='user', website=True)
|
|
|
|
|
def edit_injury_form(self, injury_id=None, **post):
|
|
|
|
|
"""Show form to edit an existing injury"""
|
|
|
|
|
if not injury_id:
|
|
|
|
|
return request.redirect('/my/players')
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
injury = self._check_access_to_injury(injury_id)
|
|
|
|
|
except UserError as e:
|
2025-07-16 11:37:50 -04:00
|
|
|
return request.render('http_routing.http_error', {
|
|
|
|
|
'status_code': 403,
|
|
|
|
|
'status_message': 'Forbidden',
|
|
|
|
|
'error_message': str(e)
|
|
|
|
|
})
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
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
|
2025-07-16 11:37:50 -04:00
|
|
|
is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
if is_treatment_prof:
|
|
|
|
|
stage_selection = request.env['sports.patient.injury']._fields['stage'].selection
|
|
|
|
|
stages = [(k, v) for k, v in stage_selection]
|
|
|
|
|
|
2025-07-16 11:37:50 -04:00
|
|
|
# These were previously fetched from models that have been removed
|
|
|
|
|
body_locations = []
|
|
|
|
|
injury_types = []
|
2025-07-11 21:47:50 -04:00
|
|
|
|
2025-07-16 11:37:50 -04:00
|
|
|
# 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
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Get parental consent options if treatment professional
|
|
|
|
|
parental_consent_options = None
|
|
|
|
|
if is_treatment_prof:
|
|
|
|
|
parental_consent_options = request.env['sports.patient.injury']._fields['parental_consent'].selection
|
|
|
|
|
|
|
|
|
|
values = {
|
|
|
|
|
'injury': injury,
|
|
|
|
|
'stages': stages,
|
|
|
|
|
'body_locations': body_locations,
|
|
|
|
|
'injury_types': injury_types,
|
|
|
|
|
'severity_options': severity_options,
|
|
|
|
|
'parental_consent_options': parental_consent_options,
|
|
|
|
|
'return_url': return_url,
|
|
|
|
|
'is_treatment_prof': is_treatment_prof,
|
|
|
|
|
'page_name': 'edit_injury',
|
|
|
|
|
'error': post.get('error'),
|
|
|
|
|
'success': post.get('success'),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return request.render('bemade_sports_clinic.portal_edit_injury', values)
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/injury/save'], type='http', auth='user', website=True, methods=['POST'])
|
|
|
|
|
def edit_injury_submit(self, **post):
|
|
|
|
|
"""Process the form submission to update an injury"""
|
|
|
|
|
injury_id = post.get('injury_id')
|
|
|
|
|
|
|
|
|
|
if not injury_id:
|
|
|
|
|
return request.redirect('/my/players')
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
injury = self._check_access_to_injury(injury_id)
|
|
|
|
|
except UserError as e:
|
2025-07-16 11:37:50 -04:00
|
|
|
return request.render('http_routing.http_error', {
|
|
|
|
|
'status_code': 403,
|
|
|
|
|
'status_message': 'Forbidden',
|
|
|
|
|
'error_message': str(e)
|
|
|
|
|
})
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Get user's role
|
|
|
|
|
user = request.env.user
|
2025-07-16 11:37:50 -04:00
|
|
|
is_treatment_prof = user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Prepare values for injury update
|
|
|
|
|
vals = {}
|
|
|
|
|
|
|
|
|
|
# Fields everyone can update
|
|
|
|
|
vals.update({
|
|
|
|
|
'diagnosis': post.get('diagnosis', injury.diagnosis or ''),
|
|
|
|
|
'external_notes': post.get('external_notes', injury.external_notes or ''),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Fields only treatment professionals can update
|
|
|
|
|
if is_treatment_prof:
|
|
|
|
|
# Only add fields that were actually submitted
|
2025-07-16 11:37:50 -04:00
|
|
|
if post.get('body_location'):
|
|
|
|
|
vals['body_location'] = post.get('body_location')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
2025-07-16 11:37:50 -04:00
|
|
|
if post.get('injury_type'):
|
|
|
|
|
vals['injury_type'] = post.get('injury_type')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
if post.get('severity'):
|
|
|
|
|
vals['severity'] = post.get('severity')
|
|
|
|
|
|
|
|
|
|
if post.get('internal_notes'):
|
|
|
|
|
vals['internal_notes'] = post.get('internal_notes')
|
|
|
|
|
|
|
|
|
|
if post.get('stage'):
|
|
|
|
|
vals['stage'] = post.get('stage')
|
|
|
|
|
|
|
|
|
|
if post.get('parental_consent'):
|
|
|
|
|
vals['parental_consent'] = post.get('parental_consent')
|
|
|
|
|
|
|
|
|
|
# Update the injury
|
|
|
|
|
injury.sudo().write(vals)
|
|
|
|
|
|
|
|
|
|
# Add a treatment note if provided
|
|
|
|
|
if post.get('treatment_note') and is_treatment_prof:
|
|
|
|
|
self._add_treatment_note(injury, post.get('treatment_note'))
|
|
|
|
|
|
|
|
|
|
# Redirect back to the edit form with success message
|
|
|
|
|
return_url = post.get('return_url', f'/my/injury/edit?injury_id={injury_id}')
|
|
|
|
|
return request.redirect(f'{return_url}&success=injury_updated')
|
|
|
|
|
|
|
|
|
|
def _add_treatment_note(self, patient, note_content, injury=None):
|
|
|
|
|
"""Helper method to add a treatment note to a patient, optionally linked to an injury"""
|
|
|
|
|
if not note_content.strip():
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# Create a new treatment note linked to patient, optionally to injury
|
|
|
|
|
vals = {
|
|
|
|
|
'patient_id': patient.id,
|
|
|
|
|
'note': note_content,
|
|
|
|
|
'date': fields.Date.today(),
|
|
|
|
|
'user_id': request.env.user.id,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# If injury is provided, link the note to it
|
|
|
|
|
if injury:
|
|
|
|
|
vals['injury_id'] = injury.id
|
|
|
|
|
|
|
|
|
|
request.env['sports.treatment.note'].sudo().create(vals)
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/injury/notes'], type='http', auth='user', website=True)
|
|
|
|
|
def view_treatment_notes(self, injury_id=None, patient_id=None, **post):
|
|
|
|
|
"""View treatment notes for an injury or a patient"""
|
|
|
|
|
# Determine context - are we viewing injury-specific notes or all patient notes?
|
|
|
|
|
# At least one of injury_id or patient_id must be provided
|
|
|
|
|
if not injury_id and not patient_id:
|
|
|
|
|
return request.redirect('/my/players')
|
|
|
|
|
|
|
|
|
|
# Get user's role
|
2025-07-16 11:37:50 -04:00
|
|
|
is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
if injury_id:
|
|
|
|
|
# Injury context
|
|
|
|
|
try:
|
|
|
|
|
injury = self._check_access_to_injury(injury_id)
|
|
|
|
|
patient = injury.patient_id
|
|
|
|
|
except UserError as e:
|
2025-07-16 11:37:50 -04:00
|
|
|
return request.render('http_routing.http_error', {
|
|
|
|
|
'status_code': 403,
|
|
|
|
|
'status_message': 'Forbidden',
|
|
|
|
|
'error_message': str(e)
|
|
|
|
|
})
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Get notes for this injury
|
|
|
|
|
notes = request.env['sports.treatment.note'].sudo().search(
|
|
|
|
|
[('injury_id', '=', int(injury_id))],
|
|
|
|
|
order='date desc, id desc'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
values = {
|
|
|
|
|
'injury': injury,
|
|
|
|
|
'notes': notes,
|
|
|
|
|
'patient': patient,
|
|
|
|
|
'is_treatment_prof': is_treatment_prof,
|
|
|
|
|
'page_name': 'injury_notes',
|
|
|
|
|
'error': post.get('error'),
|
|
|
|
|
'success': post.get('success'),
|
|
|
|
|
'context': 'injury',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
# Patient context
|
|
|
|
|
try:
|
|
|
|
|
patient = self._check_access_to_patient(patient_id)
|
|
|
|
|
except UserError as e:
|
2025-07-16 11:37:50 -04:00
|
|
|
return request.render('http_routing.http_error', {
|
|
|
|
|
'status_code': 403,
|
|
|
|
|
'status_message': 'Forbidden',
|
|
|
|
|
'error_message': str(e)
|
|
|
|
|
})
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Get all notes for this patient
|
|
|
|
|
notes = request.env['sports.treatment.note'].sudo().search(
|
|
|
|
|
[('patient_id', '=', int(patient_id))],
|
|
|
|
|
order='date desc, id desc'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
values = {
|
|
|
|
|
'injury': None,
|
|
|
|
|
'notes': notes,
|
|
|
|
|
'patient': patient,
|
|
|
|
|
'is_treatment_prof': is_treatment_prof,
|
|
|
|
|
'page_name': 'patient_notes',
|
|
|
|
|
'error': post.get('error'),
|
|
|
|
|
'success': post.get('success'),
|
|
|
|
|
'context': 'patient',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return request.render('bemade_sports_clinic.portal_treatment_notes', values)
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/injury/note/add'], type='http', auth='user', website=True, methods=['POST'])
|
|
|
|
|
def add_treatment_note(self, **post):
|
|
|
|
|
"""Add a new treatment note to a patient, optionally linked to an injury"""
|
|
|
|
|
# Get context - are we adding a note to an injury or just to a patient?
|
|
|
|
|
injury_id = post.get('injury_id')
|
|
|
|
|
patient_id = post.get('patient_id')
|
|
|
|
|
|
|
|
|
|
# Either injury_id or patient_id must be provided
|
|
|
|
|
if not injury_id and not patient_id:
|
|
|
|
|
return request.redirect('/my/players')
|
|
|
|
|
|
|
|
|
|
# Check if user is a treatment professional
|
2025-07-16 11:37:50 -04:00
|
|
|
is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
if not is_treatment_prof:
|
|
|
|
|
# Determine redirect URL based on context
|
|
|
|
|
if injury_id:
|
|
|
|
|
return request.redirect(f'/my/injury/notes?injury_id={injury_id}&error=permission_denied')
|
|
|
|
|
else:
|
|
|
|
|
return request.redirect(f'/my/injury/notes?patient_id={patient_id}&error=permission_denied')
|
|
|
|
|
|
|
|
|
|
# Get note content and validate
|
|
|
|
|
note_content = post.get('note')
|
|
|
|
|
if not note_content or not note_content.strip():
|
|
|
|
|
if injury_id:
|
|
|
|
|
return request.redirect(f'/my/injury/notes?injury_id={injury_id}&error=empty_note')
|
|
|
|
|
else:
|
|
|
|
|
return request.redirect(f'/my/injury/notes?patient_id={patient_id}&error=empty_note')
|
|
|
|
|
|
|
|
|
|
# Determine context and add the note
|
|
|
|
|
if injury_id:
|
|
|
|
|
# Injury context
|
|
|
|
|
try:
|
|
|
|
|
injury = self._check_access_to_injury(injury_id)
|
|
|
|
|
patient = injury.patient_id
|
|
|
|
|
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:
|
2025-07-16 11:37:50 -04:00
|
|
|
return request.render('http_routing.http_error', {
|
|
|
|
|
'status_code': 403,
|
|
|
|
|
'status_message': 'Forbidden',
|
|
|
|
|
'error_message': str(e)
|
|
|
|
|
})
|
2025-07-11 21:47:50 -04:00
|
|
|
else:
|
|
|
|
|
# Patient context
|
|
|
|
|
try:
|
|
|
|
|
patient = self._check_access_to_patient(patient_id)
|
|
|
|
|
except UserError as e:
|
2025-07-16 11:37:50 -04:00
|
|
|
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')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
@http.route(['/my/injury/documents'], type='http', auth='user', website=True)
|
|
|
|
|
def view_injury_documents(self, injury_id=None, **post):
|
|
|
|
|
"""View documents attached to an injury"""
|
|
|
|
|
if not injury_id:
|
|
|
|
|
return request.redirect('/my/players')
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
injury = self._check_access_to_injury(injury_id)
|
|
|
|
|
except UserError as e:
|
2025-07-16 11:37:50 -04:00
|
|
|
return request.render('http_routing.http_error', {
|
|
|
|
|
'status_code': 403,
|
|
|
|
|
'status_message': 'Forbidden',
|
|
|
|
|
'error_message': str(e)
|
|
|
|
|
})
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Get documents for this injury
|
|
|
|
|
documents = request.env['sports.injury.document'].sudo().search(
|
|
|
|
|
[('injury_id', '=', int(injury_id))],
|
|
|
|
|
order='create_date desc'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Get user's role
|
2025-07-16 11:37:50 -04:00
|
|
|
is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Document categories
|
|
|
|
|
categories = [('medical', 'Medical'), ('xray', 'X-Ray'), ('mri', 'MRI'),
|
|
|
|
|
('prescription', 'Prescription'), ('other', 'Other')]
|
|
|
|
|
|
|
|
|
|
values = {
|
|
|
|
|
'injury': injury,
|
|
|
|
|
'documents': documents,
|
|
|
|
|
'patient': injury.patient_id,
|
|
|
|
|
'is_treatment_prof': is_treatment_prof,
|
|
|
|
|
'page_name': 'injury_documents',
|
|
|
|
|
'error': post.get('error'),
|
|
|
|
|
'success': post.get('success'),
|
|
|
|
|
'categories': categories,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return request.render('bemade_sports_clinic.portal_injury_documents', values)
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/injury/document/upload'], type='http', auth='user', website=True, methods=['POST'])
|
|
|
|
|
def upload_injury_document(self, **post):
|
|
|
|
|
"""Upload a document for an injury"""
|
|
|
|
|
injury_id = post.get('injury_id')
|
|
|
|
|
|
|
|
|
|
if not injury_id:
|
|
|
|
|
return request.redirect('/my/players')
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
injury = self._check_access_to_injury(injury_id)
|
|
|
|
|
except UserError as e:
|
2025-07-16 11:37:50 -04:00
|
|
|
return request.render('http_routing.http_error', {
|
|
|
|
|
'status_code': 403,
|
|
|
|
|
'status_message': 'Forbidden',
|
|
|
|
|
'error_message': str(e)
|
|
|
|
|
})
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Check if file was uploaded
|
|
|
|
|
attachment = post.get('attachment')
|
|
|
|
|
if not attachment:
|
|
|
|
|
return request.redirect(f'/my/injury/documents?injury_id={injury_id}&error=no_file')
|
|
|
|
|
|
|
|
|
|
# Process the file
|
|
|
|
|
try:
|
|
|
|
|
name = attachment.filename
|
|
|
|
|
file_content = attachment.read()
|
|
|
|
|
file_size = len(file_content)
|
|
|
|
|
|
|
|
|
|
# Check file size (limit to 10MB)
|
|
|
|
|
if file_size > 10 * 1024 * 1024: # 10MB in bytes
|
|
|
|
|
return request.redirect(f'/my/injury/documents?injury_id={injury_id}&error=file_too_large')
|
|
|
|
|
|
|
|
|
|
# Create the document
|
|
|
|
|
document = request.env['sports.injury.document'].sudo().create({
|
|
|
|
|
'injury_id': int(injury_id),
|
|
|
|
|
'name': post.get('document_name', name),
|
|
|
|
|
'description': post.get('description', ''),
|
|
|
|
|
'category': post.get('category', 'other'),
|
|
|
|
|
'file_content': base64.b64encode(file_content),
|
|
|
|
|
'file_name': name,
|
|
|
|
|
'created_by_id': request.env.user.id,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Redirect back to documents page with success message
|
|
|
|
|
return request.redirect(f'/my/injury/documents?injury_id={injury_id}&success=document_uploaded')
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
_logger.error(f"Error uploading document: {e}")
|
|
|
|
|
return request.redirect(f'/my/injury/documents?injury_id={injury_id}&error=upload_failed')
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/injury/document/download/<int:document_id>'], type='http', auth='user')
|
|
|
|
|
def download_injury_document(self, document_id, **post):
|
|
|
|
|
"""Download a document attached to an injury"""
|
|
|
|
|
document = request.env['sports.injury.document'].sudo().browse(int(document_id))
|
|
|
|
|
|
|
|
|
|
if not document.exists():
|
|
|
|
|
return request.not_found()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# Check access to the injury this document belongs to
|
|
|
|
|
injury = self._check_access_to_injury(document.injury_id.id)
|
|
|
|
|
except UserError:
|
|
|
|
|
return request.not_found()
|
|
|
|
|
|
|
|
|
|
# Return the file for download
|
|
|
|
|
return request.make_response(
|
|
|
|
|
base64.b64decode(document.file_content),
|
|
|
|
|
headers=[
|
|
|
|
|
('Content-Type', 'application/octet-stream'),
|
|
|
|
|
('Content-Disposition', f'attachment; filename="{document.file_name}"'),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/injury/document/delete/<int:document_id>'], type='http', auth='user', website=True)
|
|
|
|
|
def delete_injury_document(self, document_id, **post):
|
|
|
|
|
"""Delete a document attached to an injury"""
|
|
|
|
|
document = request.env['sports.injury.document'].sudo().browse(int(document_id))
|
|
|
|
|
|
|
|
|
|
if not document.exists():
|
|
|
|
|
return request.not_found()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# Check access to the injury this document belongs to
|
|
|
|
|
injury = self._check_access_to_injury(document.injury_id.id)
|
|
|
|
|
except UserError:
|
|
|
|
|
return request.not_found()
|
|
|
|
|
|
|
|
|
|
# Check if user is a treatment professional (only they can delete documents)
|
2025-07-16 11:37:50 -04:00
|
|
|
is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
|
2025-07-11 21:47:50 -04:00
|
|
|
if not is_treatment_prof:
|
|
|
|
|
return request.redirect(f'/my/injury/documents?injury_id={document.injury_id.id}&error=permission_denied')
|
|
|
|
|
|
|
|
|
|
# Delete the document
|
|
|
|
|
injury_id = document.injury_id.id
|
|
|
|
|
document.sudo().unlink()
|
|
|
|
|
|
|
|
|
|
# Redirect back to documents page with success message
|
|
|
|
|
return request.redirect(f'/my/injury/documents?injury_id={injury_id}&success=document_deleted')
|
|
|
|
|
|
|
|
|
|
@http.route(['/my/injury/verify'], type='http', auth='user', website=True, methods=['POST'])
|
|
|
|
|
def verify_injury(self, injury_id, **post):
|
|
|
|
|
"""Verify an injury (change status from unverified to active)"""
|
|
|
|
|
try:
|
|
|
|
|
injury = request.env['sports.patient.injury'].browse(int(injury_id))
|
|
|
|
|
|
|
|
|
|
# Check access - user must be a treatment professional or admin
|
2025-07-16 11:37:50 -04:00
|
|
|
if not (request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional') or
|
2025-07-11 21:47:50 -04:00
|
|
|
request.env.user.has_group('base.group_system')):
|
|
|
|
|
return request.redirect('/my')
|
|
|
|
|
|
|
|
|
|
# Verify the injury
|
2025-07-22 21:07:51 -04:00
|
|
|
injury.action_verify_injury()
|
2025-07-11 21:47:50 -04:00
|
|
|
|
|
|
|
|
# Redirect back to the player page
|
|
|
|
|
return request.redirect(f'/my/player?player_id={injury.patient_id.id}')
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
_logger.error(f"Error verifying injury: {e}")
|
|
|
|
|
return request.redirect('/my')
|