telephony: автоформат +380, тип линии SIP/АТС и звонок через Asterisk AMI.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-13 14:52:09 +03:00
parent cc62324b69
commit 5d06d4d3ce
42 changed files with 1158 additions and 57 deletions
+52 -2
View File
@@ -5,10 +5,60 @@ from django.core.validators import EmailValidator
from django.utils.translation import gettext_lazy as _
PHONE_PLACEHOLDER = '+380 (__) ___-__-__'
UA_PHONE_CANONICAL_RE = re.compile(r'^\+380\d{9}$')
def phone_input_attrs(**extra):
"""Единые атрибуты полей телефона в портале и админке."""
attrs = {
'class': 'input js-phone',
'placeholder': PHONE_PLACEHOLDER,
'inputmode': 'tel',
'autocomplete': 'tel',
}
extra_class = extra.pop('class', None)
attrs.update(extra)
if extra_class:
attrs['class'] = extra_class
return attrs
def normalize_ukrainian_phone(value) -> str:
"""Приводит украинский номер к канону +380XXXXXXXXX.
Понимает 050…, 380…, +38 (050) …, 9 цифр без кода. Если это не номер —
возвращает исходную строку (валидатор уже покажет ошибку).
"""
if value is None:
return ''
text = str(value).strip()
if not text:
return ''
digits = re.sub(r'\D', '', text)
if not digits:
return text
if digits.startswith('380'):
ua = digits[:12]
elif digits.startswith('80'):
ua = ('3' + digits)[:12]
elif digits.startswith('0'):
ua = ('38' + digits)[:12]
elif len(digits) <= 9:
ua = ('380' + digits)[:12]
else:
return text
if len(ua) != 12 or not ua.startswith('380'):
return text
return f'+{ua}'
def validate_ukrainian_phone(value: str) -> None:
"""Валидация украинских номеров телефона"""
pattern = r'^\+380\d{9}$'
if not re.match(pattern, value):
if value in (None, ''):
return
canonical = normalize_ukrainian_phone(value)
if not UA_PHONE_CANONICAL_RE.match(canonical):
raise ValidationError(
_('Номер телефона должен быть в формате +380XXXXXXXXX'),
code='invalid_phone'