This commit is contained in:
2026-09-04 13:05:54 +03:30
parent 463b812e3e
commit 17082a5ea9
43 changed files with 5663 additions and 285 deletions
+92
View File
@@ -0,0 +1,92 @@
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]