# Generated by Django 6.0.1 on 2026-05-11 20:32 import apps.core.encryption import django.db.models.deletion import uuid from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ('organizations', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='ComplaintAdverseAction', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('action_type', models.CharField(choices=[('refused_service', 'Refused Service'), ('delayed_treatment', 'Delayed Treatment'), ('verbal_abuse', 'Verbal Abuse / Hostility'), ('increased_wait', 'Increased Wait Time'), ('unnecessary_procedure', 'Unnecessary Procedure'), ('dismissed_from_care', 'Dismissed from Care'), ('poor_treatment', 'Poor Treatment Quality'), ('discrimination', 'Discrimination'), ('retaliation', 'Retaliation'), ('other', 'Other')], default='other', help_text='Type of adverse action', max_length=30)), ('severity', models.CharField(choices=[('low', 'Low - Minor inconvenience'), ('medium', 'Medium - Moderate impact'), ('high', 'High - Significant harm'), ('critical', 'Critical - Severe harm / Life-threatening')], default='medium', help_text='Severity level of the adverse action', max_length=10)), ('description', models.TextField(help_text='Detailed description of what happened to the patient')), ('incident_date', models.DateTimeField(help_text='Date and time when the adverse action occurred')), ('location', models.CharField(blank=True, help_text='Location where the incident occurred (e.g., Emergency Room, Clinic B)', max_length=200)), ('patient_impact', models.TextField(blank=True, help_text='Description of the impact on the patient (physical, emotional, financial)')), ('status', models.CharField(choices=[('reported', 'Reported - Awaiting Review'), ('under_investigation', 'Under Investigation'), ('verified', 'Verified'), ('unfounded', 'Unfounded'), ('resolved', 'Resolved')], default='reported', help_text='Current status of the adverse action report', max_length=30)), ('investigation_notes', models.TextField(blank=True, help_text='Notes from the investigation')), ('investigated_at', models.DateTimeField(blank=True, help_text='When the investigation was completed', null=True)), ('resolution', models.TextField(blank=True, help_text='How the adverse action was resolved')), ('resolved_at', models.DateTimeField(blank=True, help_text='When the adverse action was resolved', null=True)), ('is_escalated', models.BooleanField(default=False, help_text='Whether this adverse action has been escalated to management')), ('escalated_at', models.DateTimeField(blank=True, help_text='When the adverse action was escalated', null=True)), ], options={ 'verbose_name': 'Complaint Adverse Action', 'verbose_name_plural': 'Complaint Adverse Actions', 'ordering': ['-incident_date', '-created_at'], }, ), migrations.CreateModel( name='ComplaintAdverseActionAttachment', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('file', models.FileField(help_text='Attachment file (image, document, audio recording, etc.)', upload_to='complaints/adverse_actions/%Y/%m/%d/')), ('filename', models.CharField(max_length=255)), ('file_type', models.CharField(blank=True, max_length=100)), ('file_size', models.IntegerField(help_text='File size in bytes')), ('description', models.TextField(blank=True, help_text='Description of what this attachment shows')), ], options={ 'verbose_name': 'Adverse Action Attachment', 'verbose_name_plural': 'Adverse Action Attachments', 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='ComplaintAttachment', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('file', models.FileField(upload_to='complaints/%Y/%m/%d/')), ('filename', models.CharField(max_length=500)), ('file_type', models.CharField(blank=True, max_length=100)), ('file_size', models.IntegerField(help_text='File size in bytes')), ('description', models.TextField(blank=True)), ], options={ 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='ComplaintCategory', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('code', models.CharField(help_text='Unique code for this category', max_length=100)), ('name_en', models.CharField(max_length=200)), ('name_ar', models.CharField(blank=True, max_length=200)), ('description_en', models.TextField(blank=True)), ('description_ar', models.TextField(blank=True)), ('level', models.IntegerField(choices=[(1, 'Domain'), (2, 'Category'), (3, 'Subcategory'), (4, 'Classification')], help_text='Hierarchy level (1=Domain, 2=Category, 3=Subcategory, 4=Classification)')), ('domain_type', models.CharField(blank=True, choices=[('CLINICAL', 'Clinical'), ('MANAGEMENT', 'Management'), ('RELATIONSHIPS', 'Relationships')], help_text='Domain type for top-level categories', max_length=20)), ('order', models.IntegerField(default=0, help_text='Display order')), ('is_active', models.BooleanField(default=True)), ], options={ 'verbose_name_plural': 'Complaint Categories', 'ordering': ['order', 'name_en'], }, ), migrations.CreateModel( name='ComplaintCommunication', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('communication_type', models.CharField(choices=[('phone_call', 'Phone Call'), ('email', 'Email'), ('sms', 'SMS'), ('meeting', 'Meeting'), ('letter', 'Letter'), ('other', 'Other')], help_text='Type of communication', max_length=20)), ('direction', models.CharField(choices=[('inbound', 'Inbound'), ('outbound', 'Outbound')], help_text='Direction of communication', max_length=20)), ('contacted_person', models.CharField(help_text='Name of person contacted', max_length=200)), ('contacted_role', models.CharField(blank=True, help_text='Role/relation (e.g., Complainant, Patient, Staff)', max_length=100)), ('contacted_phone', models.CharField(blank=True, help_text='Phone number', max_length=20)), ('contacted_email', models.EmailField(blank=True, help_text='Email address', max_length=254)), ('subject', models.CharField(blank=True, help_text='Subject/summary of communication', max_length=500)), ('notes', models.TextField(help_text='Details of what was discussed')), ('requires_followup', models.BooleanField(default=False, help_text='Whether this communication requires follow-up')), ('followup_date', models.DateField(blank=True, help_text='Date when follow-up is needed', null=True)), ('followup_notes', models.TextField(blank=True, help_text='Notes from follow-up')), ('attachment', models.FileField(blank=True, help_text='Attached document (email export, letter, etc.)', null=True, upload_to='complaints/communications/%Y/%m/%d/')), ], options={ 'verbose_name': 'Complaint Communication', 'verbose_name_plural': 'Complaint Communications', 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='ComplaintExplanation', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('explanation', models.TextField(help_text="Staff's explanation about the complaint")), ('token', models.CharField(help_text='Unique access token for explanation submission', max_length=64, unique=True)), ('is_used', models.BooleanField(db_index=True, default=False, help_text='Token expiry tracking - becomes True after submission')), ('submitted_via', models.CharField(choices=[('email_link', 'Email Link'), ('direct', 'Direct Entry')], default='email_link', help_text='How the explanation was submitted', max_length=20)), ('email_sent_at', models.DateTimeField(blank=True, help_text='When the explanation request email was sent', null=True)), ('responded_at', models.DateTimeField(blank=True, help_text='When the explanation was submitted', null=True)), ('request_message', models.TextField(blank=True, help_text='Optional message sent with the explanation request')), ('sla_due_at', models.DateTimeField(blank=True, db_index=True, help_text='SLA deadline for staff to submit explanation', null=True)), ('is_overdue', models.BooleanField(db_index=True, default=False, help_text='Explanation request is overdue')), ('reminder_sent_at', models.DateTimeField(blank=True, help_text='First reminder sent to staff about overdue explanation', null=True)), ('second_reminder_sent_at', models.DateTimeField(blank=True, help_text='Second reminder sent to staff about overdue explanation', null=True)), ('escalated_at', models.DateTimeField(blank=True, help_text='When explanation was escalated to manager', null=True)), ('acceptance_status', models.CharField(choices=[('pending', 'Pending Review'), ('acceptable', 'Acceptable'), ('not_acceptable', 'Not Acceptable')], default='pending', help_text='Review status of the explanation', max_length=20)), ('accepted_at', models.DateTimeField(blank=True, help_text='When the explanation was reviewed', null=True)), ('acceptance_notes', models.TextField(blank=True, help_text='Notes about the acceptance decision')), ], options={ 'verbose_name': 'Complaint Explanation', 'verbose_name_plural': 'Complaint Explanations', 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='ComplaintInvolvedDepartment', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('role', models.CharField(choices=[('primary', 'Primary Department'), ('secondary', 'Secondary/Supporting'), ('coordination', 'Coordination Only'), ('investigating', 'Investigating')], default='secondary', help_text='Role of this department in the complaint resolution', max_length=20)), ('is_primary', models.BooleanField(default=False, help_text='Mark as the primary responsible department')), ('notes', models.TextField(blank=True, help_text="Additional notes about this department's involvement")), ('assigned_at', models.DateTimeField(blank=True, null=True)), ('response_submitted', models.BooleanField(default=False, help_text='Whether this department has submitted their response')), ('response_submitted_at', models.DateTimeField(blank=True, null=True)), ('response_notes', models.TextField(blank=True, help_text="Department's response/feedback on the complaint")), ('forwarded_at', models.DateTimeField(blank=True, help_text='When complaint was sent to this department', null=True)), ('first_reminder_sent_at', models.DateTimeField(blank=True, help_text='When first reminder was sent to this department', null=True)), ('second_reminder_sent_at', models.DateTimeField(blank=True, help_text='When second reminder was sent to this department', null=True)), ('delay_reason', models.TextField(blank=True, help_text='Reason for department delay in response')), ('delayed_person', models.CharField(blank=True, help_text='Name of person responsible for delay', max_length=200)), ], options={ 'verbose_name': 'Complaint Involved Department', 'verbose_name_plural': 'Complaint Involved Departments', 'ordering': ['-is_primary', '-created_at'], }, ), migrations.CreateModel( name='ComplaintInvolvedStaff', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('role', models.CharField(choices=[('accused', 'Accused/Involved'), ('witness', 'Witness'), ('responsible', 'Responsible for Resolution'), ('investigator', 'Investigator'), ('support', 'Support Staff'), ('px_management', 'PX Management')], default='accused', help_text='Role of this staff member in the complaint', max_length=20)), ('notes', models.TextField(blank=True, help_text="Additional notes about this staff member's involvement")), ('explanation_requested', models.BooleanField(default=False, help_text='Whether an explanation has been requested from this staff')), ('explanation_requested_at', models.DateTimeField(blank=True, null=True)), ('explanation_received', models.BooleanField(default=False, help_text='Whether an explanation has been received')), ('explanation_received_at', models.DateTimeField(blank=True, null=True)), ('explanation', models.TextField(blank=True, help_text="The staff member's explanation")), ], options={ 'verbose_name': 'Complaint Involved Staff', 'verbose_name_plural': 'Complaint Involved Staff', 'ordering': ['role', '-created_at'], }, ), migrations.CreateModel( name='ComplaintMeeting', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('meeting_date', models.DateTimeField(help_text='Date and time of the meeting')), ('meeting_type', models.CharField(choices=[('management_intervention', 'Management Intervention'), ('pr_follow_up', 'PR Follow-up'), ('department_review', 'Department Review'), ('other', 'Other')], default='management_intervention', help_text='Type of meeting', max_length=50)), ('outcome', models.TextField(blank=True, help_text='Meeting outcome and agreed resolution')), ('notes', models.TextField(blank=True, help_text='Additional meeting notes')), ], options={ 'verbose_name': 'Complaint Meeting', 'verbose_name_plural': 'Complaint Meetings', 'ordering': ['-meeting_date'], }, ), migrations.CreateModel( name='ComplaintPRInteraction', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('contact_date', models.DateTimeField(help_text='Date and time of PR contact with complainant')), ('contact_method', models.CharField(choices=[('phone', 'Phone'), ('in_person', 'In Person'), ('email', 'Email'), ('other', 'Other')], default='in_person', help_text='Method of contact', max_length=20)), ('statement_text', models.TextField(blank=True, help_text='Formal statement taken from the complainant')), ('procedure_explained', models.BooleanField(default=False, help_text='Whether complaint procedure was explained to the complainant')), ('notes', models.TextField(blank=True, help_text='Additional notes from the PR interaction')), ], options={ 'verbose_name': 'PR Interaction', 'verbose_name_plural': 'PR Interactions', 'ordering': ['-contact_date'], }, ), migrations.CreateModel( name='ComplaintSLAConfig', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('severity', models.CharField(blank=True, choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('critical', 'Critical')], help_text='Severity level for this SLA (optional if source is specified)', max_length=20, null=True)), ('priority', models.CharField(blank=True, choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('critical', 'Critical')], help_text='Priority level for this SLA (optional if source is specified)', max_length=20, null=True)), ('sla_hours', models.IntegerField(help_text='Number of hours until SLA deadline')), ('first_reminder_hours_after', models.IntegerField(default=0, help_text='Send 1st reminder X hours after complaint creation (0 = use reminder_hours_before)')), ('second_reminder_hours_after', models.IntegerField(default=0, help_text='Send 2nd reminder X hours after complaint creation (0 = use second_reminder_hours_before)')), ('escalation_hours_after', models.IntegerField(default=0, help_text='Escalate complaint X hours after creation if unresolved (0 = use overdue logic)')), ('reminder_hours_before', models.IntegerField(default=24, help_text='Send first reminder X hours before deadline')), ('second_reminder_enabled', models.BooleanField(default=False, help_text='Enable sending a second reminder')), ('second_reminder_hours_before', models.IntegerField(default=6, help_text='Send second reminder X hours before deadline')), ('thank_you_email_enabled', models.BooleanField(default=False, help_text='Send thank you email when complaint is closed')), ('is_active', models.BooleanField(default=True)), ], options={ 'ordering': ['hospital', 'source', 'severity', 'priority'], }, ), migrations.CreateModel( name='ComplaintTemplate', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('name', models.CharField(help_text="Template name (e.g., 'Long Wait Time', 'Rude Staff')", max_length=200)), ('description', models.TextField(help_text='Default description template with placeholders')), ('default_severity', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('critical', 'Critical')], default='medium', help_text='Default severity level', max_length=20)), ('default_priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('critical', 'Critical')], default='medium', help_text='Default priority level', max_length=20)), ('usage_count', models.IntegerField(default=0, editable=False, help_text='Number of times this template has been used')), ('placeholders', models.JSONField(blank=True, default=list, help_text='List of placeholder names used in description')), ('is_active', models.BooleanField(db_index=True, default=True, help_text='Whether this template is available for selection')), ], options={ 'verbose_name': 'Complaint Template', 'verbose_name_plural': 'Complaint Templates', 'ordering': ['-usage_count', 'name'], }, ), migrations.CreateModel( name='ComplaintThreshold', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('threshold_type', models.CharField(choices=[('resolution_survey_score', 'Resolution Survey Score'), ('response_time', 'Response Time'), ('resolution_time', 'Resolution Time')], help_text='Type of threshold', max_length=50)), ('threshold_value', models.FloatField(help_text='Threshold value (e.g., 50 for 50% score)')), ('comparison_operator', models.CharField(choices=[('lt', 'Less Than'), ('lte', 'Less Than or Equal'), ('gt', 'Greater Than'), ('gte', 'Greater Than or Equal'), ('eq', 'Equal')], default='lt', help_text='How to compare against threshold', max_length=10)), ('action_type', models.CharField(choices=[('create_px_action', 'Create PX Action'), ('send_notification', 'Send Notification'), ('escalate', 'Escalate')], help_text='Action to take when threshold is breached', max_length=50)), ('is_active', models.BooleanField(default=True)), ], options={ 'ordering': ['hospital', 'threshold_type'], }, ), migrations.CreateModel( name='ComplaintUpdate', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('update_type', models.CharField(choices=[('status_change', 'Status Change'), ('assignment', 'Assignment'), ('note', 'Note'), ('resolution', 'Resolution'), ('escalation', 'Escalation'), ('communication', 'Communication')], db_index=True, max_length=50)), ('message', models.TextField()), ('old_status', models.CharField(blank=True, max_length=25)), ('new_status', models.CharField(blank=True, max_length=25)), ('metadata', models.JSONField(blank=True, default=dict)), ], options={ 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='EscalationRule', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('name', models.CharField(max_length=200)), ('description', models.TextField(blank=True)), ('escalation_level', models.IntegerField(default=1, help_text='Escalation level (1 = first level, 2 = second, etc.)')), ('max_escalation_level', models.IntegerField(default=3, help_text='Maximum escalation level before stopping (default: 3)')), ('trigger_on_overdue', models.BooleanField(default=True, help_text='Trigger when complaint is overdue')), ('trigger_hours_overdue', models.IntegerField(default=0, help_text='Trigger X hours after overdue (0 = immediately)')), ('reminder_escalation_enabled', models.BooleanField(default=False, help_text='Enable escalation after reminder if no action taken')), ('reminder_escalation_hours', models.IntegerField(default=24, help_text='Escalate X hours after reminder if no action')), ('escalate_to_role', models.CharField(choices=[('department_manager', 'Department Manager'), ('hospital_admin', 'Hospital Admin'), ('medical_director', 'Medical Director'), ('admin_director', 'Administrative Director'), ('px_admin', 'PX Admin'), ('ceo', 'CEO'), ('specific_user', 'Specific User')], help_text='Role to escalate to', max_length=50)), ('severity_filter', models.CharField(blank=True, choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('critical', 'Critical')], help_text='Only escalate complaints with this severity (blank = all)', max_length=20)), ('priority_filter', models.CharField(blank=True, choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('critical', 'Critical')], help_text='Only escalate complaints with this priority (blank = all)', max_length=20)), ('order', models.IntegerField(default=0, help_text='Escalation order (lower = first)')), ('is_active', models.BooleanField(default=True)), ], options={ 'ordering': ['hospital', 'order'], }, ), migrations.CreateModel( name='ExplanationAttachment', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('file', models.FileField(upload_to='explanation_attachments/%Y/%m/%d/')), ('filename', models.CharField(max_length=500)), ('file_type', models.CharField(blank=True, max_length=100)), ('file_size', models.IntegerField(help_text='File size in bytes')), ('description', models.TextField(blank=True)), ], options={ 'verbose_name': 'Explanation Attachment', 'verbose_name_plural': 'Explanation Attachments', 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='ExplanationSLAConfig', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('response_hours', models.IntegerField(default=48, help_text='Hours staff has to submit explanation')), ('reminder_hours_before', models.IntegerField(default=12, help_text='Send first reminder X hours before deadline')), ('second_reminder_enabled', models.BooleanField(default=True, help_text='Enable sending a second reminder before escalation')), ('second_reminder_hours_before', models.IntegerField(default=4, help_text='Send second reminder X hours before deadline')), ('auto_escalate_enabled', models.BooleanField(default=True, help_text='Automatically escalate to manager if no response')), ('escalation_hours_overdue', models.IntegerField(default=0, help_text='Escalate X hours after overdue (0 = immediately)')), ('max_escalation_levels', models.IntegerField(default=3, help_text='Maximum levels to escalate up staff hierarchy')), ('is_active', models.BooleanField(default=True)), ], options={ 'verbose_name': 'Explanation SLA Config', 'verbose_name_plural': 'Explanation SLA Configs', 'ordering': ['hospital'], }, ), migrations.CreateModel( name='GovernmentTicket', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('ticket_number', models.CharField(db_index=True, help_text='Ticket number from source system (e.g., B2022807)', max_length=50, unique=True)), ('status', models.CharField(choices=[('pending', 'Pending'), ('in_progress', 'In Progress'), ('resolved', 'Resolved'), ('closed', 'Closed')], db_index=True, default='pending', max_length=20)), ('complainant_name', models.CharField(max_length=200)), ('national_id', models.CharField(blank=True, max_length=20)), ('contact_number', models.CharField(blank=True, max_length=20)), ('received_date', models.DateTimeField(help_text='Date/time the ticket was received from source')), ('classification', models.CharField(blank=True, max_length=100)), ('content', models.TextField()), ('converted_to_complaint', models.BooleanField(db_index=True, default=False)), ], options={ 'verbose_name': 'Government Ticket', 'verbose_name_plural': 'Government Tickets', 'ordering': ['-received_date'], }, ), migrations.CreateModel( name='Inquiry', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('is_deleted', models.BooleanField(db_index=True, default=False)), ('deleted_at', models.DateTimeField(blank=True, null=True)), ('contact_name', models.CharField(blank=True, max_length=200)), ('contact_phone', models.CharField(blank=True, max_length=20)), ('contact_email', models.EmailField(blank=True, max_length=254)), ('reference_number', models.CharField(blank=True, max_length=50, unique=True)), ('subject', models.CharField(max_length=500)), ('message', models.TextField()), ('category', models.CharField(choices=[('appointment', 'Appointment'), ('billing', 'Billing'), ('medical_records', 'Medical Records'), ('general', 'General Information'), ('other', 'Other')], max_length=100)), ('priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], db_index=True, default='low', max_length=20)), ('ai_brief_en', models.CharField(blank=True, help_text='AI-generated brief summary (English)', max_length=500)), ('ai_brief_ar', models.CharField(blank=True, help_text='AI-generated brief summary (Arabic)', max_length=500)), ('status', models.CharField(choices=[('open', 'Open'), ('in_progress', 'In Progress'), ('resolved', 'Resolved'), ('closed', 'Closed'), ('contacted', 'Contacted'), ('contacted_no_response', 'Contacted, No Response')], db_index=True, default='open', max_length=25)), ('assigned_at', models.DateTimeField(blank=True, null=True)), ('activated_at', models.DateTimeField(blank=True, db_index=True, help_text='Timestamp when inquiry was first activated (moved from OPEN to IN_PROGRESS)', null=True)), ('due_at', models.DateTimeField(blank=True, db_index=True, help_text='SLA deadline', null=True)), ('is_overdue', models.BooleanField(db_index=True, default=False)), ('breached_at', models.DateTimeField(blank=True, db_index=True, help_text='Timestamp when inquiry first breached SLA', null=True)), ('reminder_sent_at', models.DateTimeField(blank=True, help_text='First SLA reminder timestamp', null=True)), ('second_reminder_sent_at', models.DateTimeField(blank=True, help_text='Second SLA reminder timestamp', null=True)), ('escalated_at', models.DateTimeField(blank=True, null=True)), ('response', models.TextField(blank=True)), ('response_en', models.TextField(blank=True, help_text='Response text (English)')), ('response_ar', models.TextField(blank=True, help_text='Response text (Arabic)')), ('response_sent_at', models.DateTimeField(blank=True, help_text='When response was sent to inquirer', null=True)), ('responded_at', models.DateTimeField(blank=True, null=True)), ('metadata', models.JSONField(blank=True, default=dict)), ('is_straightforward', models.BooleanField(default=True, help_text='Direct resolution (no department coordination needed)', verbose_name='Is Straightforward')), ('is_outgoing', models.BooleanField(default=False, help_text='Inquiry sent to external department for response', verbose_name='Is Outgoing')), ('transferred_at', models.DateTimeField(blank=True, db_index=True, help_text='When the inquiry was transferred to a department', null=True)), ('transfer_count', models.PositiveIntegerField(default=0, help_text='Number of times this inquiry has been transferred')), ('department_response_en', models.TextField(blank=True, verbose_name='Department Response (English)')), ('department_response_ar', models.TextField(blank=True, verbose_name='Department Response (Arabic)')), ('department_response_summary_en', models.TextField(blank=True, verbose_name='AI Summary of Dept Response (EN)')), ('department_response_summary_ar', models.TextField(blank=True, verbose_name='AI Summary of Dept Response (AR)')), ('department_responded_at', models.DateTimeField(blank=True, null=True)), ('dept_response_sla_due_at', models.DateTimeField(blank=True, db_index=True, help_text='SLA deadline for department response', null=True)), ('dept_response_is_overdue', models.BooleanField(db_index=True, default=False, help_text='Department response is overdue')), ('dept_response_reminder_sent_at', models.DateTimeField(blank=True, help_text='First SLA reminder for dept response', null=True)), ('dept_response_second_reminder_sent_at', models.DateTimeField(blank=True, help_text='Second SLA reminder for dept response', null=True)), ('dept_response_escalated_at', models.DateTimeField(blank=True, help_text='When dept response was escalated to manager', null=True)), ('dept_response_acceptance_status', models.CharField(choices=[('pending', 'Pending Review'), ('acceptable', 'Acceptable'), ('not_acceptable', 'Not Acceptable')], default='pending', help_text='Review status of the department response', max_length=20)), ('dept_response_accepted_at', models.DateTimeField(blank=True, help_text='When the department response was reviewed', null=True)), ('dept_response_acceptance_notes', models.TextField(blank=True, help_text='Notes about the acceptance decision')), ('contacted_nr_at', models.DateTimeField(blank=True, null=True, verbose_name='Contacted NR Date')), ('contacted_nr_time', models.TimeField(blank=True, null=True, verbose_name='Contacted NR Time')), ('contacted_nr_duration', models.DurationField(blank=True, null=True, verbose_name='Contacted NR Duration')), ('under_process_at', models.DateTimeField(blank=True, null=True, verbose_name='Under Process Date')), ('under_process_time', models.TimeField(blank=True, null=True, verbose_name='Under Process Time')), ('under_process_duration', models.DurationField(blank=True, null=True, verbose_name='Under Process Duration')), ('contacted_at', models.DateTimeField(blank=True, null=True, verbose_name='Contacted Date')), ('contacted_time', models.TimeField(blank=True, null=True, verbose_name='Contacted Time')), ('contacted_duration', models.DurationField(blank=True, null=True, verbose_name='Contacted Duration')), ('timeline_sla', models.CharField(blank=True, choices=[('24_hours', '24 Hours'), ('48_hours', '48 Hours'), ('72_hours', '72 Hours'), ('more_than_72_hours', 'More than 72 hours')], help_text='Response deadline category', max_length=50, verbose_name='Timeline SLA')), ('staff_notes', models.TextField(blank=True, verbose_name='Staff Notes')), ('supervisor_notes', models.TextField(blank=True, verbose_name='Supervisor Notes')), ('requires_follow_up', models.BooleanField(db_index=True, default=False, help_text='This inquiry requires follow-up call')), ('follow_up_due_at', models.DateTimeField(blank=True, db_index=True, help_text='Due date for follow-up call to inquirer', null=True)), ('follow_up_completed_at', models.DateTimeField(blank=True, help_text='When follow-up call was completed', null=True)), ('follow_up_notes', models.TextField(blank=True, help_text='Notes from follow-up call')), ('follow_up_reminder_sent_at', models.DateTimeField(blank=True, help_text='When reminder was sent for follow-up', null=True)), ], options={ 'verbose_name_plural': 'Inquiries', 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='InquiryAttachment', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('file', models.FileField(upload_to='inquiries/%Y/%m/%d/')), ('filename', models.CharField(max_length=500)), ('file_type', models.CharField(blank=True, max_length=100)), ('file_size', models.IntegerField(help_text='File size in bytes')), ('description', models.TextField(blank=True)), ], options={ 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='InquiryExplanation', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('explanation', models.TextField(blank=True, help_text="Staff's response to the inquiry")), ('token', models.CharField(help_text='Unique access token for response submission', max_length=64, unique=True)), ('is_used', models.BooleanField(db_index=True, default=False, help_text='Token expiry tracking - becomes True after submission')), ('submitted_via', models.CharField(choices=[('email_link', 'Email Link'), ('direct', 'Direct Entry')], default='email_link', max_length=20)), ('email_sent_at', models.DateTimeField(blank=True, help_text='When the response request email was sent', null=True)), ('responded_at', models.DateTimeField(blank=True, help_text='When the response was submitted', null=True)), ('request_message', models.TextField(blank=True, help_text='Optional message sent with the response request')), ('sla_due_at', models.DateTimeField(blank=True, db_index=True, help_text='SLA deadline for staff to submit response', null=True)), ('is_overdue', models.BooleanField(db_index=True, default=False)), ('reminder_sent_at', models.DateTimeField(blank=True, null=True)), ('second_reminder_sent_at', models.DateTimeField(blank=True, null=True)), ('escalated_at', models.DateTimeField(blank=True, null=True)), ], options={ 'verbose_name': 'Inquiry Explanation', 'verbose_name_plural': 'Inquiry Explanations', 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='InquiryExplanationAttachment', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('file', models.FileField(upload_to='inquiry_explanation_attachments/%Y/%m/%d/')), ('filename', models.CharField(max_length=500)), ('file_type', models.CharField(blank=True, max_length=100)), ('file_size', models.IntegerField(help_text='File size in bytes')), ('description', models.TextField(blank=True)), ], options={ 'verbose_name': 'Inquiry Explanation Attachment', 'verbose_name_plural': 'Inquiry Explanation Attachments', 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='InquirySLAConfig', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('sla_hours', models.IntegerField(help_text='Number of hours until SLA deadline')), ('first_reminder_hours_after', models.IntegerField(default=0, help_text='Send 1st reminder X hours after inquiry creation (0 = use reminder_hours_before)')), ('second_reminder_hours_after', models.IntegerField(default=0, help_text='Send 2nd reminder X hours after inquiry creation (0 = use second_reminder_hours_before)')), ('escalation_hours_after', models.IntegerField(default=0, help_text='Escalate inquiry X hours after creation if unresolved (0 = use overdue logic)')), ('reminder_hours_before', models.IntegerField(default=24, help_text='Send first reminder X hours before deadline')), ('second_reminder_enabled', models.BooleanField(default=False, help_text='Enable sending a second reminder')), ('second_reminder_hours_before', models.IntegerField(default=6, help_text='Send second reminder X hours before deadline')), ('is_active', models.BooleanField(default=True)), ('dept_response_hours', models.IntegerField(default=48, help_text='Hours for department to submit a response')), ('dept_response_reminder_hours_before', models.IntegerField(default=12, help_text='Send 1st reminder X hours before dept response deadline')), ('dept_response_second_reminder_enabled', models.BooleanField(default=True, help_text='Enable sending a second reminder for dept response')), ('dept_response_second_reminder_hours_before', models.IntegerField(default=4, help_text='Send 2nd reminder X hours before dept response deadline')), ('dept_response_auto_escalate_enabled', models.BooleanField(default=True, help_text='Auto-escalate to department manager if response overdue')), ('dept_response_escalation_hours_overdue', models.IntegerField(default=0, help_text='Escalate X hours after dept response deadline (0 = immediately)')), ], options={ 'verbose_name': 'Inquiry SLA Config', 'verbose_name_plural': 'Inquiry SLA Configs', 'ordering': ['hospital', 'source'], }, ), migrations.CreateModel( name='InquiryUpdate', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('update_type', models.CharField(choices=[('status_change', 'Status Change'), ('assignment', 'Assignment'), ('note', 'Note'), ('response', 'Response'), ('communication', 'Communication'), ('department_response', 'Department Response'), ('sent_to_department', 'Sent to Department'), ('transferred_to_department', 'Transferred to Department')], db_index=True, max_length=50)), ('message', models.TextField()), ('old_status', models.CharField(blank=True, max_length=25)), ('new_status', models.CharField(blank=True, max_length=25)), ('metadata', models.JSONField(blank=True, default=dict)), ], options={ 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='OnCallAdmin', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('start_date', models.DateField(blank=True, help_text='Start date for this on-call assignment (optional)', null=True)), ('end_date', models.DateField(blank=True, help_text='End date for this on-call assignment (optional)', null=True)), ('notification_priority', models.PositiveIntegerField(default=1, help_text='Priority for notifications (1 = highest)')), ('is_active', models.BooleanField(default=True, help_text='Whether this on-call assignment is currently active')), ('notify_email', models.BooleanField(default=True, help_text='Send email notifications')), ('notify_sms', models.BooleanField(default=False, help_text='Send SMS notifications')), ('sms_phone', models.CharField(blank=True, help_text='Custom phone number for SMS notifications (optional)', max_length=20)), ], options={ 'verbose_name': 'On-Call Admin', 'verbose_name_plural': 'On-Call Admins', 'ordering': ['notification_priority', '-created_at'], }, ), migrations.CreateModel( name='OnCallAdminSchedule', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('working_days', models.JSONField(default=list, help_text='List of working days (0=Monday, 6=Sunday). Default: [0,1,2,3,4] (Mon-Fri)')), ('work_start_time', models.TimeField(default='08:00', help_text='Start of working hours (e.g., 08:00)')), ('work_end_time', models.TimeField(default='17:00', help_text='End of working hours (e.g., 17:00)')), ('timezone', models.CharField(default='Asia/Riyadh', help_text='Timezone for working hours calculation (e.g., Asia/Riyadh)', max_length=50)), ('is_active', models.BooleanField(default=True, help_text='Whether this on-call schedule is active')), ], options={ 'verbose_name': 'On-Call Admin Schedule', 'verbose_name_plural': 'On-Call Admin Schedules', 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='PatientComplaintSession', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('token', models.CharField(max_length=64, unique=True)), ('expires_at', models.DateTimeField(db_index=True)), ('is_active', models.BooleanField(db_index=True, default=True)), ], options={ 'ordering': ['-created_at'], }, ), migrations.CreateModel( name='Complaint', fields=[ ('created_at', models.DateTimeField(auto_now_add=True, db_index=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('is_deleted', models.BooleanField(db_index=True, default=False)), ('deleted_at', models.DateTimeField(blank=True, null=True)), ('contact_name', models.CharField(blank=True, max_length=200)), ('contact_phone', models.CharField(blank=True, max_length=20)), ('contact_email', models.EmailField(blank=True, max_length=254)), ('relation_to_patient', models.CharField(blank=True, choices=[('patient', 'Patient'), ('relative', 'Relative'), ('friend', 'Friend'), ('other', 'Other')], help_text="Complainant's relationship to the patient", max_length=20, verbose_name='Relation to Patient')), ('patient_name', models.CharField(blank=True, help_text='Name of the patient involved', max_length=200, verbose_name='Patient Name')), ('national_id', apps.core.encryption.EncryptedCharField(blank=True, help_text='Saudi National ID or Iqama number', max_length=512, verbose_name='National ID/Iqama No.')), ('national_id_hash', models.CharField(blank=True, db_index=True, max_length=64)), ('incident_date', models.DateField(blank=True, help_text='Date when the incident occurred', null=True, verbose_name='Incident Date')), ('staff_name', models.CharField(blank=True, help_text='Name of staff member involved (if known)', max_length=200, verbose_name='Staff Involved')), ('expected_result', models.TextField(blank=True, help_text='What the complainant expects as a resolution', verbose_name='Expected Complaint Result')), ('reference_number', models.CharField(blank=True, help_text='Unique reference number for patient tracking', max_length=50, null=True, unique=True)), ('encounter_id', models.CharField(blank=True, db_index=True, help_text='Related encounter ID if applicable', max_length=100)), ('title', models.CharField(max_length=500)), ('description', models.TextField()), ('ai_brief_en', models.CharField(blank=True, db_index=True, help_text='AI-generated 2-3 word summary in English', max_length=100)), ('ai_brief_ar', models.CharField(blank=True, help_text='AI-generated 2-3 word summary in Arabic', max_length=100)), ('subcategory', models.CharField(blank=True, help_text='Level 3: Subcategory code (legacy)', max_length=100)), ('classification', models.CharField(blank=True, help_text='Level 4: Classification code (legacy)', max_length=100)), ('complaint_type', models.CharField(choices=[('complaint', 'Complaint'), ('appreciation', 'Appreciation')], db_index=True, default='complaint', help_text='Type of feedback (complaint vs appreciation)', max_length=20)), ('complaint_source_type', models.CharField(choices=[('internal', 'Internal'), ('external', 'External')], db_index=True, default='external', help_text='Source type (Internal = staff-generated, External = patient/public-generated)', max_length=20)), ('priority', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('critical', 'Critical')], db_index=True, default='medium', max_length=20)), ('severity', models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High'), ('critical', 'Critical')], db_index=True, default='medium', max_length=20)), ('status', models.CharField(choices=[('open', 'Open'), ('in_progress', 'In Progress'), ('partially_resolved', 'Partially Resolved'), ('resolved', 'Resolved'), ('closed', 'Closed'), ('cancelled', 'Cancelled'), ('contacted', 'Contacted'), ('contacted_no_response', 'Contacted, No Response'), ('pending_external', 'Pending External'), ('ovr_pending', 'OVR Pending Approval')], db_index=True, default='open', max_length=25)), ('assigned_at', models.DateTimeField(blank=True, null=True)), ('activated_at', models.DateTimeField(blank=True, db_index=True, help_text='Timestamp when complaint was first activated (moved from OPEN to IN_PROGRESS)', null=True)), ('due_at', models.DateTimeField(db_index=True, help_text='SLA deadline')), ('is_overdue', models.BooleanField(db_index=True, default=False)), ('breached_at', models.DateTimeField(blank=True, db_index=True, help_text='Timestamp when complaint first breached SLA', null=True)), ('reminder_sent_at', models.DateTimeField(blank=True, help_text='First SLA reminder timestamp', null=True)), ('second_reminder_sent_at', models.DateTimeField(blank=True, help_text='Second SLA reminder timestamp', null=True)), ('escalated_at', models.DateTimeField(blank=True, null=True)), ('explanation_requested', models.BooleanField(default=False, help_text='Whether an explanation has been requested from staff')), ('explanation_requested_at', models.DateTimeField(blank=True, help_text='When explanation request was first sent to staff', null=True)), ('explanation_received_at', models.DateTimeField(blank=True, help_text='When explanation was received from staff', null=True)), ('explanation_delay_reason', models.TextField(blank=True, help_text='Reason for delay in receiving staff explanation')), ('resolution', models.TextField(blank=True)), ('resolution_sent_at', models.DateTimeField(blank=True, null=True)), ('resolution_category', models.CharField(blank=True, choices=[('full_action_taken', 'Full Action Taken'), ('partial_action_taken', 'Partial Action Taken'), ('no_action_needed', 'No Action Needed'), ('cannot_resolve', 'Cannot Resolve'), ('patient_withdrawn', 'Patient Withdrawn')], db_index=True, help_text='Category of resolution', max_length=50)), ('resolution_outcome', models.CharField(blank=True, choices=[('patient', 'Patient'), ('hospital', 'Hospital'), ('other', 'Other — please specify')], db_index=True, help_text='Who was in wrong/right (Patient / Hospital / Other)', max_length=20)), ('resolution_outcome_other', models.TextField(blank=True, help_text='Specify if Other was selected for resolution outcome')), ('resolved_at', models.DateTimeField(blank=True, null=True)), ('closed_at', models.DateTimeField(blank=True, null=True)), ('reopened_at', models.DateTimeField(blank=True, null=True)), ('resolution_survey_sent_at', models.DateTimeField(blank=True, null=True)), ('satisfaction', models.CharField(blank=True, choices=[('satisfied', 'Satisfied'), ('neutral', 'Neutral'), ('dissatisfied', 'Dissatisfied'), ('no_response', 'No Response')], db_index=True, help_text='Direct satisfaction feedback from patient follow-up call', max_length=20)), ('satisfaction_set_at', models.DateTimeField(blank=True, help_text='When satisfaction was last set', null=True)), ('moh_reference', models.CharField(blank=True, help_text='Ministry of Health reference number', max_length=100)), ('moh_reference_date', models.DateField(blank=True, help_text='MOH reference date', null=True)), ('chi_reference', models.CharField(blank=True, help_text='Council of Health Insurance reference number', max_length=100)), ('chi_reference_date', models.DateField(blank=True, help_text='CHI reference date', null=True)), ('file_number', models.CharField(blank=True, db_index=True, help_text='Patient file/MRN number', max_length=100)), ('form_sent_at', models.DateTimeField(blank=True, help_text='When complaint form was sent to the complained department', null=True)), ('forwarded_to_dept_at', models.DateTimeField(blank=True, help_text='When complaint was forwarded to the involved department', null=True)), ('response_date', models.DateField(blank=True, help_text='Date when response was received', null=True)), ('complaint_subject', models.CharField(blank=True, help_text='Main complaint subject (from Excel classification)', max_length=500)), ('action_taken_by_dept', models.TextField(blank=True, help_text='Action taken by the responsible department')), ('action_result', models.TextField(blank=True, help_text='Result of the action/investigation taken')), ('recommendation_action_plan', models.TextField(blank=True, help_text='Solutions, suggestions, and action plan')), ('delay_reason_closure', models.CharField(blank=True, choices=[('department_no_response', 'Department No Response'), ('escalated', 'Escalated'), ('patient_not_satisfied', 'Patient Not Satisfied with Complaint Resolution')], help_text='Reason for not closing the complaint within 72 hours', max_length=50)), ('was_pending_external', models.BooleanField(default=False, help_text='Whether this complaint was ever in pending_external status')), ('pending_external_set_at', models.DateTimeField(blank=True, help_text='When complaint was set to pending_external status', null=True)), ('is_escalated_ovr', models.BooleanField(db_index=True, default=False, help_text='Whether complaint is escalated as OVR')), ('escalated_ovr_at', models.DateTimeField(blank=True, help_text='When OVR escalation was set', null=True)), ('metadata', models.JSONField(blank=True, default=dict)), ('assigned_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_complaints', to=settings.AUTH_USER_MODEL)), ('closed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='closed_complaints', to=settings.AUTH_USER_MODEL)), ('created_by', models.ForeignKey(blank=True, help_text='User who created this complaint (SourceUser or Patient)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_complaints', to=settings.AUTH_USER_MODEL)), ('deleted_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='deleted_%(class)s_set', to=settings.AUTH_USER_MODEL)), ('department', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='complaints', to='organizations.department')), ('escalated_ovr_by', models.ForeignKey(blank=True, help_text='User who escalated as OVR', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='escalated_ovr_complaints', to=settings.AUTH_USER_MODEL)), ('hospital', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='complaints', to='organizations.hospital')), ('location', models.ForeignKey(blank=True, help_text='Location (e.g., Riyadh, Jeddah)', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='complaints', to='organizations.location')), ('main_section', models.ForeignKey(blank=True, help_text='Section/Department', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='complaints', to='organizations.mainsection')), ('patient', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='complaints', to='organizations.patient')), ('reopened_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='reopened_complaints', to=settings.AUTH_USER_MODEL)), ('reopened_from', models.ForeignKey(blank=True, help_text='Original complaint this was reopened from', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='reopenings', to='complaints.complaint')), ], options={ 'ordering': ['-created_at'], }, ), ]