From da0ca4ee191ebdba8e67f635c9612cc782acf8f3 Mon Sep 17 00:00:00 2001 From: ismail Date: Wed, 11 Mar 2026 01:19:41 +0300 Subject: [PATCH] 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 --- apps/complaints/forms.py | 31 +- apps/feedback/forms.py | 319 +++++++----------- apps/rca/forms.py | 21 +- templates/complaints/complaint_form.html | 4 + .../complaints/complaint_threshold_form.html | 2 +- .../complaints/escalation_rule_form.html | 2 +- templates/complaints/inquiry_form.html | 4 + templates/feedback/feedback_form.html | 4 + 8 files changed, 142 insertions(+), 245 deletions(-) diff --git a/apps/complaints/forms.py b/apps/complaints/forms.py index 2a7af4b..fd01fa0 100644 --- a/apps/complaints/forms.py +++ b/apps/complaints/forms.py @@ -765,7 +765,7 @@ class InquiryForm(HospitalFieldMixin, forms.ModelForm): ).order_by('name') -class SLAConfigForm(forms.ModelForm): +class SLAConfigForm(HospitalFieldMixin, forms.ModelForm): """Form for creating and editing SLA configurations""" class Meta: @@ -779,14 +779,6 @@ class SLAConfigForm(forms.ModelForm): 'reminder_hours_before': forms.NumberInput(attrs={'class': 'form-control', 'min': '0'}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } - - def __init__(self, *args, **kwargs): - user = kwargs.pop('user', None) - super().__init__(*args, **kwargs) - - # Filter hospitals based on user role - if user and not user.is_px_admin() and user.hospital: - self.fields['hospital'].queryset = Hospital.objects.filter(id=user.hospital.id) self.fields['hospital'].initial = user.hospital self.fields['hospital'].widget.attrs['readonly'] = True @@ -823,7 +815,7 @@ class SLAConfigForm(forms.ModelForm): return cleaned_data -class EscalationRuleForm(forms.ModelForm): +class EscalationRuleForm(HospitalFieldMixin, forms.ModelForm): """Form for creating and editing escalation rules""" class Meta: @@ -840,11 +832,11 @@ class EscalationRuleForm(forms.ModelForm): 'name': forms.TextInput(attrs={'class': 'form-control'}), 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'escalation_level': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}), - 'max_escalation_level': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}), + 'max_escalation_level': forms.NumberInput(attrs={'class': 'form-control', 'min': 1}), 'trigger_on_overdue': forms.CheckboxInput(attrs={'class': 'form-check-input'}), - 'trigger_hours_overdue': forms.NumberInput(attrs={'class': 'form-control', 'min': '0'}), + 'trigger_hours_overdue': forms.NumberInput(attrs={'class': 'form-control', 'min': 0}), 'reminder_escalation_enabled': forms.CheckboxInput(attrs={'class': 'form-check-input'}), - 'reminder_escalation_hours': forms.NumberInput(attrs={'class': 'form-control', 'min': '0'}), + 'reminder_escalation_hours': forms.NumberInput(attrs={'class': 'form-control', 'min': 0}), 'escalate_to_role': forms.Select(attrs={'class': 'form-select', 'id': 'escalate_to_role'}), 'escalate_to_user': forms.Select(attrs={'class': 'form-select'}), 'severity_filter': forms.Select(attrs={'class': 'form-select'}), @@ -853,23 +845,16 @@ class EscalationRuleForm(forms.ModelForm): } def __init__(self, *args, **kwargs): - user = kwargs.pop('user', None) super().__init__(*args, **kwargs) - # Filter hospitals based on user role - if user and not user.is_px_admin() and user.hospital: - self.fields['hospital'].queryset = Hospital.objects.filter(id=user.hospital.id) - self.fields['hospital'].initial = user.hospital - self.fields['hospital'].widget.attrs['readonly'] = True - # Filter users for escalate_to_user field from apps.accounts.models import User - if user and user.is_px_admin(): + if self.user and self.user.is_px_admin(): self.fields['escalate_to_user'].queryset = User.objects.filter(is_active=True) - elif user and user.hospital: + elif self.user and self.user.hospital: self.fields['escalate_to_user'].queryset = User.objects.filter( is_active=True, - hospital=user.hospital + hospital=self.user.hospital ) else: self.fields['escalate_to_user'].queryset = User.objects.none() diff --git a/apps/feedback/forms.py b/apps/feedback/forms.py index c05c0cc..40acf02 100644 --- a/apps/feedback/forms.py +++ b/apps/feedback/forms.py @@ -1,161 +1,116 @@ """ Feedback forms - Forms for feedback management """ + from django import forms from apps.organizations.models import Department, Hospital, Patient, Staff - +from apps.core.form_mixins import HospitalFieldMixin from .models import Feedback, FeedbackResponse, FeedbackStatus, FeedbackType, FeedbackCategory -class FeedbackForm(forms.ModelForm): +class FeedbackForm(HospitalFieldMixin, forms.ModelForm): """Form for creating and editing feedback""" class Meta: model = Feedback fields = [ - 'patient', - 'is_anonymous', - 'contact_name', - 'contact_email', - 'contact_phone', - 'hospital', - 'department', - 'staff', - 'feedback_type', - 'title', - 'message', - 'category', - 'subcategory', - 'rating', - 'priority', - 'encounter_id', + "patient", + "is_anonymous", + "contact_name", + "contact_email", + "contact_phone", + "hospital", + "department", + "staff", + "feedback_type", + "title", + "message", + "category", + "subcategory", + "rating", + "priority", + "encounter_id", ] widgets = { - 'patient': forms.Select(attrs={ - 'class': 'form-select', - 'id': 'id_patient' - }), - 'is_anonymous': forms.CheckboxInput(attrs={ - 'class': 'form-check-input', - 'id': 'id_is_anonymous' - }), - 'contact_name': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter contact name' - }), - 'contact_email': forms.EmailInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter email address' - }), - 'contact_phone': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter phone number' - }), - 'hospital': forms.Select(attrs={ - 'class': 'form-select', - 'required': True - }), - 'department': forms.Select(attrs={ - 'class': 'form-select' - }), - 'staff': forms.Select(attrs={ - 'class': 'form-select' - }), - 'feedback_type': forms.Select(attrs={ - 'class': 'form-select', - 'required': True - }), - 'title': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter feedback title', - 'required': True - }), - 'message': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 5, - 'placeholder': 'Enter your feedback message...', - 'required': True - }), - 'category': forms.Select(attrs={ - 'class': 'form-select', - 'required': True - }), - 'subcategory': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter subcategory (optional)' - }), - 'rating': forms.NumberInput(attrs={ - 'class': 'form-control', - 'min': 1, - 'max': 5, - 'placeholder': 'Rate from 1 to 5' - }), - 'priority': forms.Select(attrs={ - 'class': 'form-select' - }), - 'encounter_id': forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Enter encounter ID (optional)' - }), + "patient": forms.Select(attrs={"class": "form-select", "id": "id_patient"}), + "is_anonymous": forms.CheckboxInput(attrs={"class": "form-check-input", "id": "id_is_anonymous"}), + "contact_name": forms.TextInput(attrs={"class": "form-control", "placeholder": "Enter contact name"}), + "contact_email": forms.EmailInput(attrs={"class": "form-control", "placeholder": "Enter email address"}), + "contact_phone": forms.TextInput(attrs={"class": "form-control", "placeholder": "Enter phone number"}), + "hospital": forms.Select(attrs={"class": "form-select", "required": True}), + "department": forms.Select(attrs={"class": "form-select"}), + "staff": forms.Select(attrs={"class": "form-select"}), + "feedback_type": forms.Select(attrs={"class": "form-select", "required": True}), + "title": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Enter feedback title", "required": True} + ), + "message": forms.Textarea( + attrs={ + "class": "form-control", + "rows": 5, + "placeholder": "Enter your feedback message...", + "required": True, + } + ), + "category": forms.Select(attrs={"class": "form-select", "required": True}), + "subcategory": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Enter subcategory (optional)"} + ), + "rating": forms.NumberInput( + attrs={"class": "form-control", "min": 1, "max": 5, "placeholder": "Rate from 1 to 5"} + ), + "priority": forms.Select(attrs={"class": "form-select"}), + "encounter_id": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Enter encounter ID (optional)"} + ), } def __init__(self, *args, **kwargs): - user = kwargs.pop('user', None) + user = kwargs.pop("user", None) super().__init__(*args, **kwargs) # Filter hospitals based on user permissions if user: if not user.is_px_admin() and user.hospital: - self.fields['hospital'].queryset = Hospital.objects.filter( - id=user.hospital.id, - status='active' - ) + self.fields["hospital"].queryset = Hospital.objects.filter(id=user.hospital.id, status="active") else: - self.fields['hospital'].queryset = Hospital.objects.filter(status='active') + self.fields["hospital"].queryset = Hospital.objects.filter(status="active") # Set initial hospital if user has one if user and user.hospital and not self.instance.pk: - self.fields['hospital'].initial = user.hospital + self.fields["hospital"].initial = user.hospital # Filter departments and physicians based on selected hospital - if self.instance.pk and hasattr(self.instance, 'hospital') and self.instance.hospital_id: - self.fields['department'].queryset = Department.objects.filter( - hospital=self.instance.hospital, - status='active' - ) - self.fields['staff'].queryset = Staff.objects.filter( - hospital=self.instance.hospital, - status='active' + if self.instance.pk and hasattr(self.instance, "hospital") and self.instance.hospital_id: + self.fields["department"].queryset = Department.objects.filter( + hospital=self.instance.hospital, status="active" ) + self.fields["staff"].queryset = Staff.objects.filter(hospital=self.instance.hospital, status="active") else: - self.fields['department'].queryset = Department.objects.none() - self.fields['staff'].queryset = Staff.objects.none() + self.fields["department"].queryset = Department.objects.none() + self.fields["staff"].queryset = Staff.objects.none() # Make patient optional if anonymous - if self.data.get('is_anonymous'): - self.fields['patient'].required = False + if self.data.get("is_anonymous"): + self.fields["patient"].required = False def clean(self): cleaned_data = super().clean() - is_anonymous = cleaned_data.get('is_anonymous') - patient = cleaned_data.get('patient') - contact_name = cleaned_data.get('contact_name') + is_anonymous = cleaned_data.get("is_anonymous") + patient = cleaned_data.get("patient") + contact_name = cleaned_data.get("contact_name") # Validate anonymous feedback if is_anonymous: if not contact_name: - raise forms.ValidationError( - "Contact name is required for anonymous feedback." - ) + raise forms.ValidationError("Contact name is required for anonymous feedback.") else: if not patient: - raise forms.ValidationError( - "Please select a patient or mark as anonymous." - ) + raise forms.ValidationError("Please select a patient or mark as anonymous.") # Validate rating - rating = cleaned_data.get('rating') + rating = cleaned_data.get("rating") if rating is not None and (rating < 1 or rating > 5): raise forms.ValidationError("Rating must be between 1 and 5.") @@ -167,21 +122,13 @@ class FeedbackResponseForm(forms.ModelForm): class Meta: model = FeedbackResponse - fields = ['response_type', 'message', 'is_internal'] + fields = ["response_type", "message", "is_internal"] widgets = { - 'response_type': forms.Select(attrs={ - 'class': 'form-select', - 'required': True - }), - 'message': forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 4, - 'placeholder': 'Enter your response...', - 'required': True - }), - 'is_internal': forms.CheckboxInput(attrs={ - 'class': 'form-check-input' - }), + "response_type": forms.Select(attrs={"class": "form-select", "required": True}), + "message": forms.Textarea( + attrs={"class": "form-control", "rows": 4, "placeholder": "Enter your response...", "required": True} + ), + "is_internal": forms.CheckboxInput(attrs={"class": "form-check-input"}), } @@ -190,111 +137,86 @@ class FeedbackFilterForm(forms.Form): search = forms.CharField( required=False, - widget=forms.TextInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Search by title, message, patient...' - }) + widget=forms.TextInput(attrs={"class": "form-control", "placeholder": "Search by title, message, patient..."}), ) feedback_type = forms.ChoiceField( required=False, - choices=[('', 'All Types')] + list(FeedbackType.choices), - widget=forms.Select(attrs={'class': 'form-select'}) + choices=[("", "All Types")] + list(FeedbackType.choices), + widget=forms.Select(attrs={"class": "form-select"}), ) status = forms.ChoiceField( required=False, - choices=[('', 'All Statuses')] + list(FeedbackStatus.choices), - widget=forms.Select(attrs={'class': 'form-select'}) + choices=[("", "All Statuses")] + list(FeedbackStatus.choices), + widget=forms.Select(attrs={"class": "form-select"}), ) category = forms.ChoiceField( required=False, - choices=[('', 'All Categories')] + list(FeedbackCategory.choices), - widget=forms.Select(attrs={'class': 'form-select'}) + choices=[("", "All Categories")] + list(FeedbackCategory.choices), + widget=forms.Select(attrs={"class": "form-select"}), ) sentiment = forms.ChoiceField( required=False, choices=[ - ('', 'All Sentiments'), - ('positive', 'Positive'), - ('neutral', 'Neutral'), - ('negative', 'Negative'), + ("", "All Sentiments"), + ("positive", "Positive"), + ("neutral", "Neutral"), + ("negative", "Negative"), ], - widget=forms.Select(attrs={'class': 'form-select'}) + widget=forms.Select(attrs={"class": "form-select"}), ) priority = forms.ChoiceField( required=False, choices=[ - ('', 'All Priorities'), - ('low', 'Low'), - ('medium', 'Medium'), - ('high', 'High'), - ('urgent', 'Urgent'), + ("", "All Priorities"), + ("low", "Low"), + ("medium", "Medium"), + ("high", "High"), + ("urgent", "Urgent"), ], - widget=forms.Select(attrs={'class': 'form-select'}) + widget=forms.Select(attrs={"class": "form-select"}), ) hospital = forms.ModelChoiceField( required=False, - queryset=Hospital.objects.filter(status='active'), - widget=forms.Select(attrs={'class': 'form-select'}), - empty_label='All Hospitals' + queryset=Hospital.objects.filter(status="active"), + widget=forms.Select(attrs={"class": "form-select"}), + empty_label="All Hospitals", ) department = forms.ModelChoiceField( required=False, - queryset=Department.objects.filter(status='active'), - widget=forms.Select(attrs={'class': 'form-select'}), - empty_label='All Departments' + queryset=Department.objects.filter(status="active"), + widget=forms.Select(attrs={"class": "form-select"}), + empty_label="All Departments", ) rating_min = forms.IntegerField( required=False, min_value=1, max_value=5, - widget=forms.NumberInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Min rating' - }) + widget=forms.NumberInput(attrs={"class": "form-control", "placeholder": "Min rating"}), ) rating_max = forms.IntegerField( required=False, min_value=1, max_value=5, - widget=forms.NumberInput(attrs={ - 'class': 'form-control', - 'placeholder': 'Max rating' - }) + widget=forms.NumberInput(attrs={"class": "form-control", "placeholder": "Max rating"}), ) - date_from = forms.DateField( - required=False, - widget=forms.DateInput(attrs={ - 'class': 'form-control', - 'type': 'date' - }) - ) + 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' - }) - ) + date_to = forms.DateField(required=False, widget=forms.DateInput(attrs={"class": "form-control", "type": "date"})) - is_featured = forms.BooleanField( - required=False, - widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}) - ) + is_featured = forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={"class": "form-check-input"})) requires_follow_up = forms.BooleanField( - required=False, - widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}) + required=False, widget=forms.CheckboxInput(attrs={"class": "form-check-input"}) ) @@ -302,38 +224,29 @@ class FeedbackStatusChangeForm(forms.Form): """Form for changing feedback status""" status = forms.ChoiceField( - choices=FeedbackStatus.choices, - widget=forms.Select(attrs={ - 'class': 'form-select', - 'required': True - }) + choices=FeedbackStatus.choices, widget=forms.Select(attrs={"class": "form-select", "required": True}) ) note = forms.CharField( required=False, - widget=forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 3, - 'placeholder': 'Add a note about this status change (optional)...' - }) + widget=forms.Textarea( + attrs={ + "class": "form-control", + "rows": 3, + "placeholder": "Add a note about this status change (optional)...", + } + ), ) class FeedbackAssignForm(forms.Form): """Form for assigning feedback to a user""" - user_id = forms.UUIDField( - widget=forms.Select(attrs={ - 'class': 'form-select', - 'required': True - }) - ) + user_id = forms.UUIDField(widget=forms.Select(attrs={"class": "form-select", "required": True})) note = forms.CharField( required=False, - widget=forms.Textarea(attrs={ - 'class': 'form-control', - 'rows': 2, - 'placeholder': 'Add a note about this assignment (optional)...' - }) + widget=forms.Textarea( + attrs={"class": "form-control", "rows": 2, "placeholder": "Add a note about this assignment (optional)..."} + ), ) diff --git a/apps/rca/forms.py b/apps/rca/forms.py index 9e7a615..5c4c0fe 100644 --- a/apps/rca/forms.py +++ b/apps/rca/forms.py @@ -1,11 +1,12 @@ """ 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, @@ -19,7 +20,7 @@ from .models import ( ) -class RootCauseAnalysisForm(forms.ModelForm): +class RootCauseAnalysisForm(HospitalFieldMixin, forms.ModelForm): """Form for creating and editing RootCauseAnalysis""" class Meta: @@ -70,7 +71,6 @@ class RootCauseAnalysisForm(forms.ModelForm): } def __init__(self, *args, **kwargs): - user = kwargs.pop('user', None) super().__init__(*args, **kwargs) # Filter assigned_to to show only active users @@ -80,19 +80,6 @@ class RootCauseAnalysisForm(forms.ModelForm): is_active=True ).order_by('email') - # Set initial hospital if user has one - if user and not self.instance.pk: - from apps.organizations.models import Hospital - try: - user_hospital = Hospital.objects.filter( - staff__user=user - ).first() - if user_hospital: - self.fields['hospital'].initial = user_hospital - self.fields['hospital'].widget.attrs['readonly'] = True - except: - pass - class RCARootCauseForm(forms.ModelForm): """Form for creating and editing RCARootCause""" diff --git a/templates/complaints/complaint_form.html b/templates/complaints/complaint_form.html index e219c31..d3771d9 100644 --- a/templates/complaints/complaint_form.html +++ b/templates/complaints/complaint_form.html @@ -506,6 +506,7 @@
+ {% if not form.hospital.is_hidden %}
+ {% else %} + {{ form.hospital }} + {% endif %}