""" PX Sources UI views - HTML template rendering """ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.db import models from django.http import JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.utils.translation import gettext_lazy as _ from .models import PXSource, SourceUser from .decorators import source_user_required, block_source_user from apps.accounts.models import User from apps.complaints.models import Complaint, Inquiry def check_source_permission(user): """Check if user has permission to manage sources""" return user.is_px_admin() or user.is_hospital_admin() @login_required @block_source_user def source_list(request): """ List all PX sources """ sources = PXSource.objects.all() # Filter by active status is_active = request.GET.get('is_active') if is_active: sources = sources.filter(is_active=is_active == 'true') # Search search = request.GET.get('search') if search: sources = sources.filter( models.Q(name_en__icontains=search) | models.Q(name_ar__icontains=search) | models.Q(description__icontains=search) | models.Q(code__icontains=search) ) sources = sources.order_by('name_en') context = { 'sources': sources, 'is_active': is_active, 'search': search, } return render(request, 'px_sources/source_list.html', context) @login_required @block_source_user def source_detail(request, pk): """ View source details """ source = get_object_or_404(PXSource, pk=pk) usage_records = source.usage_records.select_related( 'content_type', 'hospital', 'user' ).order_by('-created_at')[:20] # Get source users for this source source_users = source.source_users.select_related('user').order_by('-created_at') # Get available users (not already assigned to this source) assigned_user_ids = source_users.values_list('user_id', flat=True) available_users = User.objects.exclude(id__in=assigned_user_ids).order_by('email') # Get usage stats usage_stats = source.get_usage_stats(days=30) context = { 'source': source, 'usage_records': usage_records, 'source_users': source_users, 'available_users': available_users, 'usage_stats': usage_stats, } return render(request, 'px_sources/source_detail.html', context) @login_required @block_source_user def source_create(request): """ Create a new PX source """ if not check_source_permission(request.user): messages.error(request, _("You don't have permission to create sources.")) return redirect('px_sources:source_list') if request.method == 'POST': try: source = PXSource( code=request.POST.get('code', ''), name_en=request.POST.get('name_en'), name_ar=request.POST.get('name_ar', ''), description=request.POST.get('description', ''), source_type=request.POST.get('source_type', 'internal'), contact_email=request.POST.get('contact_email', ''), contact_phone=request.POST.get('contact_phone', ''), is_active=request.POST.get('is_active') == 'on', ) source.save() messages.success(request, _("Source created successfully!")) return redirect('px_sources:source_detail', pk=source.pk) except Exception as e: messages.error(request, _("Error creating source: {}").format(str(e))) context = { 'source_types': PXSource.SOURCE_TYPE_CHOICES, } return render(request, 'px_sources/source_form.html', context) @login_required @block_source_user def source_edit(request, pk): """ Edit an existing PX source """ if not check_source_permission(request.user): messages.error(request, _("You don't have permission to edit sources.")) return redirect('px_sources:source_detail', pk=pk) source = get_object_or_404(PXSource, pk=pk) if request.method == 'POST': try: source.code = request.POST.get('code', source.code) source.name_en = request.POST.get('name_en') source.name_ar = request.POST.get('name_ar', '') source.description = request.POST.get('description', '') source.source_type = request.POST.get('source_type', 'internal') source.contact_email = request.POST.get('contact_email', '') source.contact_phone = request.POST.get('contact_phone', '') source.is_active = request.POST.get('is_active') == 'on' source.save() messages.success(request, _("Source updated successfully!")) return redirect('px_sources:source_detail', pk=source.pk) except Exception as e: messages.error(request, _("Error updating source: {}").format(str(e))) context = { 'source': source, 'source_types': PXSource.SOURCE_TYPE_CHOICES, } return render(request, 'px_sources/source_form.html', context) @login_required @block_source_user def source_delete(request, pk): """ Delete a PX source """ if not request.user.is_px_admin(): messages.error(request, _("You don't have permission to delete sources.")) return redirect('px_sources:source_detail', pk=pk) source = get_object_or_404(PXSource, pk=pk) if request.method == 'POST': source_name = source.name_en source.delete() messages.success(request, _("Source '{}' deleted successfully!").format(source_name)) return redirect('px_sources:source_list') context = { 'source': source, } return render(request, 'px_sources/source_confirm_delete.html', context) @login_required @block_source_user def source_toggle_status(request, pk): """ Toggle source active status (supports both AJAX and regular form submission) """ if not (request.user.is_px_admin() or request.user.is_hospital_admin()): if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({'error': 'Permission denied'}, status=403) messages.error(request, _("You don't have permission to toggle source status.")) return redirect('px_sources:source_list') if request.method != 'POST': if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({'error': 'Method not allowed'}, status=405) messages.error(request, _("Invalid request method.")) return redirect('px_sources:source_list') source = get_object_or_404(PXSource, pk=pk) source.is_active = not source.is_active source.save() status_text = _('activated') if source.is_active else _('deactivated') success_message = _("Source '{}' {} successfully.").format(source.name_en, status_text) # Handle AJAX request if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'is_active': source.is_active, 'message': str(success_message) }) # Handle regular form submission messages.success(request, success_message) return redirect('px_sources:source_list') @login_required @block_source_user def ajax_search_sources(request): """ AJAX endpoint for searching sources """ term = request.GET.get('term', '') queryset = PXSource.objects.filter(is_active=True) if term: queryset = queryset.filter( models.Q(name_en__icontains=term) | models.Q(name_ar__icontains=term) | models.Q(description__icontains=term) ) sources = queryset.order_by('name_en')[:20] results = [ { 'id': str(source.id), 'text': source.name_en, 'name_en': source.name_en, 'name_ar': source.name_ar, } for source in sources ] return JsonResponse({'results': results}) @login_required @source_user_required def source_user_dashboard(request): """ Dashboard for source users. Shows: - User's assigned source - Statistics (complaints, inquiries from their source) - Create buttons for complaints/inquiries - Tables of recent complaints/inquiries from their source """ # Get source user profile source_user = SourceUser.get_active_source_user(request.user) if not source_user: messages.error( request, _("You are not assigned as a source user. Please contact your administrator.") ) return redirect('/') # Get source source = source_user.source # Get complaints from this source (recent 5) from apps.complaints.models import Complaint complaints = Complaint.objects.filter(source=source).select_related( 'patient', 'hospital', 'assigned_to' ).order_by('-created_at')[:5] # Get inquiries from this source (recent 5) from apps.complaints.models import Inquiry inquiries = Inquiry.objects.filter(source=source).select_related( 'patient', 'hospital', 'assigned_to' ).order_by('-created_at')[:5] # Calculate statistics total_complaints = Complaint.objects.filter(source=source).count() total_inquiries = Inquiry.objects.filter(source=source).count() open_complaints = Complaint.objects.filter(source=source, status='open').count() open_inquiries = Inquiry.objects.filter(source=source, status='open').count() context = { 'source_user': source_user, 'source': source, 'complaints': complaints, 'inquiries': inquiries, 'total_complaints': total_complaints, 'total_inquiries': total_inquiries, 'open_complaints': open_complaints, 'open_inquiries': open_inquiries, 'can_create_complaints': source_user.can_create_complaints, 'can_create_inquiries': source_user.can_create_inquiries, } return render(request, 'px_sources/source_user_dashboard.html', context) @login_required @source_user_required def ajax_source_choices(request): """ AJAX endpoint for getting source choices for dropdowns """ queryset = PXSource.get_active_sources() choices = [ { 'id': str(source.id), 'name_en': source.name_en, 'name_ar': source.name_ar, } for source in queryset ] return JsonResponse({'choices': choices}) @login_required @block_source_user def source_user_create(request, pk): """ Create a new source user for a specific PX source. Only PX admins can create source users. Allows selecting an existing user or creating a new user. """ # if not request.user.is_px_admin(): # messages.error(request, _("You don't have permission to create source users.")) # return redirect('px_sources:source_detail', pk=pk) source = get_object_or_404(PXSource, pk=pk) if request.method == 'POST': creation_mode = request.POST.get('creation_mode', 'existing') # 'existing' or 'new' try: if creation_mode == 'existing': # Select from existing users user_id = request.POST.get('user') if not user_id: messages.error(request, _("Please select a user.")) return redirect('px_sources:source_user_create', pk=pk) user = get_object_or_404(User, pk=user_id) # Check if user already has a source user profile if SourceUser.objects.filter(user=user).exists(): messages.error(request, _("User already has a source profile. A user can only manage one source.")) return redirect('px_sources:source_detail', pk=pk) else: # creation_mode == 'new' # Create a new user email = request.POST.get('new_email', '').strip().lower() first_name = request.POST.get('new_first_name', '').strip() last_name = request.POST.get('new_last_name', '').strip() password = request.POST.get('new_password', '') confirm_password = request.POST.get('new_password_confirm', '') # Validation errors = [] if not email: errors.append(_("Email is required.")) elif User.objects.filter(email=email).exists(): errors.append(_("A user with this email already exists.")) if not first_name: errors.append(_("First name is required.")) if not last_name: errors.append(_("Last name is required.")) if not password: errors.append(_("Password is required.")) elif len(password) < 8: errors.append(_("Password must be at least 8 characters.")) if password != confirm_password: errors.append(_("Passwords do not match.")) if errors: for error in errors: messages.error(request, error) return redirect('px_sources:source_user_create', pk=pk) # Create the user user = User.objects.create_user( email=email, password=password, first_name=first_name, last_name=last_name, phone=request.POST.get('new_phone', '').strip(), employee_id=request.POST.get('new_employee_id', '').strip(), ) # Assign to PX Admin group by default from django.contrib.auth.models import Group try: px_admin_group = Group.objects.get(name='PX Admin') user.groups.add(px_admin_group) except Group.DoesNotExist: pass # Group doesn't exist yet messages.success(request, _("New user created successfully!")) # Create source user source_user = SourceUser.objects.create( user=user, source=source, is_active=request.POST.get('is_active') == 'on', can_create_complaints=request.POST.get('can_create_complaints') == 'on', can_create_inquiries=request.POST.get('can_create_inquiries') == 'on', ) messages.success(request, _("Source user created successfully!")) return redirect('px_sources:source_detail', pk=pk) except Exception as e: messages.error(request, _("Error creating source user: {}").format(str(e))) context = { 'source': source, 'available_users': User.objects.exclude( id__in=source.source_users.values_list('user_id', flat=True) ).order_by('email'), 'creation_mode': 'new', # Default to new user creation } return render(request, 'px_sources/source_user_form.html', context) @login_required @block_source_user def source_user_edit(request, pk, user_pk): """ Edit an existing source user. Only PX admins can edit source users. """ if not request.user.is_px_admin(): messages.error(request, _("You don't have permission to edit source users.")) return redirect('px_sources:source_detail', pk=pk) source = get_object_or_404(PXSource, pk=pk) source_user = get_object_or_404(SourceUser, pk=user_pk, source=source) if request.method == 'POST': try: source_user.is_active = request.POST.get('is_active') == 'on' source_user.can_create_complaints = request.POST.get('can_create_complaints') == 'on' source_user.can_create_inquiries = request.POST.get('can_create_inquiries') == 'on' source_user.save() messages.success(request, _("Source user updated successfully!")) return redirect('px_sources:source_detail', pk=pk) except Exception as e: messages.error(request, _("Error updating source user: {}").format(str(e))) context = { 'source': source, 'source_user': source_user, } return render(request, 'px_sources/source_user_form.html', context) @login_required @block_source_user def source_user_delete(request, pk, user_pk): """ Delete a source user. Only PX admins can delete source users. """ if not request.user.is_px_admin(): messages.error(request, _("You don't have permission to delete source users.")) return redirect('px_sources:source_detail', pk=pk) source = get_object_or_404(PXSource, pk=pk) source_user = get_object_or_404(SourceUser, pk=user_pk, source=source) if request.method == 'POST': user_name = source_user.user.get_full_name() or source_user.user.email source_user.delete() messages.success(request, _("Source user '{}' deleted successfully!").format(user_name)) return redirect('px_sources:source_detail', pk=pk) context = { 'source': source, 'source_user': source_user, } return render(request, 'px_sources/source_user_confirm_delete.html', context) @login_required @block_source_user def source_user_toggle_status(request, pk, user_pk): """ Toggle source user active status (AJAX). Only PX admins can toggle status. """ if not request.user.is_px_admin(): return JsonResponse({'error': 'Permission denied'}, status=403) if request.method != 'POST': return JsonResponse({'error': 'Method not allowed'}, status=405) source = get_object_or_404(PXSource, pk=pk) source_user = get_object_or_404(SourceUser, pk=user_pk, source=source) source_user.is_active = not source_user.is_active source_user.save() return JsonResponse({ 'success': True, 'is_active': source_user.is_active, 'message': 'Source user {} successfully'.format( 'activated' if source_user.is_active else 'deactivated' ) }) @login_required @source_user_required def source_user_complaint_list(request): """ List complaints for the current Source User. Shows only complaints from their assigned source. """ # Get source user profile source_user = SourceUser.get_active_source_user(request.user) if not source_user: messages.error( request, _("You are not assigned as a source user. Please contact your administrator.") ) return redirect('/') source = source_user.source # Get complaints from this source from apps.complaints.models import Complaint, Inquiry from django.db.models import Q complaints_queryset = Complaint.objects.filter(source=source).select_related( 'patient', 'hospital', 'assigned_to', 'created_by' ) # Apply filters status_filter = request.GET.get('status') if status_filter: complaints_queryset = complaints_queryset.filter(status=status_filter) priority_filter = request.GET.get('priority') if priority_filter: complaints_queryset = complaints_queryset.filter(priority=priority_filter) category_filter = request.GET.get('category') if category_filter: complaints_queryset = complaints_queryset.filter(category=category_filter) # Search search = request.GET.get('search') if search: complaints_queryset = complaints_queryset.filter( Q(title__icontains=search) | Q(description__icontains=search) | Q(patient_name__icontains=search) ) # Order and paginate complaints_queryset = complaints_queryset.order_by('-created_at') from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger paginator = Paginator(complaints_queryset, 20) # 20 per page page = request.GET.get('page') try: complaints = paginator.page(page) except PageNotAnInteger: complaints = paginator.page(1) except EmptyPage: complaints = paginator.page(paginator.num_pages) context = { 'complaints': complaints, 'source_user': source_user, 'source': source, 'status_filter': status_filter, 'priority_filter': priority_filter, 'category_filter': category_filter, 'search': search, 'complaints_count': complaints_queryset.count(), 'total_complaints': Complaint.objects.filter(source=source).count(), 'total_inquiries': Inquiry.objects.filter(source=source).count(), } return render(request, 'px_sources/source_user_complaint_list.html', context) @login_required @source_user_required def source_user_inquiry_list(request): """ List inquiries for the current Source User. Shows only inquiries from their assigned source. """ # Get source user profile source_user = SourceUser.get_active_source_user(request.user) if not source_user: messages.error( request, _("You are not assigned as a source user. Please contact your administrator.") ) return redirect('/') source = source_user.source # Get inquiries from this source from apps.complaints.models import Inquiry, Complaint from django.db.models import Q inquiries_queryset = Inquiry.objects.filter(source=source).select_related( 'patient', 'hospital', 'assigned_to', 'created_by' ) # Apply filters status_filter = request.GET.get('status') if status_filter: inquiries_queryset = inquiries_queryset.filter(status=status_filter) category_filter = request.GET.get('category') if category_filter: inquiries_queryset = inquiries_queryset.filter(category=category_filter) # Search search = request.GET.get('search') if search: inquiries_queryset = inquiries_queryset.filter( Q(subject__icontains=search) | Q(message__icontains=search) | Q(contact_name__icontains=search) ) # Order and paginate inquiries_queryset = inquiries_queryset.order_by('-created_at') from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger paginator = Paginator(inquiries_queryset, 20) # 20 per page page = request.GET.get('page') try: inquiries = paginator.page(page) except PageNotAnInteger: inquiries = paginator.page(1) except EmptyPage: inquiries = paginator.page(paginator.num_pages) context = { 'inquiries': inquiries, 'source_user': source_user, 'source': source, 'status_filter': status_filter, 'category_filter': category_filter, 'search': search, 'inquiries_count': inquiries_queryset.count(), 'total_complaints': Complaint.objects.filter(source=source).count(), 'total_inquiries': Inquiry.objects.filter(source=source).count(), } return render(request, 'px_sources/source_user_inquiry_list.html', context) @login_required @source_user_required def source_user_create_complaint(request): """ Create a complaint for source users. Simplified form that automatically: - Assigns the user's source - Sets the hospital from the source user's context - Hides admin-only fields """ from apps.complaints.forms import PublicComplaintForm from apps.complaints.models import Complaint from apps.complaints.tasks import notify_admins_new_complaint from apps.core.services import AuditService import uuid from datetime import datetime source_user = SourceUser.get_active_source_user(request.user) if not source_user or not source_user.can_create_complaints: messages.error(request, _("You don't have permission to create complaints.")) return redirect('px_sources:source_user_dashboard') source = source_user.source if request.method == 'POST': form = PublicComplaintForm(request.POST, request.FILES) # Add Tailwind CSS classes to form fields for field_name, field in form.fields.items(): if field.widget.__class__.__name__ == 'TextInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) elif field.widget.__class__.__name__ == 'Textarea': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition', 'rows': '5'}) elif field.widget.__class__.__name__ == 'Select': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition bg-white'}) elif field.widget.__class__.__name__ == 'DateInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition', 'type': 'date'}) elif field.widget.__class__.__name__ == 'EmailInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) elif field.widget.__class__.__name__ == 'TelInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) # Auto-populate hospital and location if not provided if not form.data.get('hospital'): from apps.organizations.models import Hospital first_hospital = Hospital.objects.filter(status='active').first() if first_hospital: form.data = form.data.copy() form.data['hospital'] = str(first_hospital.id) if not form.data.get('location'): from apps.organizations.models import Location first_location = Location.objects.first() if first_location: form.data = form.data.copy() form.data['location'] = str(first_location.id) if form.is_valid(): try: # Create complaint complaint = form.save(commit=False) # Set source automatically complaint.source = source # Set hospital (use source user's context) from apps.organizations.models import Hospital # Get the first active hospital or use default logic hospital = Hospital.objects.filter(status='active').first() if hospital: complaint.hospital = hospital # Map complaint_details to description (form field vs model field) complaint.description = form.cleaned_data.get('complaint_details', '') # Generate reference number today = datetime.now().strftime("%Y%m%d") random_suffix = str(uuid.uuid4().int)[:6] complaint.reference_number = f"CMP-{today}-{random_suffix}" # Set created by complaint.created_by = request.user complaint.save() # Create initial update from apps.complaints.models import ComplaintUpdate ComplaintUpdate.objects.create( complaint=complaint, update_type="note", message=f"Complaint submitted by {source.name_en} source user.", created_by=request.user, ) # Trigger AI analysis try: from apps.complaints.tasks import analyze_complaint_with_ai analyze_complaint_with_ai.delay(str(complaint.id)) except: pass # AI analysis is optional # Notify admins try: notify_admins_new_complaint.delay(str(complaint.id)) except: pass # Notification is optional # Log audit try: AuditService.log_event( event_type="complaint_created_by_source_user", description=f"Complaint created by source user: {complaint.reference_number}", user=request.user, content_object=complaint, metadata={ 'source': source.name_en, 'source_user_id': str(source_user.id), }, ) except: pass # Audit logging is optional messages.success( request, f"Complaint submitted successfully! Reference: {complaint.reference_number}" ) return redirect('px_sources:source_user_complaint_list') except Exception as e: messages.error(request, f"Error creating complaint: {str(e)}") else: messages.error(request, "Please correct the errors below.") else: form = PublicComplaintForm() # Pre-populate hospital (get first active hospital) from apps.organizations.models import Hospital, Location first_hospital = Hospital.objects.filter(status='active').first() if first_hospital: form.initial['hospital'] = first_hospital.id # Pre-populate location (get first location) first_location = Location.objects.first() if first_location: form.initial['location'] = first_location.id # Add Tailwind CSS classes to form fields (for GET request too) for field_name, field in form.fields.items(): if field.widget.__class__.__name__ == 'TextInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) elif field.widget.__class__.__name__ == 'Textarea': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition', 'rows': '5'}) elif field.widget.__class__.__name__ == 'Select': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition bg-white'}) elif field.widget.__class__.__name__ == 'DateInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition', 'type': 'date'}) elif field.widget.__class__.__name__ == 'EmailInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) elif field.widget.__class__.__name__ == 'TelInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) context = { 'form': form, 'source': source, 'source_user': source_user, } return render(request, 'px_sources/source_user_create_complaint.html', context) @login_required @source_user_required def source_user_create_inquiry(request): """ Create an inquiry for source users. Simplified form that automatically: - Assigns the user's source - Sets the hospital from the source user's context - Hides admin-only fields """ from apps.complaints.forms import PublicInquiryForm from apps.complaints.models import Inquiry import uuid from datetime import datetime source_user = SourceUser.get_active_source_user(request.user) if not source_user or not source_user.can_create_inquiries: messages.error(request, _("You don't have permission to create inquiries.")) return redirect('px_sources:source_user_dashboard') source = source_user.source if request.method == 'POST': form = PublicInquiryForm(request.POST) # Add Tailwind CSS classes to form fields for field_name, field in form.fields.items(): if field.widget.__class__.__name__ == 'TextInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) elif field.widget.__class__.__name__ == 'Textarea': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition', 'rows': '5'}) elif field.widget.__class__.__name__ == 'EmailInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) elif field.widget.__class__.__name__ == 'TelInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) if form.is_valid(): try: # Create inquiry inquiry = form.save(commit=False) # Set source automatically inquiry.source = source # Set hospital from apps.organizations.models import Hospital hospital = Hospital.objects.filter(status='active').first() if hospital: inquiry.hospital = hospital # Generate reference number today = datetime.now().strftime("%Y%m%d") random_suffix = str(uuid.uuid4().int)[:6] inquiry.reference_number = f"INQ-{today}-{random_suffix}" # Set created by inquiry.created_by = request.user inquiry.save() # Log audit from apps.core.services import AuditService try: AuditService.log_event( event_type="inquiry_created_by_source_user", description=f"Inquiry created by source user: {inquiry.reference_number}", user=request.user, content_object=inquiry, metadata={ 'source': source.name_en, 'source_user_id': str(source_user.id), }, ) except: pass # Audit logging is optional messages.success( request, f"Inquiry submitted successfully! Reference: {inquiry.reference_number}" ) return redirect('px_sources:source_user_inquiry_list') except Exception as e: messages.error(request, f"Error creating inquiry: {str(e)}") else: messages.error(request, "Please correct the errors below.") else: form = PublicInquiryForm() # Add Tailwind CSS classes to form fields (for GET request too) for field_name, field in form.fields.items(): if field.widget.__class__.__name__ == 'TextInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) elif field.widget.__class__.__name__ == 'Textarea': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition', 'rows': '5'}) elif field.widget.__class__.__name__ == 'EmailInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) elif field.widget.__class__.__name__ == 'TelInput': field.widget.attrs.update({'class': 'w-full px-4 py-3 border-2 border-blue-100 rounded-xl text-navy focus:ring-2 focus:ring-blue focus:border-transparent transition'}) context = { 'form': form, 'source': source, 'source_user': source_user, } return render(request, 'px_sources/source_user_create_inquiry.html', context)