Почему ломается маска телефона +7 (999) 999-99-99 в обычных формах 1С и как это исправить?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов на обычных формах (ОФ) в платформе «1С:Предприятие» разработчики часто сталкиваются с крайне странным поведением полей ввода, для которых задана маска. Классический пример — поле ввода номера телефона с маской +7 (999) 999-99-99. Если заполнять это поле интерактивно (руками), всё работает отлично. Но как только мы пытаемся заполнить его программно, платформа начинает вести себя непредсказуемо.

Давайте подробно разберем, почему при программном присвоении номера "9771234567" мы получаем искаженный результат "+7 (971) 234-56-7 ", в то время как номер "9111234567" отображается абсолютно корректно. Мы проанализируем внутренние механизмы платформы, выявим причину бага и рассмотрим три надежных способа обхода этой проблемы.

Выясняем причину: техническая суть бага платформы

Когда мы программно присваиваем значение свойству поля ввода на форме (например, в обработчике ПриОткрытии), платформа запускает внутренний парсер сопоставления переданной строки с заданной маской. Этот процесс происходит символ за символом.

В нашей маске +7 (999) 999-99-99 присутствуют символы двух типов:

  1. Спецсимволы маски (в данном случае это девятки 9, каждая из которых ожидает ввода любой цифры).
  2. Литералы — статические символы оформления, которые выводятся «как есть» (знак +, скобки ( ), дефисы -, пробелы и цифра 7 в префиксе).

Проанализируем пошагово, как алгоритм платформы обрабатывает строку "9771234567":

  1. Платформа берет первый символ входной строки — 9. Она ищет для него первый свободный спецсимвол в маске. Это первая девятка внутри скобок. Символ успешно записывается: получаем +7 (9..) ....
  2. Берется второй символ входной строки — 7. И вот тут срабатывает ошибочная логика выравнивания платформы 1С. Алгоритм видит, что в самом начале маски (в префиксе) содержится жестко заданный литерал — символ 7. Парсер ошибочно предполагает, что эта семерка из нашей входной строки соответствует статическому префиксу маски, и просто пропускает («съедает») её, считая, что префикс уже пройден.
  3. Берется третий символ строки — следующая 7. Платформа помещает её во второй спецсимвол маски внутри скобок. На этом этапе мы получаем: +7 (97.) ....
  4. Все последующие символы (1234567) без проблем распределяются по оставшимся спецсимволам маски.
  5. В итоге, поскольку одна семерка была ошибочно отброшена на шаге 2, в самом конце заполненного шаблона не хватает одного символа. На форме отображается урезанная строка: +7 (971) 234-56-7 .

Почему же номер "9111234567" обрабатывается правильно? Все очень просто. В начале этой строки (на второй и третьей позициях) нет цифры 7. Внутреннему парсеру не за что «зацепиться», ложного совпадения с литералом префикса маски не происходит, и все 10 цифр корректно распределяются по своим местам.

Ниже мы подробно разберем три практических метода решения этой проблемы, чтобы вам не пришлось переписывать архитектуру хранения данных в вашей конфигурации.

---

Решение 1. Программная предобработка строки перед выводом на форму

Самый простой и быстрый способ победить некорректный парсинг — передавать в поле ввода строку, которая уже содержит префикс, совпадающий с литералами маски. Если платформа увидит на входе символы префикса, она сопоставит их со статическим префиксом маски, не затрагивая полезные цифры номера.

Вместо того чтобы напрямую писать:

ПолеВвода1 = "9771234567"; // Приведет к багу

Мы должны дополнить строку префиксом. Достаточно добавить +7 в начало строки, либо полностью привести её к формату маски:


ПолеВвода1 = "+79771234567"; // Отработает корректно
// Или еще более надежный вариант:
ПолеВвода1 = "+7 (977) 123-45-67";

Давайте напишем универсальную вспомогательную функцию, которая будет очищать строку от лишних символов и безопасно приводить любой 10-значный номер к формату, который без ошибок воспримет маска платформы:


Функция ПодготовитьТелефонДляМаски(Знач ВходнойНомер) Экспорт
    
    // Очищаем строку, оставляя только цифры
    ЧистыйНомер = "";
    Для Позиция = 1 По СтрДлина(ВходнойНомер) Цикл
        Символ = Сред(ВходнойНомер, Позиция, 1);
        Если Найти("0123456789", Символ) > 0 Тогда
            ЧистыйНомер = ЧистыйНомер + Символ;
        КонецЕсли;
    КонецЦикла;
    
    // Если передан 10-значный номер (например, 9771234567)
    Если СтрДлина(ЧистыйНомер) = 10 Тогда
        Возврат "+7" + ЧистыйНомер;
    // Если номер уже начинается с 7 или 8 и имеет длину 11 символов
    ИначеЕсли СтрДлина(ЧистыйНомер) = 11 И (Лев(ЧистыйНомер, 1) = "7" Или Лев(ЧистыйНомер, 1) = "8") Тогда
        Возврат "+7" + Сред(ЧистыйНомер, 2);
    КонецЕсли;
    
    Возврат ВходнойНомер; // Возвращаем как есть, если формат не распознан
    
КонецФункции

Теперь в обработчике формы ПриОткрытии достаточно вызвать эту функцию:


Процедура ПриОткрытии()
    НомерИзБазы = "9771234567"; // Значение, полученное из реквизита
    ЭлементыФормы.ПолеВвода1.Значение = ПодготовитьТелефонДляМаски(НомерИзБазы);
КонецПроцедуры

---

Решение 2. Вынос префикса за пределы маски ввода

Если вы хотите раз и навсегда исключить влияние подобных багов платформы на логику приложения, рекомендуется отказаться от использования символа 7 в качестве жесткого литерала внутри маски. Мы можем визуально сохранить интерфейс, разделив статическую и динамическую части поля.

Выполним следующие шаги по настройке формы:

  1. Откройте свойства вашего поля ввода в конфигураторе и измените маску, убрав из неё префикс +7. Новая маска должна выглядеть так: (999) 999-99-99.
  2. На форме, непосредственно слева от поля ввода, разместите элемент декорации Надпись с текстом +7.
  3. Пододвиньте надпись вплотную к полю ввода, чтобы визуально они воспринимались пользователем как единое целое.

При таком подходе в базе данных вы храните чистый 10-значный номер без кода страны (например, "9771234567"). При программном выводе значения на форму платформа больше не будет спотыкаться о префикс, так как в маске (999) 999-99-99 отсутствуют статические цифры. Поведение поля ввода станет стабильным и предсказуемым.

---

Решение 3. Отказ от маски в пользу программного форматирования

Стандартный механизм масок в 1С имеет множество ограничений. Он плохо справляется со вставкой номеров из буфера обмена (копированием/вставкой), жестко привязывает пользователя к одному формату длины номера и часто вызывает раздражение при попытке стереть или отредактировать часть номера.

Проанализируем альтернативный вариант, который часто применяется в современных конфигурациях (в том числе в Библиотеке Стандартных Подсистем — БСП): полный отказ от маски на уровне свойств элемента формы и переход на программное форматирование «на лету».

Для реализации этого метода выполните следующие действия:

  1. Удалите маску из свойств поля ввода на форме. Поле должно стать обычным текстовым полем.
  2. В базе данных храните номера телефонов в виде чистых строк, содержащих исключительно цифры. Это значительно упростит последующую интеграцию с сервисами IP-телефонии и SMS-рассылок.
  3. Напишите обработчик события ПриИзменении для вашего поля ввода, который будет форматировать текст.

Рассмотрим пример программной реализации форматирования ввода пользователя:


Процедура ПолеВвода1ПриИзменении(Элемент)
    
    // Получаем введенный пользователем текст
    ВходнойТекст = Элемент.Значение;
    
    // Очищаем от всех символов, кроме цифр
    ЧистыйНомер = "";
    Для Позиция = 1 По СтрДлина(ВходнойТекст) Цикл
        Символ = Сред(ВходнойТекст, Позиция, 1);
        Если Найти("0123456789", Символ) > 0 Тогда
            ЧистыйНомер = ЧистыйНомер + Символ;
        КонецЕсли;
    КонецЦикла;
    
    // Форматируем строку под стандартный шаблон телефона
    ДлинаНомера = СтрДлина(ЧистыйНомер);
    Если ДлинаНомера = 10 Тогда
        // Форматируем 9771234567 -> +7 (977) 123-45-67
        Элемент.Значение = "+7 (" + Лев(ЧистыйНомер, 3) + ") " 
            + Сред(ЧистыйНомер, 4, 3) + "-" 
            + Сред(ЧистыйНомер, 7, 2) + "-" 
            + Сред(ЧистыйНомер, 9, 2);
    ИначеЕсли ДлинаНомера = 11 И (Лев(ЧистыйНомер, 1) = "7" Или Лев(ЧистыйНомер, 1) = "8") Тогда
        // Форматируем 89771234567 -> +7 (977) 123-45-67
        Элемент.Значение = "+7 (" + Сред(ЧистыйНомер, 2, 3) + ") " 
            + Сред(ЧистыйНомер, 5, 3) + "-" 
            + Сред(ЧистыйНомер, 8, 2) + "-" 
            + Сред(ЧистыйНомер, 10, 2);
    КонецЕсли;
    
КонецПроцедуры

Используя этот подход, мы избавляемся от платформенных багов сопоставления маски, предоставляем пользователю удобный интерфейс для ввода и копирования номеров телефонов в любом формате, а также сохраняем данные в чистоте и порядке.

← На главную