""" Forms for Pharmacy app CRUD operations (corrected to match models). """ from django import forms from django.core.exceptions import ValidationError from django.utils import timezone from datetime import date from .models import ( Medication, Prescription, InventoryItem as PharmacyInventoryItem, DispenseRecord, MedicationAdministration, DrugInteraction ) from patients.models import PatientProfile from accounts.models import User class MedicationForm(forms.ModelForm): """ Form for medication management. """ class Meta: model = Medication fields = [ 'generic_name', 'brand_name', 'dosage_form', 'strength', 'unit_of_measure', 'ndc_number', 'manufacturer', 'drug_class', 'controlled_substance_schedule', 'indications', 'contraindications', 'side_effects', 'warnings', 'is_active', 'is_available' ] widgets = { 'generic_name': forms.TextInput(attrs={'class': 'form-control'}), 'brand_name': forms.TextInput(attrs={'class': 'form-control'}), 'dosage_form': forms.Select(attrs={'class': 'form-select'}), 'strength': forms.TextInput(attrs={'class': 'form-control'}), 'unit_of_measure': forms.Select(attrs={'class': 'form-select'}), 'ndc_number': forms.TextInput(attrs={'class': 'form-control'}), 'manufacturer': forms.TextInput(attrs={'class': 'form-control'}), 'drug_class': forms.TextInput(attrs={'class': 'form-control'}), 'controlled_substance_schedule': forms.Select(attrs={'class': 'form-select'}), 'indications': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'contraindications': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'side_effects': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'warnings': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'is_available': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def clean_ndc_number(self): ndc_number = self.cleaned_data.get('ndc_number') if ndc_number: digits = ''.join(filter(str.isdigit, ndc_number)) if len(digits) != 11: raise ValidationError('NDC number must be 11 digits.') return ndc_number return ndc_number class PrescriptionForm(forms.ModelForm): """ Form for prescription management. """ class Meta: model = Prescription fields = [ 'patient', 'prescriber', 'medication', 'quantity_prescribed', 'quantity_unit', 'dosage_instructions', 'frequency', 'duration', 'refills_authorized', 'refills_remaining', 'date_prescribed', 'date_written', 'expiration_date', 'status', 'indication', 'diagnosis_code', 'pharmacy_notes', 'patient_instructions', 'prior_authorization_required', 'prior_authorization_number', ] widgets = { 'patient': forms.Select(attrs={'class': 'form-select'}), 'prescriber': forms.Select(attrs={'class': 'form-select'}), 'medication': forms.Select(attrs={'class': 'form-select'}), 'quantity_prescribed': forms.NumberInput(attrs={'class': 'form-control', 'min': 1}), 'quantity_unit': forms.Select(attrs={'class': 'form-select'}), 'dosage_instructions': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'frequency': forms.TextInput(attrs={'class': 'form-control'}), 'duration': forms.TextInput(attrs={'class': 'form-control'}), 'refills_authorized': forms.NumberInput(attrs={'class': 'form-control', 'min': 0}), 'refills_remaining': forms.NumberInput(attrs={'class': 'form-control', 'min': 0}), 'date_prescribed': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}), 'date_written': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'expiration_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'status': forms.Select(attrs={'class': 'form-select'}), 'indication': forms.TextInput(attrs={'class': 'form-control'}), 'diagnosis_code': forms.TextInput(attrs={'class': 'form-control'}), 'pharmacy_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), 'patient_instructions': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), 'prior_authorization_required': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'prior_authorization_number': forms.TextInput(attrs={'class': 'form-control'}), # 'priority': forms.Select(attrs={'class': 'form-select'}), } def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) if user and hasattr(user, 'tenant'): self.fields['patient'].queryset = PatientProfile.objects.filter( tenant=user.tenant, is_active=True ).order_by('last_name') self.fields['medication'].queryset = Medication.objects.filter( tenant=user.tenant, is_active=True ).order_by('generic_name') self.fields['prescriber'].queryset = User.objects.filter( tenant=user.tenant, is_active=True, role__in=['DOCTOR', 'SPECIALIST'] ).order_by('last_name') def clean(self): data = super().clean() dpres = data.get('date_prescribed') dwritten = data.get('date_written') dexp = data.get('expiration_date') if dpres and dpres > timezone.now(): self.add_error('date_prescribed', 'Cannot be in the future.') if dexp and dwritten and dexp <= dwritten: self.add_error('expiration_date', 'Must be after written date.') auth = data.get('refills_authorized') rem = data.get('refills_remaining') if rem is not None and auth is not None and rem > auth: self.add_error('refills_remaining', 'Cannot exceed authorized refills.') return data class InventoryItemForm(forms.ModelForm): """ Form for pharmacy inventory item management. """ class Meta: model = PharmacyInventoryItem fields = [ 'medication', 'lot_number', 'expiration_date', 'quantity_on_hand', 'quantity_allocated', 'reorder_point', 'reorder_quantity', 'storage_location', 'bin_location', ] widgets = { 'medication': forms.Select(attrs={'class': 'form-select'}), 'lot_number': forms.TextInput(attrs={'class': 'form-control'}), 'expiration_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), 'quantity_on_hand': forms.NumberInput(attrs={'class': 'form-control', 'min': 0}), 'quantity_allocated': forms.NumberInput(attrs={'class': 'form-control', 'min': 0}), 'reorder_point': forms.NumberInput(attrs={'class': 'form-control', 'min': 0}), 'reorder_quantity': forms.NumberInput(attrs={'class': 'form-control', 'min': 0}), 'storage_location': forms.TextInput(attrs={'class': 'form-control'}), 'bin_location': forms.TextInput(attrs={'class': 'form-control'}), # 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def clean_expiration_date(self): exp = self.cleaned_data.get('expiration_date') if exp and exp <= date.today(): raise ValidationError('Must be future date.') return exp def clean(self): data = super().clean() rp = data.get('reorder_point') rq = data.get('reorder_quantity') if rp is not None and rq is not None and rq <= rp: self.add_error('reorder_quantity', 'Must exceed reorder point.') return data class DispenseRecordForm(forms.ModelForm): """ Form for dispense record management. """ class Meta: model = DispenseRecord fields = [ 'prescription', 'inventory_item', 'quantity_dispensed', 'dispensed_by', 'date_dispensed', 'patient_counseled', 'counseling_notes' ] widgets = { 'prescription': forms.Select(attrs={'class': 'form-select'}), 'inventory_item': forms.Select(attrs={'class': 'form-select'}), 'quantity_dispensed': forms.NumberInput(attrs={'class': 'form-control', 'min': 1}), 'dispensed_by': forms.Select(attrs={'class': 'form-select'}), 'date_dispensed': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}), 'patient_counseled': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'counseling_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), } def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) if user and hasattr(user, 'tenant'): self.fields['prescription'].queryset = Prescription.objects.filter( tenant=user.tenant, status__in=['ACTIVE', 'PENDING'] ).order_by('-date_prescribed') self.fields['inventory_item'].queryset = PharmacyInventoryItem.objects.filter( tenant=user.tenant, is_active=True, quantity_on_hand__gt=0 ).order_by('medication__generic_name') self.fields['dispensed_by'].queryset = User.objects.filter( tenant=user.tenant, role__in=['PHARMACIST', 'PHARMACY_TECH'] ).order_by('last_name') def clean_date_dispensed(self): dd = self.cleaned_data.get('date_dispensed') if dd and dd > timezone.now(): raise ValidationError('Cannot be in future.') return dd def clean(self): data = super().clean() pres = data.get('prescription') inv = data.get('inventory_item') qty = data.get('quantity_dispensed') if pres and inv and pres.medication != inv.medication: self.add_error('inventory_item', 'Medication mismatch.') if inv and qty and qty > inv.quantity_on_hand: self.add_error('quantity_dispensed', 'Exceeds on-hand.') return data class MedicationAdministrationForm(forms.ModelForm): """ Form for medication administration management. """ class Meta: model = MedicationAdministration fields = [ 'prescription', 'scheduled_datetime', 'actual_datetime', 'dose_given', 'route_given', 'status', 'administered_by', 'witnessed_by', 'reason_not_given', 'reason_notes', 'patient_response', 'side_effects_observed' ] widgets = { 'prescription': forms.Select(attrs={'class': 'form-select'}), 'scheduled_datetime': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}), 'actual_datetime': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}), 'dose_given': forms.TextInput(attrs={'class': 'form-control'}), 'route_given': forms.Select(attrs={'class': 'form-select'}), 'status': forms.Select(attrs={'class': 'form-select'}), 'administered_by': forms.Select(attrs={'class': 'form-select'}), 'witnessed_by': forms.Select(attrs={'class': 'form-select'}), 'reason_not_given': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), 'reason_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), 'patient_response': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), 'side_effects_observed': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), } def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) if user and hasattr(user, 'tenant'): self.fields['prescription'].queryset = Prescription.objects.filter( tenant=user.tenant ) self.fields['administered_by'].queryset = User.objects.filter( tenant=user.tenant, role__in=['NURSE', 'PHARMACIST'] ) self.fields['witnessed_by'].queryset = User.objects.filter( tenant=user.tenant, role='PHARMACIST' ) def clean(self): data = super().clean() status = data.get('status') reason = data.get('reason_not_given') dose = data.get('dose_given') if status == 'NOT_GIVEN' and not reason: self.add_error('reason_not_given', 'Provide a reason.') if status == 'GIVEN' and not dose: self.add_error('dose_given', 'Dose is required.') return data class DrugInteractionForm(forms.ModelForm): """ Form for drug interaction management. """ class Meta: model = DrugInteraction fields = [ 'medication_1', 'medication_2', 'interaction_type', 'severity', 'mechanism', 'clinical_effect', 'management_recommendations', 'monitoring_parameters', 'is_active' ] widgets = { 'medication_1': forms.Select(attrs={'class': 'form-select'}), 'medication_2': forms.Select(attrs={'class': 'form-select'}), 'interaction_type': forms.Select(attrs={'class': 'form-select'}), 'severity': forms.Select(attrs={'class': 'form-select'}), 'mechanism': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'clinical_effect': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'management_recommendations': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'monitoring_parameters': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) if user and hasattr(user, 'tenant'): meds = Medication.objects.filter(tenant=user.tenant, is_active=True) self.fields['medication_1'].queryset = meds self.fields['medication_2'].queryset = meds def clean(self): data = super().clean() if data.get('medication_1') == data.get('medication_2'): raise ValidationError('Medications must differ.') return data class PharmacySearchForm(forms.Form): """ Form for searching pharmacy data. """ search = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'class':'form-control','placeholder':'Search...'}) ) patient = forms.ModelChoiceField( queryset=PatientProfile.objects.none(), required=False, widget=forms.Select(attrs={'class':'form-select'}) ) medication = forms.ModelChoiceField( queryset=Medication.objects.none(), required=False, widget=forms.Select(attrs={'class':'form-select'}) ) prescriber = forms.ModelChoiceField( queryset=User.objects.none(), required=False, widget=forms.Select(attrs={'class':'form-select'}) ) status = forms.ChoiceField( choices=[('', 'All')] + list(Prescription._meta.get_field('status').choices), required=False, widget=forms.Select(attrs={'class':'form-select'}) ) date_from = forms.DateField(required=False, widget=forms.DateInput(attrs={'type':'date','class':'form-control'}) ) date_to = forms.DateField(required=False, widget=forms.DateInput(attrs={'type':'date','class':'form-control'}) ) def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) if user and hasattr(user, 'tenant'): self.fields['patient'].queryset = PatientProfile.objects.filter( tenant=user.tenant, is_active=True ).order_by('last_name') self.fields['medication'].queryset = Medication.objects.filter( tenant=user.tenant, is_active=True ).order_by('generic_name') self.fields['prescriber'].queryset = User.objects.filter( tenant=user.tenant, is_active=True, role__in=['DOCTOR','SPECIALIST'] ).order_by('last_name')