93 lines
3.9 KiB
Python
93 lines
3.9 KiB
Python
|
|
from django import forms
|
|||
|
|
from django.utils.translation import gettext_lazy as _
|
|||
|
|
|
|||
|
|
from apps.account.models import User
|
|||
|
|
|
|||
|
|
|
|||
|
|
def normalize_iran_phone(raw: str) -> str:
|
|||
|
|
"""۰۹۱۲…، +۹۸۹۱۲…، ۰۰۹۸۹۱۲… و ارقام فارسی را به شکلِ یکّهی 09xxxxxxxxx میبرد.
|
|||
|
|
|
|||
|
|
ادمین شماره را از هرجایی کپی میکند (پیامک، اکسل، پیامرسان)؛ اگر شکلِ
|
|||
|
|
ورودی را نرمال نکنیم، «کاربر پیدا نشد» میگیرد در حالی که کاربر هست.
|
|||
|
|
"""
|
|||
|
|
digits = {
|
|||
|
|
"۰": "0", "۱": "1", "۲": "2", "۳": "3", "۴": "4",
|
|||
|
|
"۵": "5", "۶": "6", "۷": "7", "۸": "8", "۹": "9",
|
|||
|
|
"٠": "0", "١": "1", "٢": "2", "٣": "3", "٤": "4",
|
|||
|
|
"٥": "5", "٦": "6", "٧": "7", "٨": "8", "٩": "9",
|
|||
|
|
}
|
|||
|
|
value = "".join(digits.get(ch, ch) for ch in (raw or "").strip())
|
|||
|
|
value = "".join(ch for ch in value if ch.isdigit() or ch == "+")
|
|||
|
|
value = value.lstrip("+")
|
|||
|
|
if value.startswith("0098"):
|
|||
|
|
value = value[4:]
|
|||
|
|
elif value.startswith("98") and len(value) == 12:
|
|||
|
|
value = value[2:]
|
|||
|
|
if value.startswith("9") and len(value) == 10:
|
|||
|
|
value = "0" + value
|
|||
|
|
return value
|
|||
|
|
|
|||
|
|
|
|||
|
|
class GemGiftForm(forms.Form):
|
|||
|
|
"""هدیهدادنِ جم به یک یا چند شماره — از پنل ادمین.
|
|||
|
|
|
|||
|
|
عمداً روی *شماره* کار میکند نه روی انتخابِ کاربر از لیست: ادمین معمولا
|
|||
|
|
شماره را دارد (از پشتیبانی، از یک کمپین)، نه شناسهی کاربر را.
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
phones = forms.CharField(
|
|||
|
|
label=_("شمارهها"),
|
|||
|
|
widget=forms.Textarea(attrs={"rows": 5, "dir": "ltr"}),
|
|||
|
|
help_text=_(
|
|||
|
|
"هر شماره در یک خط (یا با کاما جدا کن). "
|
|||
|
|
"شکلهای 09123456789 و +989123456789 هر دو قبول است."
|
|||
|
|
),
|
|||
|
|
)
|
|||
|
|
amount = forms.IntegerField(
|
|||
|
|
label=_("تعداد جم"),
|
|||
|
|
min_value=1,
|
|||
|
|
help_text=_("به هر شماره همین مقدار جم اضافه میشود."),
|
|||
|
|
)
|
|||
|
|
note = forms.CharField(
|
|||
|
|
label=_("علت / یادداشت"),
|
|||
|
|
max_length=255,
|
|||
|
|
required=False,
|
|||
|
|
help_text=_("در تاریخچهی تراکنشهای جم ذخیره میشود — مثلا «جبران خطای پرداخت»."),
|
|||
|
|
)
|
|||
|
|
create_missing = forms.BooleanField(
|
|||
|
|
label=_("اگر کاربر وجود ندارد، ساخته شود"),
|
|||
|
|
required=False,
|
|||
|
|
help_text=_(
|
|||
|
|
"برای هدیهی پیش از ثبتنام. کاربر با همان شماره ساخته میشود و "
|
|||
|
|
"جم موقع اولین ورودش آماده است."
|
|||
|
|
),
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def clean_phones(self) -> list[str]:
|
|||
|
|
raw = self.cleaned_data["phones"].replace(",", "\n")
|
|||
|
|
numbers, seen = [], set()
|
|||
|
|
invalid = []
|
|||
|
|
for line in raw.splitlines():
|
|||
|
|
if not line.strip():
|
|||
|
|
continue
|
|||
|
|
phone = normalize_iran_phone(line)
|
|||
|
|
if len(phone) != 11 or not phone.startswith("09"):
|
|||
|
|
invalid.append(line.strip())
|
|||
|
|
continue
|
|||
|
|
if phone not in seen:
|
|||
|
|
seen.add(phone)
|
|||
|
|
numbers.append(phone)
|
|||
|
|
if invalid:
|
|||
|
|
raise forms.ValidationError(
|
|||
|
|
_("این شمارهها معتبر نیستند: %(bad)s") % {"bad": "، ".join(invalid)}
|
|||
|
|
)
|
|||
|
|
if not numbers:
|
|||
|
|
raise forms.ValidationError(_("حداقل یک شماره وارد کن."))
|
|||
|
|
return numbers
|
|||
|
|
|
|||
|
|
def existing_and_missing(self) -> tuple[list[User], list[str]]:
|
|||
|
|
phones = self.cleaned_data["phones"]
|
|||
|
|
users = list(User.objects.filter(phone__in=phones))
|
|||
|
|
found = {u.phone for u in users}
|
|||
|
|
return users, [p for p in phones if p not in found]
|