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]
|