2364 lines
80 KiB
Python
2364 lines
80 KiB
Python
"""
|
|
Views for Pharmacy app.
|
|
"""
|
|
|
|
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.urls import reverse_lazy, reverse
|
|
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
|
|
from django.http import JsonResponse, HttpResponse
|
|
from django.db.models import Q, Count, Avg, F, Case, When, IntegerField, Max, Sum
|
|
from django.utils import timezone
|
|
from django.contrib import messages
|
|
from django.core.paginator import Paginator
|
|
from django.template.loader import render_to_string
|
|
from datetime import datetime, timedelta, date
|
|
import json
|
|
|
|
from core.models import AuditLogEntry
|
|
from core.utils import AuditLogger
|
|
from .models import (
|
|
Medication, Prescription, MedicationInventoryItem, DispenseRecord,
|
|
MedicationAdministration, DrugInteraction
|
|
)
|
|
from .forms import (
|
|
MedicationForm, PrescriptionForm, MedicationInventoryItemForm, DispenseRecordForm,
|
|
MedicationAdministrationForm, DrugInteractionForm, PharmacySearchForm
|
|
)
|
|
|
|
|
|
class PharmacyDashboardView(LoginRequiredMixin, ListView):
|
|
"""
|
|
Main dashboard for pharmacy management.
|
|
"""
|
|
template_name = 'pharmacy/dashboard.html'
|
|
context_object_name = 'prescriptions'
|
|
|
|
def get_queryset(self):
|
|
"""Get recent prescriptions for current tenant."""
|
|
return Prescription.objects.filter(
|
|
tenant=self.request.user.tenant,
|
|
status__in=['PENDING', 'VERIFIED', 'IN_PROGRESS']
|
|
).select_related('patient', 'prescriber').order_by('-created_at')[:10]
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
tenant = self.request.user.tenant
|
|
today = timezone.now().date()
|
|
|
|
# Dashboard statistics
|
|
context.update({
|
|
'pending_prescriptions': Prescription.objects.filter(
|
|
tenant=tenant,
|
|
status='PENDING'
|
|
).count(),
|
|
'pending_dispensing': Prescription.objects.filter(
|
|
tenant=tenant,
|
|
status='VERIFIED'
|
|
).count(),
|
|
# 'low_stock_items': InventoryItem.objects.filter(
|
|
# tenant=tenant,
|
|
# quantity_available__lte=F('minimum_stock_level')
|
|
# ).count(),
|
|
# 'expired_medications': InventoryItem.objects.filter(
|
|
# tenant=tenant,
|
|
# expiration_date__lte=today
|
|
# ).count(),
|
|
'todays_dispensed': DispenseRecord.objects.filter(
|
|
prescription__tenant=tenant,
|
|
date_dispensed__date=today
|
|
).count(),
|
|
'drug_interactions': DrugInteraction.objects.filter(
|
|
tenant=tenant,
|
|
severity__in=['HIGH', 'CRITICAL']
|
|
).count(),
|
|
# 'pending_orders': PharmacyOrder.objects.filter(
|
|
# tenant=tenant,
|
|
# status__in=['PENDING', 'ORDERED']
|
|
# ).count(),
|
|
'total_medications': Medication.objects.filter(tenant=tenant).count(),
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
class PrescriptionListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List view for prescriptions.
|
|
"""
|
|
model = Prescription
|
|
template_name = 'pharmacy/prescription_list.html'
|
|
context_object_name = 'prescriptions'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = Prescription.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
# Search functionality
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(patient__first_name__icontains=search) |
|
|
Q(patient__last_name__icontains=search) |
|
|
Q(patient__mrn__icontains=search) |
|
|
Q(medication__name__icontains=search) |
|
|
Q(prescribing_provider__first_name__icontains=search) |
|
|
Q(prescribing_provider__last_name__icontains=search)
|
|
)
|
|
|
|
# Filter by status
|
|
status = self.request.GET.get('status')
|
|
if status:
|
|
queryset = queryset.filter(status=status)
|
|
|
|
# Filter by priority
|
|
priority = self.request.GET.get('priority')
|
|
if priority:
|
|
queryset = queryset.filter(priority=priority)
|
|
|
|
# Filter by provider
|
|
provider_id = self.request.GET.get('provider')
|
|
if provider_id:
|
|
queryset = queryset.filter(prescriber__provider_id=provider_id)
|
|
|
|
# Filter by date range
|
|
date_from = self.request.GET.get('date_from')
|
|
date_to = self.request.GET.get('date_to')
|
|
if date_from:
|
|
queryset = queryset.filter(prescribed_datetime__date__gte=date_from)
|
|
if date_to:
|
|
queryset = queryset.filter(prescribed_datetime__date__lte=date_to)
|
|
|
|
return queryset.select_related(
|
|
'patient', 'medication', 'prescriber'
|
|
).order_by('-date_prescribed')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'prescription_statuses': Prescription._meta.get_field('status').choices,
|
|
# 'priorities': Prescription._meta.get_field('priority').choices,
|
|
})
|
|
return context
|
|
|
|
|
|
class PrescriptionCreateView(LoginRequiredMixin, CreateView):
|
|
"""
|
|
Create view for prescription.
|
|
"""
|
|
model = Prescription
|
|
form_class = PrescriptionForm
|
|
template_name = 'pharmacy/prescriptions/prescription_form.html'
|
|
success_url = reverse_lazy('pharmacy:prescription_list')
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
form.instance.created_by = self.request.user
|
|
response = super().form_valid(form)
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=self.request.user.tenant,
|
|
user=self.request.user,
|
|
action='CREATE',
|
|
model_name='Prescription',
|
|
object_id=self.object.id,
|
|
changes={
|
|
'patient': str(self.object.patient),
|
|
'medication': str(self.object.medication)
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'Prescription created successfully.')
|
|
return response
|
|
|
|
|
|
class PrescriptionDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Detail view for prescription.
|
|
"""
|
|
model = Prescription
|
|
template_name = 'pharmacy/prescriptions/prescription_detail.html'
|
|
context_object_name = 'prescription'
|
|
|
|
def get_queryset(self):
|
|
return Prescription.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
prescription = self.object
|
|
tenant = self.request.user.tenant
|
|
|
|
# Get related data
|
|
context.update({
|
|
'dispense_records': prescription.dispense_records.all().order_by('-date_dispensed'),
|
|
'drug_interactions': DrugInteraction.objects.filter(
|
|
Q(medication_1=prescription.medication) | Q(medication_2=prescription.medication),
|
|
tenant=tenant
|
|
),
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
class MedicationCreateView(LoginRequiredMixin, CreateView):
|
|
"""
|
|
Create view for medication.
|
|
"""
|
|
model = Medication
|
|
form_class = MedicationForm
|
|
template_name = 'pharmacy/medications/medication_form.html'
|
|
success_url = reverse_lazy('pharmacy:medication_list')
|
|
|
|
def form_valid(self, form):
|
|
form.instance.tenant = self.request.user.tenant
|
|
form.instance.created_by = self.request.user
|
|
response = super().form_valid(form)
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=self.request.user.tenant,
|
|
user=self.request.user,
|
|
action='CREATE',
|
|
model_name='Medication',
|
|
object_id=self.object.id,
|
|
changes={'name': self.object.name}
|
|
)
|
|
|
|
messages.success(self.request, f'Medication "{self.object.name}" created successfully.')
|
|
return response
|
|
|
|
|
|
class MedicationUpdateView(LoginRequiredMixin, UpdateView):
|
|
"""
|
|
Update view for medication.
|
|
"""
|
|
model = Medication
|
|
form_class = MedicationForm
|
|
template_name = 'pharmacy/medications/medication_form.html'
|
|
|
|
def get_queryset(self):
|
|
return Medication.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_success_url(self):
|
|
return reverse('pharmacy:medication_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=self.request.user.tenant,
|
|
user=self.request.user,
|
|
action='UPDATE',
|
|
model_name='Medication',
|
|
object_id=self.object.id,
|
|
changes={'name': self.object.name}
|
|
)
|
|
|
|
messages.success(self.request, f'Medication "{self.object.name}" updated successfully.')
|
|
return response
|
|
|
|
|
|
class MedicationDeleteView(LoginRequiredMixin, DeleteView):
|
|
"""
|
|
Delete view for medication (soft delete).
|
|
"""
|
|
model = Medication
|
|
template_name = 'pharmacy/medications/medication_confirm_delete.html'
|
|
success_url = reverse_lazy('pharmacy:medication_list')
|
|
|
|
def get_queryset(self):
|
|
return Medication.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
|
|
# Check if medication has active prescriptions
|
|
active_prescriptions = Prescription.objects.filter(
|
|
medication=self.object,
|
|
status='ACTIVE'
|
|
).count()
|
|
|
|
if active_prescriptions > 0:
|
|
messages.error(
|
|
request,
|
|
f'Cannot delete medication "{self.object.name}". It has {active_prescriptions} active prescriptions.'
|
|
)
|
|
return redirect('pharmacy:medication_detail', pk=self.object.pk)
|
|
|
|
# Soft delete
|
|
self.object.is_active = False
|
|
self.object.save()
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=request.user.tenant,
|
|
user=request.user,
|
|
action='DELETE',
|
|
model_name='Medication',
|
|
object_id=self.object.id,
|
|
changes={'name': self.object.name, 'is_active': False}
|
|
)
|
|
|
|
messages.success(request, f'Medication "{self.object.name}" deactivated successfully.')
|
|
return redirect(self.success_url)
|
|
|
|
|
|
class MedicationDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Detail view for medication with related prescriptions and inventory.
|
|
"""
|
|
model = Medication
|
|
template_name = 'pharmacy/medications/medication_detail.html'
|
|
context_object_name = 'medication'
|
|
|
|
def get_queryset(self):
|
|
return Medication.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
medication = self.object
|
|
|
|
# Related prescriptions
|
|
context['recent_prescriptions'] = Prescription.objects.filter(
|
|
medication=medication,
|
|
patient__tenant=self.request.user.tenant
|
|
).select_related('patient', 'prescriber').order_by('-date_prescribed')[:10]
|
|
|
|
# Inventory items
|
|
context['inventory_items'] = InventoryItem.objects.filter(
|
|
medication=medication,
|
|
status="ACTIVE"
|
|
).order_by('expiration_date')
|
|
|
|
# Drug interactions
|
|
context['interactions'] = DrugInteraction.objects.filter(
|
|
Q(medication_1=medication) | Q(medication_2=medication),
|
|
is_active=True
|
|
).select_related('medication_1', 'medication_2')
|
|
|
|
# Statistics
|
|
context['total_prescriptions'] = Prescription.objects.filter(
|
|
medication=medication,
|
|
patient__tenant=self.request.user.tenant
|
|
).count()
|
|
|
|
context['active_prescriptions'] = Prescription.objects.filter(
|
|
medication=medication,
|
|
patient__tenant=self.request.user.tenant,
|
|
status='ACTIVE'
|
|
).count()
|
|
|
|
context['total_inventory'] = InventoryItem.objects.filter(
|
|
medication=medication,
|
|
status="ACTIVE"
|
|
).aggregate(total=Sum('quantity_on_hand'))['total'] or 0
|
|
|
|
return context
|
|
|
|
|
|
class MedicationListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List view for medications.
|
|
"""
|
|
model = Medication
|
|
template_name = 'pharmacy/medications/medication_list.html'
|
|
context_object_name = 'medications'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = Medication.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
# Search functionality
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(name__icontains=search) |
|
|
Q(generic_name__icontains=search) |
|
|
Q(brand_name__icontains=search) |
|
|
Q(ndc_number__icontains=search)
|
|
)
|
|
|
|
# Filter by medication type
|
|
medication_type = self.request.GET.get('medication_type')
|
|
if medication_type:
|
|
queryset = queryset.filter(medication_type=medication_type)
|
|
|
|
# Filter by controlled substance
|
|
controlled_substance = self.request.GET.get('controlled_substance')
|
|
if controlled_substance:
|
|
queryset = queryset.filter(controlled_substance_schedule=controlled_substance)
|
|
|
|
# Filter by active status
|
|
active_only = self.request.GET.get('active_only')
|
|
if active_only:
|
|
queryset = queryset.filter(is_active=True)
|
|
|
|
return queryset.order_by('brand_name')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'medication_types': Medication.DosageForm.choices,
|
|
'controlled_schedules': Medication.ControlledSubstanceSchedule.choices,
|
|
})
|
|
return context
|
|
|
|
|
|
class InventoryItemListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List view for inventory items with search and filtering.
|
|
"""
|
|
model = MedicationInventoryItem
|
|
template_name = 'pharmacy/inventory/inventory_list.html'
|
|
context_object_name = 'inventory_items'
|
|
paginate_by = 20
|
|
|
|
def get_queryset(self):
|
|
queryset = MedicationInventoryItem.objects.filter(
|
|
medication__tenant=self.request.user.tenant
|
|
).select_related('medication').order_by('medication__generic_name', 'expiration_date')
|
|
|
|
# Apply search filters
|
|
form = InventoryItemForm(data=self.request.GET)
|
|
if form.is_valid():
|
|
search = form.cleaned_data.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(medication__name__icontains=search) |
|
|
Q(lot_number__icontains=search) |
|
|
Q(supplier__icontains=search) |
|
|
Q(location__icontains=search)
|
|
)
|
|
|
|
medication = form.cleaned_data.get('medication')
|
|
if medication:
|
|
queryset = queryset.filter(medication=medication)
|
|
|
|
location = form.cleaned_data.get('location')
|
|
if location:
|
|
queryset = queryset.filter(location__icontains=location)
|
|
|
|
low_stock = form.cleaned_data.get('low_stock')
|
|
if low_stock:
|
|
queryset = queryset.filter(quantity_on_hand__lte=F('reorder_point'))
|
|
|
|
expiring_soon = form.cleaned_data.get('expiring_soon')
|
|
if expiring_soon:
|
|
threshold = timezone.now().date() + timedelta(days=30)
|
|
queryset = queryset.filter(expiration_date__lte=threshold)
|
|
|
|
is_active = form.cleaned_data.get('is_active')
|
|
if is_active == 'true':
|
|
queryset = queryset.filter(is_active=True)
|
|
elif is_active == 'false':
|
|
queryset = queryset.filter(is_active=False)
|
|
|
|
return queryset
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['search_form'] = InventoryItemForm(
|
|
data=self.request.GET,
|
|
# user=self.request.user
|
|
)
|
|
return context
|
|
|
|
|
|
class InventoryItemDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Detail view for inventory item with dispense history.
|
|
"""
|
|
model = MedicationInventoryItem
|
|
template_name = 'pharmacy/inventory/inventory_detail.html'
|
|
context_object_name = 'inventory_item'
|
|
|
|
def get_queryset(self):
|
|
return MedicationInventoryItem.objects.filter(
|
|
medication__tenant=self.request.user.tenant
|
|
).select_related('medication')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
inventory_item = self.object
|
|
|
|
# Dispense history
|
|
context['dispense_records'] = DispenseRecord.objects.filter(
|
|
inventory_item=inventory_item
|
|
).select_related(
|
|
'prescription__patient', 'dispensed_by'
|
|
).order_by('-date_dispensed')
|
|
|
|
# Calculate total dispensed
|
|
context['total_dispensed'] = DispenseRecord.objects.filter(
|
|
inventory_item=inventory_item
|
|
).aggregate(total=Sum('quantity_dispensed'))['total'] or 0
|
|
|
|
# Days until expiry
|
|
if inventory_item.expiration_date:
|
|
days_until_expiry = (inventory_item.expiration_date - timezone.now().date()).days
|
|
context['days_until_expiry'] = days_until_expiry
|
|
context['is_expiring_soon'] = days_until_expiry <= 30
|
|
|
|
# Low stock alert
|
|
context['is_low_stock'] = inventory_item.quantity_on_hand <= inventory_item.reorder_point
|
|
|
|
return context
|
|
|
|
|
|
class InventoryItemCreateView(LoginRequiredMixin, CreateView):
|
|
"""
|
|
Create view for inventory item.
|
|
"""
|
|
model = MedicationInventoryItem
|
|
form_class = MedicationInventoryItemForm
|
|
template_name = 'pharmacy/inventory/inventory_form.html'
|
|
success_url = reverse_lazy('pharmacy:inventory_list')
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
form.instance.created_by = self.request.user
|
|
response = super().form_valid(form)
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=self.request.user.tenant,
|
|
user=self.request.user,
|
|
action='CREATE',
|
|
model_name='InventoryItem',
|
|
object_id=self.object.id,
|
|
changes={
|
|
'medication': str(self.object.medication),
|
|
'lot_number': self.object.lot_number,
|
|
'quantity': self.object.quantity_on_hand
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'Inventory item created successfully.')
|
|
return response
|
|
|
|
|
|
class InventoryItemUpdateView(LoginRequiredMixin, UpdateView):
|
|
"""
|
|
Update view for inventory item.
|
|
"""
|
|
model = MedicationInventoryItem
|
|
form_class = MedicationInventoryItemForm
|
|
template_name = 'pharmacy/inventory/inventory_form.html'
|
|
|
|
def get_queryset(self):
|
|
return InventoryItem.objects.filter(medication__tenant=self.request.user.tenant)
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def get_success_url(self):
|
|
return reverse('pharmacy:inventory_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=self.request.user.tenant,
|
|
user=self.request.user,
|
|
action='UPDATE',
|
|
model_name='InventoryItem',
|
|
object_id=self.object.id,
|
|
changes={'quantity': self.object.quantity_on_hand}
|
|
)
|
|
|
|
messages.success(self.request, 'Inventory item updated successfully.')
|
|
return response
|
|
|
|
|
|
class DispenseRecordListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List view for dispense records.
|
|
"""
|
|
model = DispenseRecord
|
|
template_name = 'pharmacy/dispense_record_list.html'
|
|
context_object_name = 'dispense_records'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = DispenseRecord.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
# Search functionality
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(prescription__patient__first_name__icontains=search) |
|
|
Q(prescription__patient__last_name__icontains=search) |
|
|
Q(prescription__patient__mrn__icontains=search) |
|
|
Q(prescription__medication__name__icontains=search)
|
|
)
|
|
|
|
# Filter by date range
|
|
date_from = self.request.GET.get('date_from')
|
|
date_to = self.request.GET.get('date_to')
|
|
if date_from:
|
|
queryset = queryset.filter(dispensed_datetime__date__gte=date_from)
|
|
if date_to:
|
|
queryset = queryset.filter(dispensed_datetime__date__lte=date_to)
|
|
|
|
# Filter by pharmacist
|
|
pharmacist_id = self.request.GET.get('pharmacist')
|
|
if pharmacist_id:
|
|
queryset = queryset.filter(dispensed_by_id=pharmacist_id)
|
|
|
|
return queryset.select_related(
|
|
'prescription__patient', 'prescription__medication', 'dispensed_by'
|
|
).order_by('-dispensed_datetime')
|
|
|
|
|
|
class DrugInteractionListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List view for drug interactions.
|
|
"""
|
|
model = DrugInteraction
|
|
template_name = 'pharmacy/interactions/drug_interaction_list.html'
|
|
context_object_name = 'interactions'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = DrugInteraction.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
# Search functionality
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(medication_a__name__icontains=search) |
|
|
Q(medication_b__name__icontains=search) |
|
|
Q(interaction_description__icontains=search)
|
|
)
|
|
|
|
# Filter by severity
|
|
severity = self.request.GET.get('severity')
|
|
if severity:
|
|
queryset = queryset.filter(severity=severity)
|
|
|
|
# Filter by interaction type
|
|
interaction_type = self.request.GET.get('interaction_type')
|
|
if interaction_type:
|
|
queryset = queryset.filter(interaction_type=interaction_type)
|
|
|
|
return queryset.select_related('medication_1', 'medication_2').order_by('-severity', 'medication_1')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'severities': DrugInteraction._meta.get_field('severity').choices,
|
|
'interaction_types': DrugInteraction._meta.get_field('interaction_type').choices,
|
|
})
|
|
return context
|
|
|
|
|
|
# Medication Administration Views
|
|
class MedicationAdministrationListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List view for medication administration records.
|
|
"""
|
|
model = MedicationAdministration
|
|
template_name = 'pharmacy/administration_list.html'
|
|
context_object_name = 'administrations'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = MedicationAdministration.objects.filter(
|
|
encounter__tenant=self.request.user.tenant
|
|
).select_related(
|
|
'prescription__patient', 'prescription__medication', 'administered_by'
|
|
).order_by('-scheduled_datetime')
|
|
|
|
# Search functionality
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(prescription__patient__first_name__icontains=search) |
|
|
Q(prescription__patient__last_name__icontains=search) |
|
|
Q(prescription__patient__mrn__icontains=search) |
|
|
Q(prescription__medication__generic_name__icontains=search) |
|
|
Q(prescription__medication__brand_name__icontains=search)
|
|
)
|
|
|
|
# Filter by status
|
|
status = self.request.GET.get('status')
|
|
if status:
|
|
queryset = queryset.filter(status=status)
|
|
|
|
# Filter by route
|
|
route = self.request.GET.get('route')
|
|
if route:
|
|
queryset = queryset.filter(route_given=route)
|
|
|
|
# Filter by date range
|
|
date_from = self.request.GET.get('date_from')
|
|
date_to = self.request.GET.get('date_to')
|
|
if date_from:
|
|
queryset = queryset.filter(scheduled_datetime__date__gte=date_from)
|
|
if date_to:
|
|
queryset = queryset.filter(scheduled_datetime__date__lte=date_to)
|
|
|
|
return queryset
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
tenant = self.request.user.tenant
|
|
today = timezone.now().date()
|
|
|
|
# Statistics
|
|
context['todays_administrations'] = MedicationAdministration.objects.filter(
|
|
encounter__tenant=tenant,
|
|
scheduled_datetime__date=today
|
|
).count()
|
|
|
|
context['given_today'] = MedicationAdministration.objects.filter(
|
|
encounter__tenant=tenant,
|
|
scheduled_datetime__date=today,
|
|
status='GIVEN'
|
|
).count()
|
|
|
|
context['pending_administrations'] = MedicationAdministration.objects.filter(
|
|
encounter__tenant=tenant,
|
|
scheduled_datetime__gte=timezone.now(),
|
|
status='SCHEDULED'
|
|
).count()
|
|
|
|
context['overdue_administrations'] = MedicationAdministration.objects.filter(
|
|
encounter__tenant=tenant,
|
|
scheduled_datetime__lt=timezone.now(),
|
|
status='SCHEDULED'
|
|
).count()
|
|
|
|
return context
|
|
|
|
|
|
class MedicationAdministrationDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Detail view for medication administration record.
|
|
"""
|
|
model = MedicationAdministration
|
|
template_name = 'pharmacy/administration_detail.html'
|
|
context_object_name = 'administration'
|
|
|
|
def get_queryset(self):
|
|
return MedicationAdministration.objects.filter(
|
|
tenant=self.request.user.tenant
|
|
).select_related(
|
|
'prescription__patient', 'prescription__medication',
|
|
'administered_by', 'witnessed_by', 'double_checked_by'
|
|
)
|
|
|
|
|
|
class MedicationAdministrationCreateView(LoginRequiredMixin, CreateView):
|
|
"""
|
|
Create view for medication administration record.
|
|
"""
|
|
model = MedicationAdministration
|
|
form_class = MedicationAdministrationForm
|
|
template_name = 'pharmacy/administration_form.html'
|
|
success_url = reverse_lazy('pharmacy:administration_list')
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
form.instance.tenant = self.request.user.tenant
|
|
response = super().form_valid(form)
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=self.request.user.tenant,
|
|
user=self.request.user,
|
|
action='CREATE',
|
|
model_name='MedicationAdministration',
|
|
object_id=self.object.id,
|
|
changes={
|
|
'prescription': str(self.object.prescription),
|
|
'status': self.object.status
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'Medication administration recorded successfully.')
|
|
return response
|
|
|
|
|
|
class MedicationAdministrationUpdateView(LoginRequiredMixin, UpdateView):
|
|
"""
|
|
Update view for medication administration record.
|
|
"""
|
|
model = MedicationAdministration
|
|
form_class = MedicationAdministrationForm
|
|
template_name = 'pharmacy/administration_form.html'
|
|
|
|
def get_queryset(self):
|
|
return MedicationAdministration.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_success_url(self):
|
|
return reverse('pharmacy:administration_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=self.request.user.tenant,
|
|
user=self.request.user,
|
|
action='UPDATE',
|
|
model_name='MedicationAdministration',
|
|
object_id=self.object.id,
|
|
changes={'status': self.object.status}
|
|
)
|
|
|
|
messages.success(self.request, 'Medication administration updated successfully.')
|
|
return response
|
|
|
|
|
|
@login_required
|
|
def pharmacy_stats(request):
|
|
"""
|
|
HTMX endpoint for pharmacy statistics.
|
|
"""
|
|
tenant = request.user.tenant
|
|
today = timezone.now().date()
|
|
|
|
stats = {
|
|
'pending_prescriptions': Prescription.objects.filter(
|
|
tenant=tenant,
|
|
status='PENDING'
|
|
).count(),
|
|
'pending_dispensing': Prescription.objects.filter(
|
|
tenant=tenant,
|
|
status='VERIFIED'
|
|
).count(),
|
|
'low_stock_items': InventoryItem.objects.filter(
|
|
tenant=tenant,
|
|
quantity_on_hand__lte=F('reorder_point')
|
|
).count(),
|
|
'expired_medications': InventoryItem.objects.filter(
|
|
tenant=tenant,
|
|
expiration_date__lte=today
|
|
).count(),
|
|
}
|
|
|
|
return render(request, 'pharmacy/partials/pharmacy_stats.html', {'stats': stats})
|
|
|
|
|
|
@login_required
|
|
def prescription_search(request):
|
|
"""
|
|
HTMX endpoint for prescription search.
|
|
"""
|
|
search = request.GET.get('search', '')
|
|
status = request.GET.get('status', '')
|
|
priority = request.GET.get('priority', '')
|
|
|
|
queryset = Prescription.objects.filter(tenant=request.user.tenant)
|
|
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(patient__first_name__icontains=search) |
|
|
Q(patient__last_name__icontains=search) |
|
|
Q(patient__mrn__icontains=search) |
|
|
Q(medication__name__icontains=search)
|
|
)
|
|
|
|
if status:
|
|
queryset = queryset.filter(status=status)
|
|
|
|
if priority:
|
|
queryset = queryset.filter(priority=priority)
|
|
|
|
prescriptions = queryset.select_related(
|
|
'patient', 'medication', 'prescribing_provider'
|
|
).order_by('-prescribed_datetime')[:20]
|
|
|
|
return render(request, 'pharmacy/prescriptions/prescription_list.html', {'prescriptions': prescriptions})
|
|
|
|
|
|
@login_required
|
|
def inventory_alerts(request):
|
|
"""
|
|
HTMX endpoint for inventory alerts.
|
|
"""
|
|
tenant = request.user.tenant
|
|
today = timezone.now().date()
|
|
thirty_days = today + timedelta(days=30)
|
|
|
|
# Low stock items
|
|
low_stock = InventoryItem.objects.filter(
|
|
tenant=tenant,
|
|
quantity_on_hand__lte=F('reorder_point')
|
|
).select_related('medication')[:10]
|
|
|
|
# Expiring items
|
|
expiring = InventoryItem.objects.filter(
|
|
tenant=tenant,
|
|
expiration_date__lte=thirty_days,
|
|
expiration_date__gt=today
|
|
).select_related('medication')[:10]
|
|
|
|
# Expired items
|
|
expired = InventoryItem.objects.filter(
|
|
tenant=tenant,
|
|
expiration_date__lte=today
|
|
).select_related('medication')[:10]
|
|
|
|
return render(request, 'pharmacy/partials/inventory_alerts.html', {
|
|
'low_stock': low_stock,
|
|
'expiring': expiring,
|
|
'expired': expired
|
|
})
|
|
|
|
|
|
@login_required
|
|
def drug_interaction_check(request, prescription_id):
|
|
"""
|
|
HTMX endpoint for drug interaction check.
|
|
"""
|
|
prescription = get_object_or_404(
|
|
Prescription,
|
|
id=prescription_id,
|
|
tenant=request.user.tenant
|
|
)
|
|
|
|
# Check for interactions with this medication
|
|
interactions = DrugInteraction.objects.filter(
|
|
Q(medication_a=prescription.medication) | Q(medication_b=prescription.medication),
|
|
tenant=request.user.tenant
|
|
).select_related('medication_a', 'medication_b')
|
|
|
|
return render(request, 'pharmacy/drug_interactions/drug_interactions.html', {
|
|
'prescription': prescription,
|
|
'interactions': interactions
|
|
})
|
|
|
|
|
|
@login_required
|
|
def verify_prescription(request, prescription_id):
|
|
"""
|
|
Verify a prescription.
|
|
"""
|
|
if request.method == 'POST':
|
|
prescription = get_object_or_404(
|
|
Prescription,
|
|
id=prescription_id,
|
|
tenant=request.user.tenant
|
|
)
|
|
|
|
prescription.status = 'VERIFIED'
|
|
prescription.verified_by = request.user
|
|
prescription.verified_datetime = timezone.now()
|
|
prescription.save()
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=request.user,
|
|
action='VERIFY_PRESCRIPTION',
|
|
model_name='Prescription',
|
|
object_id=prescription.id,
|
|
details=f'Prescription verified for {prescription.patient.get_full_name()}'
|
|
)
|
|
|
|
messages.success(request, 'Prescription verified successfully.')
|
|
|
|
if request.headers.get('HX-Request'):
|
|
return JsonResponse({'status': 'success'})
|
|
|
|
return redirect('pharmacy:prescription_detail', pk=prescription.id)
|
|
|
|
return JsonResponse({'status': 'error'})
|
|
|
|
|
|
@login_required
|
|
def dispense_medication(request, prescription_id):
|
|
"""
|
|
Dispense medication for a prescription.
|
|
"""
|
|
if request.method == 'POST':
|
|
prescription = get_object_or_404(
|
|
Prescription,
|
|
id=prescription_id,
|
|
tenant=request.user.tenant
|
|
)
|
|
|
|
quantity_dispensed = request.POST.get('quantity_dispensed')
|
|
lot_number = request.POST.get('lot_number')
|
|
|
|
# Create dispense record
|
|
dispense_record = DispenseRecord.objects.create(
|
|
tenant=request.user.tenant,
|
|
prescription=prescription,
|
|
quantity_dispensed=quantity_dispensed,
|
|
dispensed_by=request.user,
|
|
dispensed_datetime=timezone.now(),
|
|
lot_number=lot_number
|
|
)
|
|
|
|
# Update prescription status
|
|
prescription.status = 'DISPENSED'
|
|
prescription.save()
|
|
|
|
# Update inventory
|
|
try:
|
|
inventory_item = InventoryItem.objects.get(
|
|
tenant=request.user.tenant,
|
|
medication=prescription.medication,
|
|
lot_number=lot_number
|
|
)
|
|
inventory_item.current_stock -= int(quantity_dispensed)
|
|
inventory_item.save()
|
|
except InventoryItem.DoesNotExist:
|
|
pass
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=request.user,
|
|
action='DISPENSE_MEDICATION',
|
|
model_name='DispenseRecord',
|
|
object_id=dispense_record.id,
|
|
details=f'Dispensed {quantity_dispensed} units of {prescription.medication.name}'
|
|
)
|
|
|
|
messages.success(request, 'Medication dispensed successfully.')
|
|
|
|
if request.headers.get('HX-Request'):
|
|
return JsonResponse({'status': 'success'})
|
|
|
|
return redirect('pharmacy:prescription_detail', pk=prescription.id)
|
|
|
|
return JsonResponse({'status': 'error'})
|
|
|
|
|
|
@login_required
|
|
def update_inventory(request, item_id):
|
|
"""
|
|
Update inventory item stock.
|
|
"""
|
|
if request.method == 'POST':
|
|
item = get_object_or_404(
|
|
InventoryItem,
|
|
id=item_id,
|
|
tenant=request.user.tenant
|
|
)
|
|
|
|
new_stock = request.POST.get('new_stock')
|
|
adjustment_reason = request.POST.get('adjustment_reason', '')
|
|
|
|
old_stock = item.current_stock
|
|
item.current_stock = int(new_stock)
|
|
item.last_updated = timezone.now()
|
|
item.save()
|
|
|
|
# Log the action
|
|
AuditLogger.log_event(
|
|
user=request.user,
|
|
action='UPDATE_INVENTORY',
|
|
model_name='InventoryItem',
|
|
object_id=item.id,
|
|
details=f'Stock updated from {old_stock} to {new_stock}. Reason: {adjustment_reason}'
|
|
)
|
|
|
|
messages.success(request, 'Inventory updated successfully.')
|
|
|
|
if request.headers.get('HX-Request'):
|
|
return JsonResponse({'status': 'success'})
|
|
|
|
return redirect('pharmacy:inventory_list')
|
|
|
|
return JsonResponse({'status': 'error'})
|
|
|
|
|
|
def get_medication_info(request, pk):
|
|
medication = get_object_or_404(Medication, pk=pk)
|
|
medication_info = {
|
|
'generic_name': medication.generic_name,
|
|
'brand_name': medication.brand_name,
|
|
|
|
}
|
|
return JsonResponse(medication_info)
|
|
#
|
|
# """
|
|
# Views for Pharmacy app with comprehensive CRUD operations following healthcare best practices.
|
|
# """
|
|
#
|
|
# from django.shortcuts import render, get_object_or_404, redirect
|
|
# from django.contrib.auth.decorators import login_required
|
|
# from django.contrib.auth.mixins import LoginRequiredMixin
|
|
# from django.contrib import messages
|
|
# from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
|
|
# from django.urls import reverse_lazy, reverse
|
|
# from django.db.models import Q, Count, Sum, Avg, F
|
|
# from django.http import JsonResponse, HttpResponse
|
|
# from django.utils import timezone
|
|
# from django.core.paginator import Paginator
|
|
# from datetime import datetime, timedelta
|
|
# import csv
|
|
#
|
|
# from .models import *
|
|
# from .forms import *
|
|
# from core.models import AuditLogEntry
|
|
# from patients.models import PatientProfile
|
|
#
|
|
#
|
|
# class PharmacyDashboardView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# Pharmacy dashboard with comprehensive statistics and recent activity.
|
|
# """
|
|
# template_name = 'pharmacy/dashboard.html'
|
|
# context_object_name = 'recent_prescriptions'
|
|
# paginate_by = 10
|
|
#
|
|
# def get_queryset(self):
|
|
# return Prescription.objects.filter(
|
|
# patient__tenant=self.request.user.tenant
|
|
# ).select_related(
|
|
# 'patient', 'medication', 'prescriber'
|
|
# ).order_by('-prescribed_date')[:10]
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# user = self.request.user
|
|
#
|
|
# # Basic statistics
|
|
# context['total_medications'] = Medication.objects.filter(
|
|
# tenant=user.tenant, is_active=True
|
|
# ).count()
|
|
#
|
|
# context['active_prescriptions'] = Prescription.objects.filter(
|
|
# patient__tenant=user.tenant,
|
|
# status='ACTIVE'
|
|
# ).count()
|
|
#
|
|
# context['pending_prescriptions'] = Prescription.objects.filter(
|
|
# patient__tenant=user.tenant,
|
|
# status='PENDING'
|
|
# ).count()
|
|
#
|
|
# context['low_stock_items'] = InventoryItem.objects.filter(
|
|
# medication__tenant=user.tenant,
|
|
# is_active=True,
|
|
# quantity_on_hand__lte=F('reorder_point')
|
|
# ).count()
|
|
#
|
|
# # Today's statistics
|
|
# today = timezone.now().date()
|
|
# context['prescriptions_today'] = Prescription.objects.filter(
|
|
# patient__tenant=user.tenant,
|
|
# prescribed_date__date=today
|
|
# ).count()
|
|
#
|
|
# context['dispensed_today'] = DispenseRecord.objects.filter(
|
|
# prescription__patient__tenant=user.tenant,
|
|
# dispensed_date__date=today
|
|
# ).count()
|
|
#
|
|
# context['administered_today'] = MedicationAdministration.objects.filter(
|
|
# patient__tenant=user.tenant,
|
|
# administered_date=today,
|
|
# status='GIVEN'
|
|
# ).count()
|
|
#
|
|
# # Expiring medications (next 30 days)
|
|
# expiry_threshold = today + timedelta(days=30)
|
|
# context['expiring_medications'] = InventoryItem.objects.filter(
|
|
# medication__tenant=user.tenant,
|
|
# is_active=True,
|
|
# expiry_date__lte=expiry_threshold,
|
|
# expiry_date__gt=today
|
|
# ).count()
|
|
#
|
|
# # Recent activity
|
|
# context['recent_dispenses'] = DispenseRecord.objects.filter(
|
|
# prescription__patient__tenant=user.tenant
|
|
# ).select_related(
|
|
# 'prescription__patient', 'prescription__medication', 'dispensed_by'
|
|
# ).order_by('-dispensed_date')[:5]
|
|
#
|
|
# context['recent_administrations'] = MedicationAdministration.objects.filter(
|
|
# patient__tenant=user.tenant
|
|
# ).select_related(
|
|
# 'patient', 'medication', 'administered_by'
|
|
# ).order_by('-administered_date')[:5]
|
|
#
|
|
# # Drug interactions count
|
|
# context['drug_interactions'] = DrugInteraction.objects.filter(
|
|
# medication_1__tenant=user.tenant,
|
|
# is_active=True
|
|
# ).count()
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# # Medication Views
|
|
# class MedicationListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List view for medications with search and filtering.
|
|
# """
|
|
# model = Medication
|
|
# template_name = 'pharmacy/medication_list.html'
|
|
# context_object_name = 'medications'
|
|
# paginate_by = 20
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = Medication.objects.filter(
|
|
# tenant=self.request.user.tenant
|
|
# ).order_by('name')
|
|
#
|
|
# search = self.request.GET.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(name__icontains=search) |
|
|
# Q(generic_name__icontains=search) |
|
|
# Q(brand_name__icontains=search) |
|
|
# Q(ndc_number__icontains=search)
|
|
# )
|
|
#
|
|
# drug_class = self.request.GET.get('drug_class')
|
|
# if drug_class:
|
|
# queryset = queryset.filter(drug_class__icontains=drug_class)
|
|
#
|
|
# is_active = self.request.GET.get('is_active')
|
|
# if is_active == 'true':
|
|
# queryset = queryset.filter(is_active=True)
|
|
# elif is_active == 'false':
|
|
# queryset = queryset.filter(is_active=False)
|
|
#
|
|
# return queryset
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context['search_form'] = PharmacySearchForm(
|
|
# data=self.request.GET,
|
|
# user=self.request.user
|
|
# )
|
|
# return context
|
|
#
|
|
#
|
|
# class MedicationDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Detail view for medication with related prescriptions and inventory.
|
|
# """
|
|
# model = Medication
|
|
# template_name = 'pharmacy/medication_detail.html'
|
|
# context_object_name = 'medication'
|
|
#
|
|
# def get_queryset(self):
|
|
# return Medication.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# medication = self.object
|
|
#
|
|
# # Related prescriptions
|
|
# context['recent_prescriptions'] = Prescription.objects.filter(
|
|
# medication=medication,
|
|
# patient__tenant=self.request.user.tenant
|
|
# ).select_related('patient', 'prescriber').order_by('-prescribed_date')[:10]
|
|
#
|
|
# # Inventory items
|
|
# context['inventory_items'] = InventoryItem.objects.filter(
|
|
# medication=medication,
|
|
# is_active=True
|
|
# ).order_by('expiry_date')
|
|
#
|
|
# # Drug interactions
|
|
# context['interactions'] = DrugInteraction.objects.filter(
|
|
# Q(medication_1=medication) | Q(medication_2=medication),
|
|
# is_active=True
|
|
# ).select_related('medication_1', 'medication_2')
|
|
#
|
|
# # Statistics
|
|
# context['total_prescriptions'] = Prescription.objects.filter(
|
|
# medication=medication,
|
|
# patient__tenant=self.request.user.tenant
|
|
# ).count()
|
|
#
|
|
# context['active_prescriptions'] = Prescription.objects.filter(
|
|
# medication=medication,
|
|
# patient__tenant=self.request.user.tenant,
|
|
# status='ACTIVE'
|
|
# ).count()
|
|
#
|
|
# context['total_inventory'] = InventoryItem.objects.filter(
|
|
# medication=medication,
|
|
# is_active=True
|
|
# ).aggregate(total=Sum('quantity_on_hand'))['total'] or 0
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class MedicationCreateView(LoginRequiredMixin, CreateView):
|
|
# """
|
|
# Create view for medication.
|
|
# """
|
|
# model = Medication
|
|
# form_class = MedicationForm
|
|
# template_name = 'pharmacy/medication_form.html'
|
|
# success_url = reverse_lazy('pharmacy:medication_list')
|
|
#
|
|
# def form_valid(self, form):
|
|
# form.instance.tenant = self.request.user.tenant
|
|
# form.instance.created_by = self.request.user
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Create audit log
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=self.request.user.tenant,
|
|
# user=self.request.user,
|
|
# action='CREATE',
|
|
# model_name='Medication',
|
|
# object_id=self.object.id,
|
|
# changes={'name': self.object.name}
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Medication "{self.object.name}" created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class MedicationUpdateView(LoginRequiredMixin, UpdateView):
|
|
# """
|
|
# Update view for medication.
|
|
# """
|
|
# model = Medication
|
|
# form_class = MedicationForm
|
|
# template_name = 'pharmacy/medication_form.html'
|
|
#
|
|
# def get_queryset(self):
|
|
# return Medication.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('pharmacy:medication_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Create audit log
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=self.request.user.tenant,
|
|
# user=self.request.user,
|
|
# action='UPDATE',
|
|
# model_name='Medication',
|
|
# object_id=self.object.id,
|
|
# changes={'name': self.object.name}
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Medication "{self.object.name}" updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class MedicationDeleteView(LoginRequiredMixin, DeleteView):
|
|
# """
|
|
# Delete view for medication (soft delete).
|
|
# """
|
|
# model = Medication
|
|
# template_name = 'pharmacy/medication_confirm_delete.html'
|
|
# success_url = reverse_lazy('pharmacy:medication_list')
|
|
#
|
|
# def get_queryset(self):
|
|
# return Medication.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def delete(self, request, *args, **kwargs):
|
|
# self.object = self.get_object()
|
|
#
|
|
# # Check if medication has active prescriptions
|
|
# active_prescriptions = Prescription.objects.filter(
|
|
# medication=self.object,
|
|
# status='ACTIVE'
|
|
# ).count()
|
|
#
|
|
# if active_prescriptions > 0:
|
|
# messages.error(
|
|
# request,
|
|
# f'Cannot delete medication "{self.object.name}". It has {active_prescriptions} active prescriptions.'
|
|
# )
|
|
# return redirect('pharmacy:medication_detail', pk=self.object.pk)
|
|
#
|
|
# # Soft delete
|
|
# self.object.is_active = False
|
|
# self.object.save()
|
|
#
|
|
# # Create audit log
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=request.user.tenant,
|
|
# user=request.user,
|
|
# action='DELETE',
|
|
# model_name='Medication',
|
|
# object_id=self.object.id,
|
|
# changes={'name': self.object.name, 'is_active': False}
|
|
# )
|
|
#
|
|
# messages.success(request, f'Medication "{self.object.name}" deactivated successfully.')
|
|
# return redirect(self.success_url)
|
|
#
|
|
#
|
|
# # Prescription Views
|
|
# class PrescriptionListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List view for prescriptions with search and filtering.
|
|
# """
|
|
# model = Prescription
|
|
# template_name = 'pharmacy/prescription_list.html'
|
|
# context_object_name = 'prescriptions'
|
|
# paginate_by = 20
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = Prescription.objects.filter(
|
|
# patient__tenant=self.request.user.tenant
|
|
# ).select_related(
|
|
# 'patient', 'medication', 'prescriber'
|
|
# ).order_by('-prescribed_date')
|
|
#
|
|
# # Apply search filters
|
|
# form = PharmacySearchForm(data=self.request.GET, user=self.request.user)
|
|
# if form.is_valid():
|
|
# search = form.cleaned_data.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(patient__first_name__icontains=search) |
|
|
# Q(patient__last_name__icontains=search) |
|
|
# Q(medication__name__icontains=search) |
|
|
# Q(prescriber__first_name__icontains=search) |
|
|
# Q(prescriber__last_name__icontains=search)
|
|
# )
|
|
#
|
|
# patient = form.cleaned_data.get('patient')
|
|
# if patient:
|
|
# queryset = queryset.filter(patient=patient)
|
|
#
|
|
# medication = form.cleaned_data.get('medication')
|
|
# if medication:
|
|
# queryset = queryset.filter(medication=medication)
|
|
#
|
|
# prescriber = form.cleaned_data.get('prescriber')
|
|
# if prescriber:
|
|
# queryset = queryset.filter(prescriber=prescriber)
|
|
#
|
|
# status = form.cleaned_data.get('status')
|
|
# if status:
|
|
# queryset = queryset.filter(status=status)
|
|
#
|
|
# priority = form.cleaned_data.get('priority')
|
|
# if priority:
|
|
# queryset = queryset.filter(priority=priority)
|
|
#
|
|
# date_from = form.cleaned_data.get('date_from')
|
|
# if date_from:
|
|
# queryset = queryset.filter(prescribed_date__date__gte=date_from)
|
|
#
|
|
# date_to = form.cleaned_data.get('date_to')
|
|
# if date_to:
|
|
# queryset = queryset.filter(prescribed_date__date__lte=date_to)
|
|
#
|
|
# return queryset
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context['search_form'] = PharmacySearchForm(
|
|
# data=self.request.GET,
|
|
# user=self.request.user
|
|
# )
|
|
# return context
|
|
#
|
|
#
|
|
# class PrescriptionDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Detail view for prescription with dispense records.
|
|
# """
|
|
# model = Prescription
|
|
# template_name = 'pharmacy/prescription_detail.html'
|
|
# context_object_name = 'prescription'
|
|
#
|
|
# def get_queryset(self):
|
|
# return Prescription.objects.filter(
|
|
# patient__tenant=self.request.user.tenant
|
|
# ).select_related('patient', 'medication', 'prescriber')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# prescription = self.object
|
|
#
|
|
# # Dispense records
|
|
# context['dispense_records'] = DispenseRecord.objects.filter(
|
|
# prescription=prescription
|
|
# ).select_related('inventory_item', 'dispensed_by').order_by('-dispensed_date')
|
|
#
|
|
# # Available inventory
|
|
# context['available_inventory'] = InventoryItem.objects.filter(
|
|
# medication=prescription.medication,
|
|
# is_active=True,
|
|
# quantity_on_hand__gt=0
|
|
# ).order_by('expiry_date')
|
|
#
|
|
# # Calculate dispensed quantity
|
|
# context['total_dispensed'] = DispenseRecord.objects.filter(
|
|
# prescription=prescription
|
|
# ).aggregate(total=Sum('quantity_dispensed'))['total'] or 0
|
|
#
|
|
# context['remaining_quantity'] = prescription.quantity - context['total_dispensed']
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class PrescriptionCreateView(LoginRequiredMixin, CreateView):
|
|
# """
|
|
# Create view for prescription.
|
|
# """
|
|
# model = Prescription
|
|
# form_class = PrescriptionForm
|
|
# template_name = 'pharmacy/prescription_form.html'
|
|
# success_url = reverse_lazy('pharmacy:prescription_list')
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def form_valid(self, form):
|
|
# form.instance.created_by = self.request.user
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Create audit log
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=self.request.user.tenant,
|
|
# user=self.request.user,
|
|
# action='CREATE',
|
|
# model_name='Prescription',
|
|
# object_id=self.object.id,
|
|
# changes={
|
|
# 'patient': str(self.object.patient),
|
|
# 'medication': str(self.object.medication)
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Prescription created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class PrescriptionUpdateView(LoginRequiredMixin, UpdateView):
|
|
# """
|
|
# Update view for prescription (limited updates).
|
|
# """
|
|
# model = Prescription
|
|
# form_class = PrescriptionForm
|
|
# template_name = 'pharmacy/prescription_form.html'
|
|
#
|
|
# def get_queryset(self):
|
|
# return Prescription.objects.filter(patient__tenant=self.request.user.tenant)
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('pharmacy:prescription_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# # Restrict updates for filled prescriptions
|
|
# if self.object.status == 'FILLED':
|
|
# messages.error(self.request, 'Cannot update filled prescriptions.')
|
|
# return redirect('pharmacy:prescription_detail', pk=self.object.pk)
|
|
#
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Create audit log
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=self.request.user.tenant,
|
|
# user=self.request.user,
|
|
# action='UPDATE',
|
|
# model_name='Prescription',
|
|
# object_id=self.object.id,
|
|
# changes={'status': self.object.status}
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Prescription updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # Inventory Views
|
|
# class InventoryItemListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List view for inventory items with search and filtering.
|
|
# """
|
|
# model = InventoryItem
|
|
# template_name = 'pharmacy/inventory_list.html'
|
|
# context_object_name = 'inventory_items'
|
|
# paginate_by = 20
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = InventoryItem.objects.filter(
|
|
# medication__tenant=self.request.user.tenant
|
|
# ).select_related('medication').order_by('medication__name', 'expiry_date')
|
|
#
|
|
# # Apply search filters
|
|
# form = InventorySearchForm(data=self.request.GET, user=self.request.user)
|
|
# if form.is_valid():
|
|
# search = form.cleaned_data.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(medication__name__icontains=search) |
|
|
# Q(lot_number__icontains=search) |
|
|
# Q(supplier__icontains=search) |
|
|
# Q(location__icontains=search)
|
|
# )
|
|
#
|
|
# medication = form.cleaned_data.get('medication')
|
|
# if medication:
|
|
# queryset = queryset.filter(medication=medication)
|
|
#
|
|
# location = form.cleaned_data.get('location')
|
|
# if location:
|
|
# queryset = queryset.filter(location__icontains=location)
|
|
#
|
|
# low_stock = form.cleaned_data.get('low_stock')
|
|
# if low_stock:
|
|
# queryset = queryset.filter(quantity_on_hand__lte=F('reorder_point'))
|
|
#
|
|
# expiring_soon = form.cleaned_data.get('expiring_soon')
|
|
# if expiring_soon:
|
|
# threshold = timezone.now().date() + timedelta(days=30)
|
|
# queryset = queryset.filter(expiry_date__lte=threshold)
|
|
#
|
|
# is_active = form.cleaned_data.get('is_active')
|
|
# if is_active == 'true':
|
|
# queryset = queryset.filter(is_active=True)
|
|
# elif is_active == 'false':
|
|
# queryset = queryset.filter(is_active=False)
|
|
#
|
|
# return queryset
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context['search_form'] = InventorySearchForm(
|
|
# data=self.request.GET,
|
|
# user=self.request.user
|
|
# )
|
|
# return context
|
|
#
|
|
#
|
|
# class InventoryItemDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Detail view for inventory item with dispense history.
|
|
# """
|
|
# model = InventoryItem
|
|
# template_name = 'pharmacy/inventory_detail.html'
|
|
# context_object_name = 'inventory_item'
|
|
#
|
|
# def get_queryset(self):
|
|
# return InventoryItem.objects.filter(
|
|
# medication__tenant=self.request.user.tenant
|
|
# ).select_related('medication')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# inventory_item = self.object
|
|
#
|
|
# # Dispense history
|
|
# context['dispense_records'] = DispenseRecord.objects.filter(
|
|
# inventory_item=inventory_item
|
|
# ).select_related(
|
|
# 'prescription__patient', 'dispensed_by'
|
|
# ).order_by('-dispensed_date')
|
|
#
|
|
# # Calculate total dispensed
|
|
# context['total_dispensed'] = DispenseRecord.objects.filter(
|
|
# inventory_item=inventory_item
|
|
# ).aggregate(total=Sum('quantity_dispensed'))['total'] or 0
|
|
#
|
|
# # Days until expiry
|
|
# if inventory_item.expiry_date:
|
|
# days_until_expiry = (inventory_item.expiry_date - timezone.now().date()).days
|
|
# context['days_until_expiry'] = days_until_expiry
|
|
# context['is_expiring_soon'] = days_until_expiry <= 30
|
|
#
|
|
# # Low stock alert
|
|
# context['is_low_stock'] = inventory_item.quantity_on_hand <= inventory_item.reorder_point
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class InventoryItemCreateView(LoginRequiredMixin, CreateView):
|
|
# """
|
|
# Create view for inventory item.
|
|
# """
|
|
# model = InventoryItem
|
|
# form_class = InventoryItemForm
|
|
# template_name = 'pharmacy/inventory_form.html'
|
|
# success_url = reverse_lazy('pharmacy:inventory_list')
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def form_valid(self, form):
|
|
# form.instance.created_by = self.request.user
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Create audit log
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=self.request.user.tenant,
|
|
# user=self.request.user,
|
|
# action='CREATE',
|
|
# model_name='InventoryItem',
|
|
# object_id=self.object.id,
|
|
# changes={
|
|
# 'medication': str(self.object.medication),
|
|
# 'lot_number': self.object.lot_number,
|
|
# 'quantity': self.object.quantity_on_hand
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Inventory item created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class InventoryItemUpdateView(LoginRequiredMixin, UpdateView):
|
|
# """
|
|
# Update view for inventory item.
|
|
# """
|
|
# model = InventoryItem
|
|
# form_class = InventoryItemForm
|
|
# template_name = 'pharmacy/inventory_form.html'
|
|
#
|
|
# def get_queryset(self):
|
|
# return InventoryItem.objects.filter(medication__tenant=self.request.user.tenant)
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('pharmacy:inventory_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Create audit log
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=self.request.user.tenant,
|
|
# user=self.request.user,
|
|
# action='UPDATE',
|
|
# model_name='InventoryItem',
|
|
# object_id=self.object.id,
|
|
# changes={'quantity': self.object.quantity_on_hand}
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Inventory item updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # Dispense Record Views
|
|
# class DispenseRecordListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List view for dispense records with search and filtering.
|
|
# """
|
|
# model = DispenseRecord
|
|
# template_name = 'pharmacy/dispense_list.html'
|
|
# context_object_name = 'dispense_records'
|
|
# paginate_by = 20
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = DispenseRecord.objects.filter(
|
|
# prescription__patient__tenant=self.request.user.tenant
|
|
# ).select_related(
|
|
# 'prescription__patient', 'prescription__medication',
|
|
# 'inventory_item', 'dispensed_by'
|
|
# ).order_by('-dispensed_date')
|
|
#
|
|
# # Apply search filters
|
|
# form = DispenseSearchForm(data=self.request.GET, user=self.request.user)
|
|
# if form.is_valid():
|
|
# search = form.cleaned_data.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(prescription__patient__first_name__icontains=search) |
|
|
# Q(prescription__patient__last_name__icontains=search) |
|
|
# Q(prescription__medication__name__icontains=search)
|
|
# )
|
|
#
|
|
# patient = form.cleaned_data.get('patient')
|
|
# if patient:
|
|
# queryset = queryset.filter(prescription__patient=patient)
|
|
#
|
|
# medication = form.cleaned_data.get('medication')
|
|
# if medication:
|
|
# queryset = queryset.filter(prescription__medication=medication)
|
|
#
|
|
# dispensed_by = form.cleaned_data.get('dispensed_by')
|
|
# if dispensed_by:
|
|
# queryset = queryset.filter(dispensed_by=dispensed_by)
|
|
#
|
|
# date_from = form.cleaned_data.get('date_from')
|
|
# if date_from:
|
|
# queryset = queryset.filter(dispensed_date__date__gte=date_from)
|
|
#
|
|
# date_to = form.cleaned_data.get('date_to')
|
|
# if date_to:
|
|
# queryset = queryset.filter(dispensed_date__date__lte=date_to)
|
|
#
|
|
# return queryset
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context['search_form'] = DispenseSearchForm(
|
|
# data=self.request.GET,
|
|
# user=self.request.user
|
|
# )
|
|
# return context
|
|
#
|
|
#
|
|
# class DispenseRecordDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Detail view for dispense record.
|
|
# """
|
|
# model = DispenseRecord
|
|
# template_name = 'pharmacy/dispense_detail.html'
|
|
# context_object_name = 'dispense_record'
|
|
#
|
|
# def get_queryset(self):
|
|
# return DispenseRecord.objects.filter(
|
|
# prescription__patient__tenant=self.request.user.tenant
|
|
# ).select_related(
|
|
# 'prescription__patient', 'prescription__medication',
|
|
# 'inventory_item', 'dispensed_by'
|
|
# )
|
|
#
|
|
#
|
|
# class DispenseRecordCreateView(LoginRequiredMixin, CreateView):
|
|
# """
|
|
# Create view for dispense record.
|
|
# """
|
|
# model = DispenseRecord
|
|
# form_class = DispenseRecordForm
|
|
# template_name = 'pharmacy/dispense_form.html'
|
|
# success_url = reverse_lazy('pharmacy:dispense_list')
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Update inventory quantity
|
|
# inventory_item = self.object.inventory_item
|
|
# inventory_item.quantity_on_hand -= self.object.quantity_dispensed
|
|
# inventory_item.save()
|
|
#
|
|
# # Update prescription status if fully dispensed
|
|
# prescription = self.object.prescription
|
|
# total_dispensed = DispenseRecord.objects.filter(
|
|
# prescription=prescription
|
|
# ).aggregate(total=Sum('quantity_dispensed'))['total'] or 0
|
|
#
|
|
# if total_dispensed >= prescription.quantity:
|
|
# prescription.status = 'FILLED'
|
|
# prescription.save()
|
|
#
|
|
# # Create audit log
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=self.request.user.tenant,
|
|
# user=self.request.user,
|
|
# action='CREATE',
|
|
# model_name='DispenseRecord',
|
|
# object_id=self.object.id,
|
|
# changes={
|
|
# 'prescription': str(self.object.prescription),
|
|
# 'quantity': self.object.quantity_dispensed
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Medication dispensed successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # Medication Administration Views
|
|
# class MedicationAdministrationListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List view for medication administration records.
|
|
# """
|
|
# model = MedicationAdministration
|
|
# template_name = 'pharmacy/administration_list.html'
|
|
# context_object_name = 'administrations'
|
|
# paginate_by = 20
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = MedicationAdministration.objects.filter(
|
|
# patient__tenant=self.request.user.tenant
|
|
# ).select_related(
|
|
# 'patient', 'medication', 'administered_by'
|
|
# ).order_by('-administered_date', '-actual_time')
|
|
#
|
|
# # Apply search filters
|
|
# form = AdministrationSearchForm(data=self.request.GET, user=self.request.user)
|
|
# if form.is_valid():
|
|
# search = form.cleaned_data.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(patient__first_name__icontains=search) |
|
|
# Q(patient__last_name__icontains=search) |
|
|
# Q(medication__name__icontains=search)
|
|
# )
|
|
#
|
|
# patient = form.cleaned_data.get('patient')
|
|
# if patient:
|
|
# queryset = queryset.filter(patient=patient)
|
|
#
|
|
# medication = form.cleaned_data.get('medication')
|
|
# if medication:
|
|
# queryset = queryset.filter(medication=medication)
|
|
#
|
|
# administered_by = form.cleaned_data.get('administered_by')
|
|
# if administered_by:
|
|
# queryset = queryset.filter(administered_by=administered_by)
|
|
#
|
|
# status = form.cleaned_data.get('status')
|
|
# if status:
|
|
# queryset = queryset.filter(status=status)
|
|
#
|
|
# route = form.cleaned_data.get('route')
|
|
# if route:
|
|
# queryset = queryset.filter(route=route)
|
|
#
|
|
# date_from = form.cleaned_data.get('date_from')
|
|
# if date_from:
|
|
# queryset = queryset.filter(administered_date__gte=date_from)
|
|
#
|
|
# date_to = form.cleaned_data.get('date_to')
|
|
# if date_to:
|
|
# queryset = queryset.filter(administered_date__lte=date_to)
|
|
#
|
|
# return queryset
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context['search_form'] = AdministrationSearchForm(
|
|
# data=self.request.GET,
|
|
# user=self.request.user
|
|
# )
|
|
# return context
|
|
#
|
|
#
|
|
# class MedicationAdministrationCreateView(LoginRequiredMixin, CreateView):
|
|
# """
|
|
# Create view for medication administration.
|
|
# """
|
|
# model = MedicationAdministration
|
|
# form_class = MedicationAdministrationForm
|
|
# template_name = 'pharmacy/administration_form.html'
|
|
# success_url = reverse_lazy('pharmacy:administration_list')
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Create audit log
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=self.request.user.tenant,
|
|
# user=self.request.user,
|
|
# action='CREATE',
|
|
# model_name='MedicationAdministration',
|
|
# object_id=self.object.id,
|
|
# changes={
|
|
# 'patient': str(self.object.patient),
|
|
# 'medication': str(self.object.medication),
|
|
# 'status': self.object.status
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Medication administration recorded successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # Drug Interaction Views
|
|
# class DrugInteractionListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List view for drug interactions.
|
|
# """
|
|
# model = DrugInteraction
|
|
# template_name = 'pharmacy/interaction_list.html'
|
|
# context_object_name = 'interactions'
|
|
# paginate_by = 20
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = DrugInteraction.objects.filter(
|
|
# medication_1__tenant=self.request.user.tenant
|
|
# ).select_related('medication_1', 'medication_2').order_by('severity', 'medication_1__name')
|
|
#
|
|
# search = self.request.GET.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(medication_1__name__icontains=search) |
|
|
# Q(medication_2__name__icontains=search) |
|
|
# Q(description__icontains=search)
|
|
# )
|
|
#
|
|
# severity = self.request.GET.get('severity')
|
|
# if severity:
|
|
# queryset = queryset.filter(severity=severity)
|
|
#
|
|
# is_active = self.request.GET.get('is_active')
|
|
# if is_active == 'true':
|
|
# queryset = queryset.filter(is_active=True)
|
|
# elif is_active == 'false':
|
|
# queryset = queryset.filter(is_active=False)
|
|
#
|
|
# return queryset
|
|
#
|
|
#
|
|
|
|
class DrugInteractionCreateView(LoginRequiredMixin, CreateView):
|
|
"""
|
|
Create view for drug interaction.
|
|
"""
|
|
model = DrugInteraction
|
|
form_class = DrugInteractionForm
|
|
template_name = 'pharmacy/interaction_form.html'
|
|
success_url = reverse_lazy('pharmacy:interaction_list')
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
form.instance.created_by = self.request.user
|
|
response = super().form_valid(form)
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=self.request.user.tenant,
|
|
user=self.request.user,
|
|
action='CREATE',
|
|
model_name='DrugInteraction',
|
|
object_id=self.object.id,
|
|
changes={
|
|
'medication_1': str(self.object.medication_1),
|
|
'medication_2': str(self.object.medication_2),
|
|
'severity': self.object.severity
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'Drug interaction created successfully.')
|
|
return response
|
|
|
|
|
|
class DrugInteractionDetailView(LoginRequiredMixin, DetailView):
|
|
model = DrugInteraction
|
|
template_name = 'pharmacy/interactions/drug_interaction_detail.html'
|
|
context_object_name = 'interaction'
|
|
|
|
|
|
# HTMX Views
|
|
@login_required
|
|
def htmx_pharmacy_stats(request):
|
|
"""
|
|
HTMX view for real-time pharmacy statistics.
|
|
"""
|
|
user = request.user
|
|
today = timezone.now().date()
|
|
|
|
stats = {
|
|
'total_medications': Medication.objects.filter(
|
|
tenant=user.tenant, is_active=True
|
|
).count(),
|
|
'active_prescriptions': Prescription.objects.filter(
|
|
patient__tenant=user.tenant, status='ACTIVE'
|
|
).count(),
|
|
'pending_prescriptions': Prescription.objects.filter(
|
|
patient__tenant=user.tenant, status='PENDING'
|
|
).count(),
|
|
'low_stock_items': InventoryItem.objects.filter(
|
|
medication__tenant=user.tenant,
|
|
is_active=True,
|
|
quantity_on_hand__lte=F('reorder_point')
|
|
).count(),
|
|
'prescriptions_today': Prescription.objects.filter(
|
|
patient__tenant=user.tenant,
|
|
prescribed_date__date=today
|
|
).count(),
|
|
'dispensed_today': DispenseRecord.objects.filter(
|
|
prescription__patient__tenant=user.tenant,
|
|
dispensed_date__date=today
|
|
).count(),
|
|
}
|
|
|
|
return render(request, 'pharmacy/partials/pharmacy_stats.html', {'stats': stats})
|
|
|
|
|
|
@login_required
|
|
def htmx_prescription_search(request):
|
|
"""
|
|
HTMX view for prescription search.
|
|
"""
|
|
search = request.GET.get('search', '')
|
|
prescriptions = Prescription.objects.filter(
|
|
patient__tenant=request.user.tenant
|
|
).select_related('patient', 'medication', 'prescriber')
|
|
|
|
if search:
|
|
prescriptions = prescriptions.filter(
|
|
Q(patient__first_name__icontains=search) |
|
|
Q(patient__last_name__icontains=search) |
|
|
Q(medication__name__icontains=search)
|
|
)
|
|
|
|
prescriptions = prescriptions.order_by('-prescribed_date')[:10]
|
|
|
|
return render(request, 'pharmacy/partials/prescription_list.html', {
|
|
'prescriptions': prescriptions
|
|
})
|
|
|
|
|
|
# Action Views
|
|
@login_required
|
|
def fill_prescription(request, pk):
|
|
"""
|
|
Action view to fill a prescription.
|
|
"""
|
|
prescription = get_object_or_404(
|
|
Prescription,
|
|
pk=pk,
|
|
patient__tenant=request.user.tenant
|
|
)
|
|
|
|
if prescription.status != 'ACTIVE':
|
|
messages.error(request, 'Only active prescriptions can be filled.')
|
|
return redirect('pharmacy:prescription_detail', pk=pk)
|
|
|
|
# Check inventory availability
|
|
available_inventory = InventoryItem.objects.filter(
|
|
medication=prescription.medication,
|
|
is_active=True,
|
|
quantity_on_hand__gte=prescription.quantity
|
|
).first()
|
|
|
|
if not available_inventory:
|
|
messages.error(request, 'Insufficient inventory to fill this prescription.')
|
|
return redirect('pharmacy:prescription_detail', pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
# Create dispense record
|
|
dispense_record = DispenseRecord.objects.create(
|
|
prescription=prescription,
|
|
inventory_item=available_inventory,
|
|
quantity_dispensed=prescription.quantity,
|
|
dispensed_by=request.user,
|
|
dispensed_date=timezone.now(),
|
|
patient_counseled=True
|
|
)
|
|
|
|
# Update inventory
|
|
available_inventory.quantity_on_hand -= prescription.quantity
|
|
available_inventory.save()
|
|
|
|
# Update prescription status
|
|
prescription.status = 'FILLED'
|
|
prescription.save()
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=request.user.tenant,
|
|
user=request.user,
|
|
action='UPDATE',
|
|
model_name='Prescription',
|
|
object_id=prescription.id,
|
|
changes={'status': 'FILLED', 'filled_by': str(request.user)}
|
|
)
|
|
|
|
messages.success(request, 'Prescription filled successfully.')
|
|
return redirect('pharmacy:prescription_detail', pk=pk)
|
|
|
|
return render(request, 'pharmacy/fill_prescription.html', {
|
|
'prescription': prescription,
|
|
'available_inventory': available_inventory
|
|
})
|
|
|
|
|
|
@login_required
|
|
def cancel_prescription(request, pk):
|
|
"""
|
|
Action view to cancel a prescription.
|
|
"""
|
|
prescription = get_object_or_404(
|
|
Prescription,
|
|
pk=pk,
|
|
patient__tenant=request.user.tenant
|
|
)
|
|
|
|
if prescription.status in ['FILLED', 'CANCELLED']:
|
|
messages.error(request, 'Cannot cancel filled or already cancelled prescriptions.')
|
|
return redirect('pharmacy:prescription_detail', pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
prescription.status = 'CANCELLED'
|
|
prescription.save()
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=request.user.tenant,
|
|
user=request.user,
|
|
action='UPDATE',
|
|
model_name='Prescription',
|
|
object_id=prescription.id,
|
|
changes={'status': 'CANCELLED', 'cancelled_by': str(request.user)}
|
|
)
|
|
|
|
messages.success(request, 'Prescription cancelled successfully.')
|
|
return redirect('pharmacy:prescription_detail', pk=pk)
|
|
|
|
return render(request, 'pharmacy/cancel_prescription.html', {
|
|
'prescription': prescription
|
|
})
|
|
|
|
|
|
@login_required
|
|
def adjust_inventory(request, pk):
|
|
"""
|
|
Action view to adjust inventory quantity.
|
|
"""
|
|
inventory_item = get_object_or_404(
|
|
InventoryItem,
|
|
pk=pk,
|
|
medication__tenant=request.user.tenant
|
|
)
|
|
|
|
if request.method == 'POST':
|
|
adjustment = int(request.POST.get('adjustment', 0))
|
|
reason = request.POST.get('reason', '')
|
|
|
|
if adjustment == 0:
|
|
messages.error(request, 'Adjustment amount cannot be zero.')
|
|
return redirect('pharmacy:inventory_detail', pk=pk)
|
|
|
|
old_quantity = inventory_item.quantity_on_hand
|
|
inventory_item.quantity_on_hand += adjustment
|
|
|
|
if inventory_item.quantity_on_hand < 0:
|
|
messages.error(request, 'Adjustment would result in negative inventory.')
|
|
return redirect('pharmacy:inventory_detail', pk=pk)
|
|
|
|
inventory_item.save()
|
|
|
|
# Create audit log
|
|
AuditLogEntry.objects.create(
|
|
tenant=request.user.tenant,
|
|
user=request.user,
|
|
action='UPDATE',
|
|
model_name='InventoryItem',
|
|
object_id=inventory_item.id,
|
|
changes={
|
|
'old_quantity': old_quantity,
|
|
'new_quantity': inventory_item.quantity_on_hand,
|
|
'adjustment': adjustment,
|
|
'reason': reason
|
|
}
|
|
)
|
|
|
|
messages.success(request, f'Inventory adjusted by {adjustment} units.')
|
|
return redirect('pharmacy:inventory_detail', pk=pk)
|
|
|
|
return render(request, 'pharmacy/adjust_inventory.html', {
|
|
'inventory_item': inventory_item
|
|
})
|
|
#
|
|
#
|
|
# # Export Views
|
|
# @login_required
|
|
# def export_prescriptions_csv(request):
|
|
# """
|
|
# Export prescriptions to CSV.
|
|
# """
|
|
# response = HttpResponse(content_type='text/csv')
|
|
# response['Content-Disposition'] = 'attachment; filename="prescriptions.csv"'
|
|
#
|
|
# writer = csv.writer(response)
|
|
# writer.writerow([
|
|
# 'Patient', 'Medication', 'Prescriber', 'Quantity', 'Status',
|
|
# 'Prescribed Date', 'Priority'
|
|
# ])
|
|
#
|
|
# prescriptions = Prescription.objects.filter(
|
|
# patient__tenant=request.user.tenant
|
|
# ).select_related('patient', 'medication', 'prescriber')
|
|
#
|
|
# for prescription in prescriptions:
|
|
# writer.writerow([
|
|
# f"{prescription.patient.first_name} {prescription.patient.last_name}",
|
|
# prescription.medication.name,
|
|
# f"{prescription.prescriber.first_name} {prescription.prescriber.last_name}",
|
|
# prescription.quantity,
|
|
# prescription.get_status_display(),
|
|
# prescription.prescribed_date.strftime('%Y-%m-%d %H:%M'),
|
|
# prescription.get_priority_display()
|
|
# ])
|
|
#
|
|
# return response
|
|
#
|
|
#
|
|
# @login_required
|
|
# def export_inventory_csv(request):
|
|
# """
|
|
# Export inventory to CSV.
|
|
# """
|
|
# response = HttpResponse(content_type='text/csv')
|
|
# response['Content-Disposition'] = 'attachment; filename="inventory.csv"'
|
|
#
|
|
# writer = csv.writer(response)
|
|
# writer.writerow([
|
|
# 'Medication', 'Lot Number', 'Quantity', 'Unit Cost', 'Expiry Date',
|
|
# 'Location', 'Reorder Point', 'Status'
|
|
# ])
|
|
#
|
|
# inventory_items = InventoryItem.objects.filter(
|
|
# medication__tenant=request.user.tenant
|
|
# ).select_related('medication')
|
|
#
|
|
# for item in inventory_items:
|
|
# writer.writerow([
|
|
# item.medication.name,
|
|
# item.lot_number,
|
|
# item.quantity_on_hand,
|
|
# item.unit_cost,
|
|
# item.expiry_date.strftime('%Y-%m-%d') if item.expiry_date else '',
|
|
# item.location,
|
|
# item.reorder_point,
|
|
# 'Active' if item.is_active else 'Inactive'
|
|
# ])
|
|
#
|
|
# return response
|
|
#
|
|
def save_prescription_draft(request, pk):
|
|
prescription = get_object_or_404(Prescription, pk=pk)
|
|
prescription.status = "DRAFT"
|
|
prescription.save()
|
|
return redirect('pharmacy:prescription_detail', pk=pk)
|