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
+13 -4
View File
@@ -13,6 +13,7 @@ class ConnectorConfig(BaseModel):
('mail', 'Почта (IMAP/SMTP)'),
('nextcloud', 'Nextcloud'),
('onlyoffice', 'OnlyOffice'),
('asterisk', 'Asterisk AMI'),
]
connector_type = models.CharField(
@@ -41,9 +42,9 @@ class ConnectorConfig(BaseModel):
_('Дополнительные параметры'), default=dict, blank=True,
help_text=_(
'JSON. Nextcloud: verify_ssl, timeout. '
'OnlyOffice: public_url (URL Document Server), '
'callback_base (публичный URL портала для скачивания/сохранения файла), '
'verify_ssl.'
'OnlyOffice: public_url, callback_base, verify_ssl. '
'Asterisk AMI: originate_context (from-internal), '
'sip_channel (PJSIP/{exten}), analog_channel (DAHDI/{exten}), timeout.'
),
)
@@ -62,7 +63,15 @@ class ConnectorConfig(BaseModel):
super().clean()
if self.is_enabled and not self.host:
raise ValidationError({'host': _('Хост обязателен для включённого коннектора')})
if self.is_enabled and not self.token and not self.username:
if self.connector_type == 'asterisk' and self.is_enabled:
errors = {}
if not self.username:
errors['username'] = _('Для AMI нужен логин менеджера')
if not self.password:
errors['password'] = _('Для AMI нужен секрет (password)')
if errors:
raise ValidationError(errors)
elif self.is_enabled and not self.token and not self.username:
raise ValidationError(
_('Для включённого коннектора необходимо указать токен или логин')
)
+32
View File
@@ -3,10 +3,42 @@ import logging
from django.db import models
from core.utils.crypto import encrypt_secret, decrypt_secret
from core.utils.validators import (
normalize_ukrainian_phone,
phone_input_attrs,
validate_ukrainian_phone,
)
logger = logging.getLogger(__name__)
class UkrainianPhoneField(models.CharField):
"""Украинский телефон: в БД всегда +380XXXXXXXXX, в форме — маска."""
default_validators = [validate_ukrainian_phone]
def __init__(self, *args, **kwargs):
kwargs.setdefault('max_length', 20)
super().__init__(*args, **kwargs)
def to_python(self, value):
value = super().to_python(value)
if value in (None, ''):
return value
return normalize_ukrainian_phone(value)
def get_prep_value(self, value):
value = super().get_prep_value(value)
if value in (None, ''):
return value
return normalize_ukrainian_phone(value)
def formfield(self, **kwargs):
from django import forms
kwargs.setdefault('widget', forms.TextInput(attrs=phone_input_attrs()))
return super().formfield(**kwargs)
class EncryptedCharField(models.CharField):
"""CharField, который хранит значение в БД в зашифрованном виде (Fernet).