""" Appointments forms for the Tenhal Multidisciplinary Healthcare Platform. This module contains forms for appointment booking, confirmation, rescheduling, etc. """ from django import forms from django.utils.translation import gettext_lazy as _ from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Fieldset, Row, Column, Submit, HTML from .models import Appointment, Provider, Room, Schedule class AppointmentBookingForm(forms.ModelForm): """ Form for booking new appointments. """ # Define common service types SERVICE_TYPE_CHOICES = [ ('', _('Select a service type')), ('consultation', _('Consultation')), ('follow_up', _('Follow-up Visit')), ('assessment', _('Assessment')), ('therapy_session', _('Therapy Session')), ('evaluation', _('Evaluation')), ('intervention', _('Intervention')), ('screening', _('Screening')), ('parent_training', _('Parent Training')), ('group_session', _('Group Session')), ('other', _('Other')), ] service_type = forms.ChoiceField( choices=SERVICE_TYPE_CHOICES, widget=forms.Select(attrs={'class': 'form-control'}), label=_('Service Type') ) class Meta: model = Appointment fields = [ 'patient', 'clinic', 'provider', 'room', 'service_type', 'scheduled_date', 'scheduled_time', 'duration', 'finance_cleared', 'consent_verified', 'notes', ] widgets = { 'patient': forms.Select(attrs={'class': 'form-select select2', 'data-placeholder': 'Select patient'}), 'clinic': forms.Select(attrs={'class': 'form-select select2', 'data-placeholder': 'Select clinic'}), 'provider': forms.Select(attrs={'class': 'form-select select2', 'data-placeholder': 'Select provider'}), 'room': forms.Select(attrs={'class': 'form-select select2', 'data-placeholder': 'Select room'}), 'scheduled_date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}), 'scheduled_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), 'duration': forms.NumberInput(attrs={'class': 'form-control', 'min': '15', 'step': '15'}), 'finance_cleared': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'consent_verified': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'notes': forms.Textarea(attrs={'rows': 3, 'class': 'form-control'}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_method = 'post' self.helper.layout = Layout( Fieldset( _('Appointment Details'), 'patient', Row( Column('clinic', css_class='form-group col-md-6 mb-0'), Column('provider', css_class='form-group col-md-6 mb-0'), css_class='form-row' ), Row( Column('service_type', css_class='form-group col-md-6 mb-0'), Column('room', css_class='form-group col-md-6 mb-0'), css_class='form-row' ), Row( Column('scheduled_date', css_class='form-group col-md-4 mb-0'), Column('scheduled_time', css_class='form-group col-md-4 mb-0'), Column('duration', css_class='form-group col-md-4 mb-0'), css_class='form-row' ), 'notes', ), Submit('submit', _('Book Appointment'), css_class='btn btn-primary') ) def clean(self): cleaned_data = super().clean() provider = cleaned_data.get('provider') clinic = cleaned_data.get('clinic') # Validate provider belongs to clinic if provider and clinic: if not provider.specialties.filter(id=clinic.id).exists(): raise forms.ValidationError( _('Selected provider does not work in this clinic.') ) return cleaned_data class AppointmentConfirmForm(forms.Form): """ Form for confirming appointments. """ confirmation_method = forms.ChoiceField( label=_('Confirmation Method'), choices=Appointment.ConfirmationMethod.choices, widget=forms.RadioSelect(attrs={'class': 'form-check-input'}) ) notes = forms.CharField( required=False, label=_('Notes'), widget=forms.Textarea(attrs={'rows': 2, 'class': 'form-control'}) ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_method = 'post' self.helper.layout = Layout( 'confirmation_method', 'notes', Submit('submit', _('Confirm Appointment'), css_class='btn btn-success') ) class AppointmentRescheduleForm(forms.ModelForm): """ Form for rescheduling appointments. """ reschedule_reason = forms.CharField( label=_('Reason for Rescheduling'), widget=forms.Textarea(attrs={'rows': 3, 'class': 'form-control'}) ) class Meta: model = Appointment fields = ['scheduled_date', 'scheduled_time', 'reschedule_reason'] widgets = { 'scheduled_date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}), 'scheduled_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), } labels = { 'scheduled_date': _('New Date'), 'scheduled_time': _('New Time'), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_method = 'post' self.helper.layout = Layout( Row( Column('scheduled_date', css_class='form-group col-md-6 mb-0'), Column('scheduled_time', css_class='form-group col-md-6 mb-0'), css_class='form-row' ), 'reschedule_reason', Submit('submit', _('Reschedule'), css_class='btn btn-warning') ) class AppointmentCancelForm(forms.Form): """ Form for cancelling appointments. """ cancel_reason = forms.CharField( label=_('Reason for Cancellation'), widget=forms.Textarea(attrs={'rows': 3, 'class': 'form-control'}) ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_method = 'post' self.helper.layout = Layout( 'cancel_reason', Submit('submit', _('Cancel Appointment'), css_class='btn btn-danger') ) class AppointmentArrivalForm(forms.Form): """ Form for marking patient arrival. """ notes = forms.CharField( required=False, label=_('Notes'), widget=forms.Textarea(attrs={'rows': 2, 'class': 'form-control'}) ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_method = 'post' self.helper.layout = Layout( 'notes', Submit('submit', _('Mark as Arrived'), css_class='btn btn-info') ) class AppointmentSearchForm(forms.Form): """ Form for searching appointments. """ search_query = forms.CharField( required=False, label=_('Search'), widget=forms.TextInput(attrs={ 'placeholder': _('Patient name, MRN, Appointment #...'), 'class': 'form-control' }) ) clinic = forms.ModelChoiceField( required=False, label=_('Clinic'), queryset=None, # Will be set in __init__ widget=forms.Select(attrs={'class': 'form-select select2', 'data-placeholder': 'Select clinic'}) ) provider = forms.ModelChoiceField( required=False, label=_('Provider'), queryset=None, # Will be set in __init__ widget=forms.Select(attrs={'class': 'form-select select2', 'data-placeholder': 'Select provider'}) ) status = forms.ChoiceField( required=False, label=_('Status'), choices=[('', _('All'))] + list(Appointment.Status.choices), widget=forms.Select(attrs={'class': 'form-control'}) ) date_from = forms.DateField( required=False, label=_('From Date'), widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}) ) date_to = forms.DateField( required=False, label=_('To Date'), widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}) ) def __init__(self, *args, **kwargs): tenant = kwargs.pop('tenant', None) super().__init__(*args, **kwargs) if tenant: from core.models import Clinic self.fields['clinic'].queryset = Clinic.objects.filter(tenant=tenant, is_active=True) self.fields['provider'].queryset = Provider.objects.filter(tenant=tenant, is_available=True) self.helper = FormHelper() self.helper.form_method = 'get' self.helper.layout = Layout( Row( Column('search_query', css_class='form-group col-md-3 mb-0'), Column('clinic', css_class='form-group col-md-2 mb-0'), Column('provider', css_class='form-group col-md-2 mb-0'), Column('status', css_class='form-group col-md-2 mb-0'), Column('date_from', css_class='form-group col-md-1.5 mb-0'), Column('date_to', css_class='form-group col-md-1.5 mb-0'), css_class='form-row' ), Submit('search', _('Search'), css_class='btn btn-primary') ) class ProviderScheduleForm(forms.ModelForm): """ Form for managing provider schedules. """ class Meta: model = Schedule fields = ['provider', 'day_of_week', 'start_time', 'end_time', 'slot_duration', 'is_active'] widgets = { 'provider': forms.Select(attrs={'class': 'form-select select2', 'data-placeholder': 'Select provider'}), 'day_of_week': forms.Select(attrs={'class': 'form-control'}), 'start_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), 'end_time': forms.TimeInput(attrs={'type': 'time', 'class': 'form-control'}), 'slot_duration': forms.NumberInput(attrs={'class': 'form-control', 'min': '15', 'step': '15'}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_method = 'post' self.helper.layout = Layout( 'provider', Row( Column('day_of_week', css_class='form-group col-md-4 mb-0'), Column('start_time', css_class='form-group col-md-3 mb-0'), Column('end_time', css_class='form-group col-md-3 mb-0'), Column('slot_duration', css_class='form-group col-md-2 mb-0'), css_class='form-row' ), 'is_active', Submit('submit', _('Save Schedule'), css_class='btn btn-primary') ) def clean(self): cleaned_data = super().clean() start_time = cleaned_data.get('start_time') end_time = cleaned_data.get('end_time') if start_time and end_time and start_time >= end_time: raise forms.ValidationError(_('End time must be after start time.')) return cleaned_data # Alias for backward compatibility with views AppointmentForm = AppointmentBookingForm RescheduleForm = AppointmentRescheduleForm