"""یکسان‌سازی شماره‌های اصلی + زمان‌بندی اختصاصی هر نوع پیامک. * «شمارهٔ اصلی» از رکورد ``PhoneNumber`` به خودِ ``Customer`` منتقل می‌شود و با مقدارِ نرمال‌شدهٔ شماره ذخیره می‌گردد تا با افزودن/حذف/ویرایش مداوم مشتریان پایدار بماند. * فیلد زمانی هر قالب با معنای واقعی همان نوع هم‌راستا می‌شود (مثلاً نظرسنجی = ۳ روز بعد از صدور فاکتور، و یادآوری ثبت چک = هر ۳ روز تا ثبت اطلاعات چک). """ from django.db import migrations, models def _normalize(raw): """نرمال‌سازی شماره (بدون وابستگی به کد اپ، برای اجرای امن روی دادهٔ قدیمی).""" from integrations.sms.normalize import normalize_mobile return normalize_mobile(raw) def backfill_primary_phones(apps, schema_editor): """پرچم‌های ``is_primary`` موجود را به مقدار نرمال‌شده روی مشتری منتقل می‌کند.""" PhoneNumber = apps.get_model("admin_panel", "PhoneNumber") for customer_id, phone_number in ( PhoneNumber.objects.filter(is_primary=True) .values_list("customer_id", "phone_number") ): normalized = _normalize((phone_number or "").strip()) if not normalized: continue customer = apps.get_model("admin_panel", "Customer").objects.filter(pk=customer_id).first() if customer is None: continue current = [p for p in (customer.sms_primary_phones or "").split(",") if p] if normalized in current: continue current.append(normalized) customer.sms_primary_phones = ",".join(current) customer.save(update_fields=["sms_primary_phones"]) def normalize_template_timing(apps, schema_editor): """مقادیر زمانیِ بی‌معنی را با معنای درست هر نوع پیامک هم‌راستا می‌کند. فقط مقادیری که برای آن نوع معنا ندارند اصلاح می‌شوند؛ تنظیمات معنادارِ مدیر دست‌نخورده می‌ماند. """ SMSTemplate = apps.get_model("admin_panel", "SMSTemplate") # نظرسنجی: «۳ روز بعد از صدور فاکتور» (مقدار پیشین ۲ بود). SMSTemplate.objects.filter(sms_type="survey", offset_days=2).update(offset_days=3) # یادآوری ثبت چک: فاصله تکرار ۳ روز و بدون offset سررسید. for template in SMSTemplate.objects.filter(sms_type="cheque_registration"): template.offset_days = 0 if not template.repeat_interval_days: template.repeat_interval_days = 3 template.save(update_fields=["offset_days", "repeat_interval_days"]) # یادآوری بازدید: دورهٔ پیش‌فرض ۹ ماه (قابل تغییر از پنل). SMSTemplate.objects.filter( sms_type="visit", repeat_interval_months__isnull=True, ).update(repeat_interval_months=9) # متن «یادآوری ثبت چک» به معنای جدید (ثبت اطلاعات چک) هم‌راستا می‌شود. SMSTemplate.objects.filter(sms_type="cheque_registration").update( title="یادآوری ثبت اطلاعات چک", text="{name} عزیز، برای چک با سررسید {date} هنوز اطلاعات ثبت چک " "(شمارهٔ صیادی) در سامانه ثبت نشده است. لطفاً نسبت به ثبت آن اقدام فرمایید.", ) # سایر انواع: هیچ فیلد زمانی نباید مقدار داشته باشد. SMSTemplate.objects.filter( sms_type__in=["welcome", "debt_due", "custom"], ).update(offset_days=0, repeat_interval_days=None, repeat_interval_months=None) def noop(apps, schema_editor): pass class Migration(migrations.Migration): dependencies = [ ("admin_panel", "0060_sms_permission_label"), ] operations = [ migrations.AddField( model_name="customer", name="sms_primary_phones", field=models.CharField( blank=True, default="", max_length=100, verbose_name="شماره‌های اصلی پیامک", help_text="شماره‌های نرمال‌شده (+98…) جدا‌شده با کاما؛ خالی = شمارهٔ اول مشتری.", ), ), # ترتیب مهم است: ابتدا مقادیر فعلی خوانده و منتقل می‌شوند، سپس ستون قدیمی حذف می‌گردد. migrations.RunPython(backfill_primary_phones, noop), migrations.RemoveField(model_name="phonenumber", name="is_primary"), migrations.AddField( model_name="smstemplate", name="repeat_interval_months", field=models.PositiveSmallIntegerField( blank=True, null=True, verbose_name="دورهٔ تکرار (ماه)", help_text="برای «یادآوری بازدید»: چند ماه یک‌بار بازدید تکرار شود. " "خالی = پیش‌فرض سامانه (۹ ماه).", ), ), migrations.AlterField( model_name="smstemplate", name="offset_days", field=models.PositiveSmallIntegerField( default=0, verbose_name="فاصلهٔ زمانی (روز)", help_text="معنای این عدد برای هر نوع پیامک متفاوت است و در پنل توضیح داده " "می‌شود؛ مثلاً «چند روز قبل از سررسید» یا «چند روز بعد از صدور فاکتور».", ), ), migrations.RunPython(normalize_template_timing, noop), ]