estelam ticket, varizha , others
This commit is contained in:
1 parent
35a1ec1794
commit
6b546aad76
71 files changed
+2865
-191
No files matched your search
@@ -0,0 +1,42 @@
|
||||
"""Isolated worker handler; existing events keep their delivery flow."""
|
||||
from django.core.exceptions import ValidationError
|
||||
from apps.tickets.models import DispatchResponseControl
|
||||
|
||||
|
||||
DEFAULT_OPENING_SMS = (
|
||||
"لوله گستر گلپایگان با سلام؛ پاسخ استعلام شما بازگشایی شد. جهت صیانت از رقابت عادلانه، تمامی پیشنهادات تا این لحظه کاملاً محرمانه بوده و هیچکس دسترسی به مشاهده آنها نداشته است. اکنون تمامی پاسخها بهصورت همزمان بازگشایی شدند تا سلامت فرآیند تضمین گردد.\n"
|
||||
"از همراهی و اعتماد شما سپاسگزاریم. واحد فناوری اطلاعات لوله گستر گلپایگان"
|
||||
)
|
||||
|
||||
|
||||
def ensure_opening_template():
|
||||
"""Legacy seed bundles may predate this event; never replace an existing body."""
|
||||
from .models import SMSTemplate
|
||||
return SMSTemplate.objects.get_or_create(unit=None, event_type="DISPATCH_RESPONSES_OPENED", defaults={
|
||||
"title": "بازگشایی پاسخهای ارسال گروهی", "body": DEFAULT_OPENING_SMS, "allowed_tags": [], "is_active": True,
|
||||
})[0]
|
||||
|
||||
|
||||
def send_opening_sms(job, provider_factory):
|
||||
control = DispatchResponseControl.objects.get(dispatch_id=job.payload.get("dispatch_id"))
|
||||
permitted = (control.mode == "SEALED" and control.notify_sms
|
||||
and control.recipient_count_snapshot <= 15 and control.opened_at
|
||||
and job.ticket_id and job.ticket.group_dispatch_id == control.dispatch_id
|
||||
and job.customer_id == job.ticket.customer_id
|
||||
and str(job.customer.public_id) in control.respondents_snapshot)
|
||||
if not permitted or job.payload.get("body") != control.sms_body_snapshot:
|
||||
raise ValidationError("Opening notification does not match its authorized snapshot.")
|
||||
return provider_factory().send_message(job.customer.mobile, control.sms_body_snapshot)
|
||||
|
||||
|
||||
def send_dispatch_reply_bale(job, text, provider_factory):
|
||||
"""Dispatch metadata goes to its creator's private destination, never a unit room."""
|
||||
from apps.tickets.policies import DispatchPolicy
|
||||
dispatch = job.ticket.group_dispatch
|
||||
owner = dispatch.created_by
|
||||
profile = getattr(owner, "staff_profile", None)
|
||||
if not owner.is_active or not profile or not profile.is_active or not profile.bale_notifications_enabled:
|
||||
return None
|
||||
if not profile.bale_chat_id.strip() or not DispatchPolicy.can_view(owner, dispatch):
|
||||
return None
|
||||
return provider_factory().send_message(profile.bale_chat_id.strip(), text)
|
||||
@@ -58,6 +58,9 @@ def _bale_ticket_text(ticket, message=None, *, is_reply=False, message_count=1,
|
||||
message = message or ticket.ticket_messages.filter(is_internal=False).order_by("created_at", "id").first()
|
||||
raw_excerpt = " ".join((message.body if message else "").split())
|
||||
excerpt = f"{raw_excerpt[:277].rstrip()}…" if len(raw_excerpt)>280 else raw_excerpt or "بدون متن"
|
||||
from apps.tickets.dispatch_responses import response_control
|
||||
if message and message.author_id == ticket.customer_id and ticket.group_dispatch_id and response_control(ticket.group_dispatch):
|
||||
excerpt = "پاسخ ثبت شده است؛ محتوا فقط در پنل و با رعایت مجوز بازگشایی قابل مشاهده است."
|
||||
local_created = timezone.localtime(message.created_at if message else ticket.created_at)
|
||||
jalali = jdatetime.datetime.fromgregorian(datetime=local_created, locale=jdatetime.FA_LOCALE).strftime("%d %B، %H:%M")
|
||||
lines=[
|
||||
@@ -85,6 +88,9 @@ def _prepare_sms(job):
|
||||
|
||||
|
||||
def _send_sms(job):
|
||||
if job.event_type == "DISPATCH_RESPONSES_OPENED":
|
||||
from apps.notifications.dispatch_opening import send_opening_sms
|
||||
return send_opening_sms(job, get_sms_provider), None
|
||||
if job.event_type == 'PAYMENT_RECEIPT_RECEIVED':
|
||||
from apps.payments.receipt_alerts import send_receipt_alert
|
||||
return send_receipt_alert(job, sms_provider_factory=get_sms_provider), None
|
||||
@@ -109,6 +115,19 @@ def _send_sms(job):
|
||||
raise ValueError(f"Unsupported SMS event: {job.event_type}")
|
||||
return get_sms_provider().send_message(job.customer.mobile, message), issued
|
||||
|
||||
def _send_customer_reply_bale(job, unit_chat_id):
|
||||
message = job.ticket.ticket_messages.filter(pk=job.payload.get("message_id"), is_internal=False).first()
|
||||
if not message:
|
||||
raise ValueError("Customer reply message no longer exists")
|
||||
text = _bale_ticket_text(job.ticket, message, is_reply=True,
|
||||
message_count=job.payload.get("message_count", 1), reopened=job.event_type == "CUSTOMER_TICKET_REOPENED")
|
||||
from apps.tickets.dispatch_responses import response_control
|
||||
if job.ticket.group_dispatch_id and response_control(job.ticket.group_dispatch):
|
||||
from apps.notifications.dispatch_opening import send_dispatch_reply_bale
|
||||
return send_dispatch_reply_bale(job, text, get_bale_provider)
|
||||
return get_bale_provider().send_message(unit_chat_id, text) if unit_chat_id else None
|
||||
|
||||
|
||||
def _send_bale(job):
|
||||
if job.event_type == 'PAYMENT_RECEIPT_RECEIVED':
|
||||
from apps.payments.receipt_alerts import send_receipt_alert
|
||||
@@ -117,9 +136,7 @@ def _send_bale(job):
|
||||
if job.event_type == "NEW_CUSTOMER_TICKET":
|
||||
return get_bale_provider().send_message(unit_chat_id, _bale_ticket_text(job.ticket)) if unit_chat_id else None
|
||||
if job.event_type in ["CUSTOMER_TICKET_REPLY","CUSTOMER_TICKET_REOPENED"]:
|
||||
message = job.ticket.ticket_messages.filter(pk=job.payload.get("message_id"), is_internal=False).first()
|
||||
if not message: raise ValueError("Customer reply message no longer exists")
|
||||
return get_bale_provider().send_message(unit_chat_id, _bale_ticket_text(job.ticket,message,is_reply=True,message_count=job.payload.get("message_count",1),reopened=job.event_type=="CUSTOMER_TICKET_REOPENED")) if unit_chat_id else None
|
||||
return _send_customer_reply_bale(job, unit_chat_id)
|
||||
if not job.staff_user or not hasattr(job.staff_user, "staff_profile"): raise ValueError("Bale recipient is not configured")
|
||||
profile = job.staff_user.staff_profile
|
||||
if not profile.bale_notifications_enabled: return None
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 5.1.1 on 2026-10-13 09:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def seed_opening_template(apps, schema_editor):
|
||||
apps.get_model('notifications', 'SMSTemplate').objects.using(schema_editor.connection.alias).get_or_create(
|
||||
unit=None, event_type='DISPATCH_RESPONSES_OPENED', defaults={
|
||||
'title': 'بازگشایی پاسخهای ارسال گروهی', 'is_active': True, 'allowed_tags': [],
|
||||
'body': 'لوله گستر گلپایگان با سلام؛ پاسخ استعلام شما بازگشایی شد. جهت صیانت از رقابت عادلانه، تمامی پیشنهادات تا این لحظه کاملاً محرمانه بوده و هیچکس دسترسی به مشاهده آنها نداشته است. اکنون تمامی پاسخها بهصورت همزمان بازگشایی شدند تا سلامت فرآیند تضمین گردد.\nاز همراهی و اعتماد شما سپاسگزاریم. واحد فناوری اطلاعات لوله گستر گلپایگان',
|
||||
})
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('notifications', '0020_receipt_rejection_template'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.SeparateDatabaseAndState(state_operations=[migrations.AlterField(
|
||||
model_name='smstemplate',
|
||||
name='event_type',
|
||||
field=models.CharField(choices=[('DISPATCH_RESPONSES_OPENED', 'بازگشایی پاسخ\u200cهای ارسال گروهی'), ('CUSTOMER_TICKET_CREATED', 'ثبت تیکت توسط مشتری'), ('STAFF_TICKET_CREATED', 'ثبت تیکت توسط کارشناس'), ('STAFF_REPLY', 'پاسخ کارشناس'), ('PAYMENT_NOTICE_CREATED', 'صدور اعلامیه واریز'), ('PAYMENT_RECEIPT_APPROVED', 'تأیید نهایی فیش واریز'), ('PAYMENT_RECEIPT_REJECTED', 'رد فیش واریز'), ('UNIT_SUPERVISOR_NEW_TICKET', 'اطلاع تیکت جدید به سرپرستان واحد')], max_length=50),
|
||||
)], database_operations=[]),
|
||||
migrations.RunPython(seed_opening_template, migrations.RunPython.noop),
|
||||
]
|
||||
@@ -10,6 +10,7 @@ def sms_template_tags():
|
||||
class SMSTemplate(PersianTextNormalizationMixin, models.Model):
|
||||
persian_text_fields={"title":False,"body":True}
|
||||
class Event(models.TextChoices):
|
||||
DISPATCH_RESPONSES_OPENED="DISPATCH_RESPONSES_OPENED","بازگشایی پاسخهای ارسال گروهی"
|
||||
CUSTOMER_TICKET_CREATED="CUSTOMER_TICKET_CREATED","ثبت تیکت توسط مشتری"
|
||||
STAFF_TICKET_CREATED="STAFF_TICKET_CREATED","ثبت تیکت توسط کارشناس"
|
||||
STAFF_REPLY="STAFF_REPLY","پاسخ کارشناس"
|
||||
@@ -29,6 +30,8 @@ class SMSTemplate(PersianTextNormalizationMixin, models.Model):
|
||||
def __str__(self): return self.get_event_type_display()
|
||||
@classmethod
|
||||
def tags_for_event(cls, event_type):
|
||||
if event_type == "DISPATCH_RESPONSES_OPENED":
|
||||
return []
|
||||
if event_type in {'PAYMENT_NOTICE_CREATED', 'PAYMENT_RECEIPT_APPROVED', 'PAYMENT_RECEIPT_REJECTED'}:
|
||||
from apps.payments.notifications import TAGS
|
||||
return list(TAGS)
|
||||
|
||||
Reference in new issue
Block a user