From 4525ec3e6aa93a1aed214bc87ce9b1d622894972 Mon Sep 17 00:00:00 2001 From: javad Date: Mon, 28 Sep 2026 21:13:19 +0330 Subject: [PATCH] fix button force anoncecall --- apps/notifications/announcement_worker.py | 8 +- .../commands/process_notifications.py | 14 +++- apps/tickets/views.py | 15 +++- static/css/operations.css | 10 +-- .../tickets/_immediate_announcement.html | 6 +- templates/tickets/staff_detail.html | 2 +- tests/e2e/pages/tickets.py | 6 +- tests/e2e/test_staff_operations_ui.py | 5 +- tests/test_announcements.py | 81 +++++++++++++------ 9 files changed, 99 insertions(+), 48 deletions(-) diff --git a/apps/notifications/announcement_worker.py b/apps/notifications/announcement_worker.py index 7f341fd..6724dd7 100644 --- a/apps/notifications/announcement_worker.py +++ b/apps/notifications/announcement_worker.py @@ -85,7 +85,7 @@ def _deliver(job): job.last_error = "" -def process_due_announcements(): +def process_due_announcements(*, job_id=None): now = timezone.now() # A crashed worker can safely retry with the same provider idempotency key. AnnouncementJob.objects.filter( @@ -93,13 +93,15 @@ def process_due_announcements(): next_attempt_at__lt=now - timedelta(minutes=10), ).update(status=AnnouncementJob.Status.PENDING, next_attempt_at=now) with transaction.atomic(): - jobs = list( + queryset = ( AnnouncementJob.objects.select_for_update(skip_locked=True) .filter(status=AnnouncementJob.Status.PENDING, next_attempt_at__lte=now) .select_related("message__ticket__customer", "ticket__customer") .annotate(priority=Case(When(source=AnnouncementJob.Source.MANUAL, then=Value(0)), default=Value(1), output_field=IntegerField())) - .order_by("priority", "next_attempt_at", "pk")[:20] ) + if job_id is not None: + queryset = queryset.filter(pk=job_id) + jobs = list(queryset.order_by("priority", "next_attempt_at", "pk")[:20]) AnnouncementJob.objects.filter(pk__in=[job.pk for job in jobs]).update( status=AnnouncementJob.Status.PROCESSING, next_attempt_at=now, ) diff --git a/apps/notifications/management/commands/process_notifications.py b/apps/notifications/management/commands/process_notifications.py index 6658730..dc513bb 100644 --- a/apps/notifications/management/commands/process_notifications.py +++ b/apps/notifications/management/commands/process_notifications.py @@ -251,9 +251,19 @@ def _process_delivery_checks(options): NotificationJob.objects.filter(pk__in=[job.pk for job in jobs]).update( delivery_next_check_at=now + timedelta(minutes=10), ) - provider = get_sms_provider() + if not jobs: + return + try: + provider = get_sms_provider() + except Exception: + logger.exception("SMS delivery provider initialization failed") + return for job in jobs: - result = provider.get_delivery_status(job.provider_message_id) + try: + result = provider.get_delivery_status(job.provider_message_id) + except Exception: + logger.exception("SMS delivery check failed for job %s", job.pk) + continue if result is None: job.delivery_status = "" job.delivery_next_check_at = None diff --git a/apps/tickets/views.py b/apps/tickets/views.py index 84bb898..bd44f63 100644 --- a/apps/tickets/views.py +++ b/apps/tickets/views.py @@ -472,8 +472,19 @@ def staff_announcement_now(request, public_id): elif not ticket.customer.is_active or not ticket.customer.local_mobile: messages.error(request,"شماره همراه مخاطب برای تماس در دسترس نیست.") else: - _,created=schedule_immediate_announcement(ticket,request.user) - messages.success(request,"درخواست اطلاع‌رسانی تلفنی فوری در صف قرار گرفت." if created else "درخواست اطلاع‌رسانی تلفنی این تیکت در دقیقهٔ گذشته ثبت شده است.") + job,created=schedule_immediate_announcement(ticket,request.user) + if created: + from apps.notifications.announcement_worker import process_due_announcements + process_due_announcements(job_id=job.pk) + job.refresh_from_db() + if job.status == AnnouncementJob.Status.SENT: + messages.success(request,"درخواست تماس توسط سرویس پذیرفته شد.") + elif job.attempts: + messages.warning(request,"سرویس تماس پاسخ نداد؛ تلاش مجدد در صف قرار گرفت.") + else: + messages.info(request,"درخواست تماس در حال پردازش است.") + else: + messages.info(request,"درخواست اطلاع‌رسانی تلفنی این تیکت در دقیقهٔ گذشته ثبت شده است.") return redirect("staff_tickets:detail",public_id=public_id) @staff_required @require_POST diff --git a/static/css/operations.css b/static/css/operations.css index edb67e5..eadbf45 100644 --- a/static/css/operations.css +++ b/static/css/operations.css @@ -1,15 +1,11 @@ /* Staff organizational operations — intentionally scoped to avoid regressions. */ -.immediate-announcement-card{display:grid;gap:.7rem;border:1px solid #c9e4df;background:#f8fcfb} -.immediate-announcement-head{display:flex;align-items:center;gap:.65rem} -.immediate-announcement-head h3{margin:0;color:#164b47;font-size:.86rem;line-height:1.6} -.immediate-announcement-icon{display:grid;place-items:center;width:2rem;height:2rem;flex:none;border-radius:.65rem;background:#e2f3ef;color:#0f766e;font-size:1.05rem} -.immediate-announcement-card p{margin:0;color:#45605f;font-size:.73rem;line-height:1.85} +.immediate-announcement-card{display:grid;gap:.3rem;padding:.65rem;border:1px solid #c9e4df;background:#f8fcfb} .immediate-announcement-card form{margin:0} -.immediate-announcement-button{display:flex;align-items:center;justify-content:center;gap:.5rem;width:100%;min-height:2.75rem;border:1px solid #0f766e;border-radius:.7rem;background:#0f766e;color:#fff;font-size:.76rem;font-weight:800;cursor:pointer;transition:background .15s ease,box-shadow .15s ease} +.immediate-announcement-button{display:flex;align-items:center;justify-content:center;gap:.4rem;width:100%;min-height:2.25rem;border:1px solid #0f766e;border-radius:.55rem;background:#0f766e;color:#fff;font-size:.73rem;font-weight:800;cursor:pointer;transition:background .15s ease,box-shadow .15s ease} .immediate-announcement-button:hover:not(:disabled){background:#0b5f59;box-shadow:0 5px 15px -10px #0b5f59} .immediate-announcement-button:focus-visible{outline:2px solid #0f766e;outline-offset:3px} .immediate-announcement-button:disabled{border-color:#aebfc0;background:#aebfc0;cursor:not-allowed} -.immediate-announcement-card small{color:#607574;font-size:.65rem;line-height:1.7} +.immediate-announcement-card small{color:#607574;font-size:.62rem;line-height:1.45;text-align:center} .operations-page{display:grid;gap:1rem;min-width:0} .operations-page .staff-page-head{margin-bottom:0} .operations-page .ticket-stream,.operations-page .filters{border:1px solid #d9e4ee;background:#fff;box-shadow:0 18px 45px -38px rgba(8,55,107,.55)} diff --git a/templates/tickets/_immediate_announcement.html b/templates/tickets/_immediate_announcement.html index d176edc..16c5ee9 100644 --- a/templates/tickets/_immediate_announcement.html +++ b/templates/tickets/_immediate_announcement.html @@ -1,13 +1,11 @@ {% if can_request_announcement %}
-

اطلاع‌رسانی تلفنی فوری

-

این دکمه درخواست تماس با مخاطب و پخش یک پیام اطلاع‌رسانی از پیش ضبط‌شده را ثبت می‌کند.

{% csrf_token %}
- {% if announcement_ready %}درخواست در صف تماس ثبت می‌شود؛ نتیجه را در وضعیت اطلاع‌رسانی‌های تلفنی ببینید.{% else %}شماره مخاطب یا تنظیمات سرویس تماس در دسترس نیست.{% endif %} + {% if announcement_ready %}پخش پیام صوتی از پیش ضبط‌شده برای مخاطب{% else %}شماره مخاطب یا تنظیمات تماس در دسترس نیست.{% endif %}
{% endif %} diff --git a/templates/tickets/staff_detail.html b/templates/tickets/staff_detail.html index 5d1e21c..62232ad 100644 --- a/templates/tickets/staff_detail.html +++ b/templates/tickets/staff_detail.html @@ -17,8 +17,8 @@