HH/apps/complaints/signals.py
2026-02-22 08:35:53 +03:00

208 lines
7.8 KiB
Python

"""
Complaint signals - Automatic SMS notifications on status changes
This module handles automatic SMS notifications to complainants when:
1. Complaint is created (confirmation)
2. Complaint status changes to resolved or closed
3. Auto-sync department from staff when staff is assigned
"""
import logging
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
from django.contrib.sites.shortcuts import get_current_site
from .models import Complaint, ComplaintUpdate
logger = logging.getLogger(__name__)
@receiver(pre_save, sender=Complaint)
def sync_department_from_staff(sender, instance, **kwargs):
"""
Automatically set complaint.department from staff.department when staff is assigned.
This ensures the department is always in sync with the assigned staff member,
regardless of how the complaint is saved (API, admin, forms, etc.).
"""
if instance.staff:
# If staff is assigned, set department from staff's department
staff_department = instance.staff.department
if staff_department and instance.department_id != staff_department.id:
instance.department = staff_department
logger.info(
f"Complaint #{instance.id}: Auto-synced department to '{staff_department.name}' "
f"from staff '{instance.staff.name}'"
)
elif instance.pk:
# If staff is being removed (set to None), check if we should clear department
# Only clear if the department was originally from a staff member
# We keep the department if it was manually set
pass
@receiver(post_save, sender=Complaint)
def send_complaint_creation_sms(sender, instance, created, **kwargs):
"""
Send SMS notification when complaint is created.
Only sends for public complaints (those with contact_phone).
"""
if not created:
return
# Only send SMS if phone number is provided
if not instance.contact_phone:
logger.info(f"Complaint #{instance.id} created but no phone number provided. Skipping SMS.")
return
# Send SMS notification
try:
from apps.notifications.services import NotificationService
# Get tracking URL
tracking_url = instance.get_tracking_url()
# Bilingual SMS messages
messages = {
'en': f"PX360: Your complaint #{instance.reference_number} has been received. Track: {tracking_url}",
'ar': f"PX360: تم استلام شكوتك #{instance.reference_number}. تتبع الشكوى: {tracking_url}"
}
# Default to English (can be enhanced to detect language)
sms_message = messages['en']
# Send SMS
notification_log = NotificationService.send_sms(
phone=instance.contact_phone,
message=sms_message,
related_object=instance,
metadata={
'notification_type': 'complaint_created',
'reference_number': instance.reference_number,
'tracking_url': tracking_url,
'language': 'en' # Default to English
}
)
logger.info(f"Creation SMS sent to {instance.contact_phone} for complaint #{instance.id}")
# Create complaint update to track SMS
ComplaintUpdate.objects.create(
complaint=instance,
update_type='communication',
message=f"SMS notification sent to complainant: Your complaint has been received",
metadata={
'notification_type': 'complaint_created',
'notification_log_id': str(notification_log.id) if notification_log else None
}
)
except Exception as e:
# Log error but don't fail the complaint save
logger.error(f"Failed to send creation SMS for complaint #{instance.id}: {str(e)}")
@receiver(post_save, sender=Complaint)
def send_complaint_status_change_sms(sender, instance, created, **kwargs):
"""
Send SMS notification when complaint status changes to resolved or closed.
Uses update_fields to detect actual status changes (not just re-saves).
"""
# Skip on creation (handled by creation signal)
if created:
return
# Check if this is a status change to resolved or closed
# Use update_fields to detect actual status changes
if not hasattr(instance, '_status_was'):
return
old_status = instance._status_was
new_status = instance.status
# Only send SMS for resolved or closed status changes
if new_status not in ['resolved', 'closed']:
return
# Only send if status actually changed
if old_status == new_status:
return
# Only send SMS if phone number is provided
if not instance.contact_phone:
logger.info(f"Complaint #{instance.id} status changed to {new_status} but no phone number. Skipping SMS.")
return
# Send SMS notification
try:
from apps.notifications.services import NotificationService
# Bilingual SMS messages
messages_en = {
'resolved': f"PX360: Your complaint #{instance.reference_number} has been resolved. Thank you for your feedback.",
'closed': f"PX360: Your complaint #{instance.reference_number} has been closed. Thank you for your feedback."
}
messages_ar = {
'resolved': f"PX360: تم حل شكوتك #{instance.reference_number}. شكراً لتعاونكم.",
'closed': f"PX360: تم إغلاق شكوتك #{instance.reference_number}. شكراً لتعاونكم."
}
# Default to English (can be enhanced to detect language)
sms_message = messages_en.get(new_status, '')
# Send SMS
notification_log = NotificationService.send_sms(
phone=instance.contact_phone,
message=sms_message,
related_object=instance,
metadata={
'notification_type': 'complaint_status_change',
'reference_number': instance.reference_number,
'old_status': old_status,
'new_status': new_status,
'language': 'en' # Default to English
}
)
logger.info(f"Status change SMS sent to {instance.contact_phone} for complaint #{instance.id}: {old_status} -> {new_status}")
# Create complaint update to track SMS
ComplaintUpdate.objects.create(
complaint=instance,
update_type='communication',
message=f"SMS notification sent to complainant: Status changed to {new_status}",
metadata={
'notification_type': 'complaint_status_change',
'old_status': old_status,
'new_status': new_status,
'notification_log_id': str(notification_log.id) if notification_log else None
}
)
except Exception as e:
# Log error but don't fail the complaint save
logger.error(f"Failed to send status change SMS for complaint #{instance.id}: {str(e)}")
# Hook into ComplaintUpdate to track SMS sent manually via API
@receiver(post_save, sender=ComplaintUpdate)
def track_manual_sms(sender, instance, created, **kwargs):
"""
Track manually sent SMS notifications.
This ensures that SMS sent via API endpoints (like send_resolution_notification)
are also properly tracked.
"""
if not created:
return
# Check if this update was for a communication/notification
if instance.update_type == 'communication':
# Log tracking info
logger.info(
f"Manual communication update created for complaint #{instance.complaint.id}: "
f"{instance.message[:50]}..."
)