""" 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) # Filter usage records by hospital based on selected context usage_records_qs = source.usage_records.select_related("content_type", "hospital", "user") if request.user.is_hospital_admin() and request.user.hospital: usage_records_qs = usage_records_qs.filter(hospital=request.user.hospital) elif request.user.is_px_admin() and hasattr(request, "tenant_hospital") and request.tenant_hospital: usage_records_qs = usage_records_qs.filter(hospital=request.tenant_hospital) usage_records = usage_records_qs.order_by("-created_at")[:20] # Get source users for this source - filter by hospital based on selected context source_users_qs = source.source_users.select_related("user") if request.user.is_hospital_admin() and request.user.hospital: source_users_qs = source_users_qs.filter(hospital=request.user.hospital) elif request.user.is_px_admin() and hasattr(request, "tenant_hospital") and request.tenant_hospital: source_users_qs = source_users_qs.filter(hospital=request.tenant_hospital) source_users = source_users_qs.order_by("-created_at") # Get available users (not already assigned to this source) - filter by hospital assigned_user_ids = source_users.values_list("user_id", flat=True) available_users_qs = User.objects.exclude(id__in=assigned_user_ids) if request.user.is_hospital_admin() and request.user.hospital: available_users_qs = available_users_qs.filter(hospital=request.user.hospital) elif request.user.is_px_admin() and hasattr(request, "tenant_hospital") and request.tenant_hospital: available_users_qs = available_users_qs.filter(hospital=request.tenant_hospital) available_users = available_users_qs.order_by("email") # Get usage stats - filtered by hospital based on selected context usage_stats_queryset = source.usage_records.all() if request.user.is_hospital_admin() and request.user.hospital: usage_stats_queryset = usage_stats_queryset.filter(hospital=request.user.hospital) elif request.user.is_px_admin() and hasattr(request, "tenant_hospital") and request.tenant_hospital: usage_stats_queryset = usage_stats_queryset.filter(hospital=request.tenant_hospital) # Calculate stats from filtered queryset from django.utils import timezone from datetime import timedelta cutoff = timezone.now() - timedelta(days=30) recent_usage = usage_stats_queryset.filter(created_at__gte=cutoff) from django.contrib.contenttypes.models import ContentType complaint_ct = ContentType.objects.get(app_label="complaints", model="complaint") inquiry_ct = ContentType.objects.get(app_label="complaints", model="inquiry") usage_stats = { "total": usage_stats_queryset.count(), "recent": recent_usage.count(), "complaints": recent_usage.filter(content_type=complaint_ct).count(), "inquiries": recent_usage.filter(content_type=inquiry_ct).count(), } source_complaints = source.complaints.select_related("hospital", "department").order_by("-created_at")[:50] source_inquiries = source.inquiries.select_related("hospital", "department").order_by("-created_at")[:50] context = { "source": source, "usage_records": usage_records, "source_users": source_users, "available_users": available_users, "usage_stats": usage_stats, "source_complaints": source_complaints, "source_inquiries": source_inquiries, "complaints_count": source.complaints.count(), "inquiries_count": source.inquiries.count(), } 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) - filter by hospital from apps.complaints.models import Complaint complaints_queryset = Complaint.objects.filter(source=source) if source_user.hospital: complaints_queryset = complaints_queryset.filter(hospital=source_user.hospital) complaints = complaints_queryset.select_related("patient", "hospital", "assigned_to").order_by("-created_at")[:5] # Get inquiries from this source (recent 5) - filter by hospital from apps.complaints.models import Inquiry inquiries_queryset = Inquiry.objects.filter(source=source) if source_user.hospital: inquiries_queryset = inquiries_queryset.filter(hospital=source_user.hospital) inquiries = inquiries_queryset.select_related("patient", "hospital", "assigned_to").order_by("-created_at")[:5] # Calculate statistics - filtered by hospital total_complaints = complaints_queryset.count() total_inquiries = inquiries_queryset.count() open_complaints = complaints_queryset.filter(status="open").count() open_inquiries = inquiries_queryset.filter(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!")) # Get hospital from request (for PX Admins with tenant_hospital) or user's hospital hospital = None if request.user.is_px_admin() and hasattr(request, "tenant_hospital") and request.tenant_hospital: hospital = request.tenant_hospital elif request.user.hospital: hospital = request.user.hospital elif user.hospital: hospital = user.hospital # Create source user source_user = SourceUser.objects.create( user=user, source=source, hospital=hospital, 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 - filter by hospital for data isolation from apps.complaints.models import Complaint, Inquiry from django.db.models import Q complaints_queryset = Complaint.objects.filter(source=source) # Apply hospital filter for data isolation if source_user.hospital: complaints_queryset = complaints_queryset.filter(hospital=source_user.hospital) complaints_queryset = complaints_queryset.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) # Calculate totals filtered by hospital total_complaints_queryset = Complaint.objects.filter(source=source) total_inquiries_queryset = Inquiry.objects.filter(source=source) if source_user.hospital: total_complaints_queryset = total_complaints_queryset.filter(hospital=source_user.hospital) total_inquiries_queryset = total_inquiries_queryset.filter(hospital=source_user.hospital) 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": total_complaints_queryset.count(), "total_inquiries": total_inquiries_queryset.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 - filter by hospital for data isolation from apps.complaints.models import Inquiry, Complaint from django.db.models import Q inquiries_queryset = Inquiry.objects.filter(source=source) # Apply hospital filter for data isolation if source_user.hospital: inquiries_queryset = inquiries_queryset.filter(hospital=source_user.hospital) inquiries_queryset = inquiries_queryset.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) # Calculate totals filtered by hospital total_complaints_queryset = Complaint.objects.filter(source=source) total_inquiries_queryset = Inquiry.objects.filter(source=source) if source_user.hospital: total_complaints_queryset = total_complaints_queryset.filter(hospital=source_user.hospital) total_inquiries_queryset = total_inquiries_queryset.filter(hospital=source_user.hospital) 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": total_complaints_queryset.count(), "total_inquiries": total_inquiries_queryset.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 from source user's context if not form.data.get("hospital"): if source_user.hospital: form.data = form.data.copy() form.data["hospital"] = str(source_user.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 from source user's context if source_user.hospital: complaint.hospital = source_user.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 from source user's context if source_user.hospital: form.initial["hospital"] = source_user.hospital.id # Pre-populate location (get first location from user's hospital if available) if source_user.hospital: from apps.organizations.models import Location first_location = Location.objects.filter(hospital=source_user.hospital).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 source user's context if source_user.hospital: inquiry.hospital = source_user.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)