HH/apps/rca/forms.py
ismail da0ca4ee19 Fix hospital fields in forms - Add HospitalFieldMixin and update templates
PHASE 1: Add HospitalFieldMixin to forms without it
- apps/complaints/forms.py: SLAConfigForm, EscalationRuleForm
- apps/feedback/forms.py: FeedbackForm
- apps/rca/forms.py: RootCauseAnalysisForm

PHASE 2: Update templates to conditionally hide hospital field labels
- templates/complaints/complaint_form.html
- templates/complaints/inquiry_form.html
- templates/complaints/complaint_threshold_form.html
- templates/complaints/escalation_rule_form.html
- templates/feedback/feedback_form.html

PHASE 3: Remove redundant manual hospital filtering code
- Removed manual __init__ hospital logic from forms now using mixin

Behavior:
- PX Admin: Sees hospital dropdown (can select any hospital)
- Hospital Admin/Staff: Hospital field hidden, auto-set to their hospital
- Cleaner code: Mixin handles all role-based filtering automatically
2026-03-11 01:19:41 +03:00

352 lines
11 KiB
Python

"""
RCA (Root Cause Analysis) forms
"""
RCA (Root Cause Analysis) forms
"""
from django import forms
from django.utils import timezone
from apps.core.models import PriorityChoices
from apps.core.form_mixins import HospitalFieldMixin
from .models import (
RCAActionStatus,
RCAActionType,
RCACorrectiveAction,
RCAStatus,
RCASeverity,
RCAAttachment,
RCANote,
RCARootCause,
RootCauseAnalysis,
)
class RootCauseAnalysisForm(HospitalFieldMixin, forms.ModelForm):
"""Form for creating and editing RootCauseAnalysis"""
class Meta:
model = RootCauseAnalysis
fields = [
'title',
'description',
'background',
'hospital',
'department',
'status',
'severity',
'priority',
'assigned_to',
'target_completion_date',
'root_cause_summary',
]
widgets = {
'title': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter RCA title'
}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Describe the incident or issue'
}),
'background': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Provide background information and context'
}),
'hospital': forms.Select(attrs={'class': 'form-select'}),
'department': forms.Select(attrs={'class': 'form-select'}),
'status': forms.Select(attrs={'class': 'form-select'}),
'severity': forms.Select(attrs={'class': 'form-select'}),
'priority': forms.Select(attrs={'class': 'form-select'}),
'assigned_to': forms.Select(attrs={'class': 'form-select'}),
'target_completion_date': forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
}),
'root_cause_summary': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Summary of root cause analysis findings'
}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Filter assigned_to to show only active users
if 'assigned_to' in self.fields:
from apps.accounts.models import User
self.fields['assigned_to'].queryset = User.objects.filter(
is_active=True
).order_by('email')
class RCARootCauseForm(forms.ModelForm):
"""Form for creating and editing RCARootCause"""
class Meta:
model = RCARootCause
fields = [
'description',
'category',
'contributing_factors',
'likelihood',
'impact',
'evidence',
]
widgets = {
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Describe the root cause'
}),
'category': forms.Select(attrs={'class': 'form-select'}),
'contributing_factors': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Factors that contributed to this root cause'
}),
'likelihood': forms.NumberInput(attrs={
'class': 'form-control',
'min': 1,
'max': 5,
'placeholder': '1-5'
}),
'impact': forms.NumberInput(attrs={
'class': 'form-control',
'min': 1,
'max': 5,
'placeholder': '1-5'
}),
'evidence': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Evidence supporting this root cause'
}),
}
def clean_likelihood(self):
likelihood = self.cleaned_data.get('likelihood')
if likelihood and (likelihood < 1 or likelihood > 5):
raise forms.ValidationError('Likelihood must be between 1 and 5')
return likelihood
def clean_impact(self):
impact = self.cleaned_data.get('impact')
if impact and (impact < 1 or impact > 5):
raise forms.ValidationError('Impact must be between 1 and 5')
return impact
class RCACorrectiveActionForm(forms.ModelForm):
"""Form for creating and editing RCACorrectiveAction"""
class Meta:
model = RCACorrectiveAction
fields = [
'description',
'action_type',
'root_cause',
'responsible_person',
'target_date',
'completion_date',
'status',
'effectiveness_measure',
'effectiveness_assessment',
'effectiveness_score',
'obstacles',
]
widgets = {
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Describe the corrective action'
}),
'action_type': forms.Select(attrs={'class': 'form-select'}),
'root_cause': forms.Select(attrs={'class': 'form-select'}),
'responsible_person': forms.Select(attrs={'class': 'form-select'}),
'target_date': forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
}),
'completion_date': forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
}),
'status': forms.Select(attrs={'class': 'form-select'}),
'effectiveness_measure': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'How will effectiveness be measured?'
}),
'effectiveness_assessment': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Assessment of action effectiveness'
}),
'effectiveness_score': forms.NumberInput(attrs={
'class': 'form-control',
'min': 1,
'max': 5,
'placeholder': '1-5'
}),
'obstacles': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Obstacles encountered during implementation'
}),
}
def __init__(self, *args, **kwargs):
rca = kwargs.pop('rca', None)
super().__init__(*args, **kwargs)
# Filter root_cause to show only for this RCA
if 'root_cause' in self.fields and rca:
self.fields['root_cause'].queryset = rca.root_causes.all()
def clean_effectiveness_score(self):
score = self.cleaned_data.get('effectiveness_score')
if score and (score < 1 or score > 5):
raise forms.ValidationError('Effectiveness score must be between 1 and 5')
return score
class RCAFilterForm(forms.Form):
"""Form for filtering RCA list"""
status = forms.ChoiceField(
required=False,
choices=[('', 'All Statuses')] + list(RCAStatus.choices),
widget=forms.Select(attrs={'class': 'form-select'})
)
severity = forms.ChoiceField(
required=False,
choices=[('', 'All Severities')] + list(RCASeverity.choices),
widget=forms.Select(attrs={'class': 'form-select'})
)
priority = forms.ChoiceField(
required=False,
choices=[('', 'All Priorities')] + list(PriorityChoices.choices),
widget=forms.Select(attrs={'class': 'form-select'})
)
hospital = forms.ChoiceField(
required=False,
choices=[('', 'All Hospitals')],
widget=forms.Select(attrs={'class': 'form-select'})
)
department = forms.ChoiceField(
required=False,
choices=[('', 'All Departments')],
widget=forms.Select(attrs={'class': 'form-select'})
)
search = forms.CharField(
required=False,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Search RCAs...'
})
)
date_from = forms.DateField(
required=False,
widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
)
date_to = forms.DateField(
required=False,
widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Populate hospital choices
from apps.organizations.models import Hospital
hospital_choices = [('', 'All Hospitals')]
hospital_choices.extend([
(h.id, h.name)
for h in Hospital.objects.all()
])
self.fields['hospital'].choices = hospital_choices
class RCAStatusChangeForm(forms.Form):
"""Form for changing RCA status"""
new_status = forms.ChoiceField(
choices=RCAStatus.choices,
widget=forms.Select(attrs={'class': 'form-select'})
)
notes = forms.CharField(
required=False,
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Add notes about this status change'
})
)
class RCAApprovalForm(forms.Form):
"""Form for approving RCA"""
approval_notes = forms.CharField(
required=False,
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Add approval notes'
})
)
class RCAClosureForm(forms.Form):
"""Form for closing RCA"""
closure_notes = forms.CharField(
required=True,
widget=forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Provide closure notes and summary'
})
)
actual_completion_date = forms.DateField(
required=True,
widget=forms.DateInput(attrs={
'class': 'form-control',
'type': 'date'
})
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Set default to today
self.fields['actual_completion_date'].initial = timezone.now().date()
class RCAAttachmentForm(forms.ModelForm):
"""Form for uploading RCA attachments"""
class Meta:
model = RCAAttachment
fields = ['file', 'description']
widgets = {
'file': forms.FileInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 2,
'placeholder': 'Describe this attachment'
}),
}
class RCANoteForm(forms.ModelForm):
"""Form for adding RCA notes"""
class Meta:
model = RCANote
fields = ['note', 'is_internal']
widgets = {
'note': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Add a note'
}),
'is_internal': forms.CheckboxInput(attrs={
'class': 'form-check-input'
}),
}