Files
2026-10-27 11:49:09 +03:30

108 lines
4.8 KiB
Python

"""معنای «زمان‌بندی» برای هر نوع پیامک.
هر نوع پیامک زمان‌بندی متفاوتی دارد و نمی‌شود همه را زیر یک برچسب «روز قبل از سررسید»
جمع کرد. این ماژول تنها مرجع این معناست: هم پنل قالب‌ها بر اساسش فیلد درست را نشان
می‌دهد، هم اسکنر بر اساسش تصمیم می‌گیرد. هر نوع دقیقاً به یک فیلد زمانی نگاشت می‌شود
(``field``) تا داده هرگز ناهمخوان نشود؛ بقیهٔ فیلدهای زمانی صفر/خالی می‌مانند.
"""
from ..models import SMSType
NONE = "none"
BEFORE_DUE = "before_due"
AFTER_DUE = "after_due"
PERIOD_MONTHS = "period_months"
# پیش‌فرض دورهٔ بازدید (ماه) وقتی مدیر مقداری در قالب نگذاشته باشد.
DEFAULT_VISIT_MONTHS = 9
TIMING = {
SMSType.WELCOME: {
"kind": NONE,
"field": None,
"note": "به‌محض ثبت مشتری و بدون تأخیر ارسال می‌شود؛ تنظیم زمانی ندارد.",
},
SMSType.SURVEY: {
"kind": NONE,
"field": None,
"note": "بی‌درنگ پس از ثبت فاکتور ارسال می‌شود؛ تنظیم زمانی ندارد.",
},
SMSType.CHEQUE_REGISTRATION: {
"kind": BEFORE_DUE,
"field": "offset_days",
"label": "چند روز قبل از سررسید چک",
"default": 1,
"note": "این تعداد روز پیش از سررسید چکِ پرداخت‌نشده، پیامک یادآوری "
"تأمین موجودی با مبلغ چک ارسال می‌شود.",
},
SMSType.VISIT: {
"kind": PERIOD_MONTHS,
"field": "repeat_interval_months",
"label": "هر چند ماه یک‌بار",
"default": DEFAULT_VISIT_MONTHS,
"note": "برای مشتریانی که از این مدت بازدید نشده‌اند ارسال می‌شود.",
},
SMSType.VISIT_CREATED: {
"kind": NONE,
"field": None,
"note": "به‌محض ثبت «اقدام از نوع بازدید» ارسال می‌شود؛ تنظیم زمانی ندارد.",
},
SMSType.INSTALL_CREATED: {
"kind": NONE,
"field": None,
"note": "به‌محض ثبت «اقدام از نوع نصب» ارسال می‌شود؛ تنظیم زمانی ندارد.",
},
SMSType.PAYMENT_THANKS: {
"kind": NONE,
"field": None,
"note": "بی‌درنگ پس از ثبت پرداخت (سند وصول/بستانکار) ارسال می‌شود؛ تنظیم زمانی ندارد.",
},
SMSType.NESIYE_DUE: {
"kind": BEFORE_DUE,
"field": "offset_days",
"label": "چند روز قبل از سررسید نسیه",
"default": 0,
"note": "پنجرهٔ یادآوری از «سررسید − این تعداد روز» باز می‌شود و تا سررسید ادامه دارد.",
},
}
def timing_for(sms_type):
"""اطلاعات زمان‌بندی یک نوع پیامک (با پیش‌فرض بی‌خطر برای انواع ناشناخته)."""
return TIMING.get(sms_type, {"kind": NONE, "field": None, "note": ""})
def apply_timing(template, value):
"""فیلد زمانیِ متناسب با نوع قالب را می‌نشاند و بقیه را صفر/خالی می‌کند.
``value`` عدد صحیحِ واردشده از فرم است (یا ``None``). این کار تضمین می‌کند صرف‌نظر از
اینکه فرم چه بفرستد، فقط یک فیلد زمانی برای هر قالب معنا داشته باشد.
"""
info = timing_for(template.sms_type)
field = info["field"]
template.offset_days = 0
template.repeat_interval_days = None
template.repeat_interval_months = None
if field == "offset_days":
template.offset_days = value if value is not None else info["default"]
elif field == "repeat_interval_days":
template.repeat_interval_days = value if value else info["default"]
elif field == "repeat_interval_months":
template.repeat_interval_months = value if value else None
return template
def current_timing_value(template):
"""مقدار فعلی فیلد زمانیِ متناسب (برای نمایش در فرم).
اگر مقدار خالی باشد، پیش‌فرض همان نوع برگردانده می‌شود تا فیلدِ تنظیم‌نشده هم در
فرم دیده شود و مدیر بتواند مقدارش را تغییر دهد.
"""
info = timing_for(template.sms_type)
field = info["field"]
if field is None:
return None
value = getattr(template, field)
if value is None:
value = info.get("default")
return value