При разработке и автоматизации бизнес-процессов в среде 1С:Предприятие часто возникает задача программного импорта, обновления или дополнения контактной информации клиентов, партнеров или контрагентов. В современных конфигурациях, таких как 1С:ERP Управление предприятием, 1С:Комплексная автоматизация и 1С:Управление торговлей, подсистема контактной информации реализована на базе Библиотеки стандартных подсистем (БСП). Это накладывает определенные требования к коду и архитектуре решения.
В этой статье мы подробно разберем, как правильно работать с табличной частью контактной информации, проанализируем распространенные ошибки начинающих разработчиков и рассмотрим профессиональный метод заполнения полей телефона с использованием стандартных механизмов БСП.
Рассмотрим ситуацию, с которой часто сталкиваются программисты при первой попытке программной работы с контактной информацией. Задача состоит в том, чтобы на основании уже заполненного строкового поля Представление автоматически заполнить служебные поля НомерТелефона и НомерТелефонаБезКодов в табличной части КонтактнаяИнформация справочника Партнеры.
Начинающие разработчики часто пытаются написать код следующим образом:
Процедура ПартнерПередЗаписьюПроверитьДополнитьКИПередЗаписью(Объект, Отказ) Экспорт
Таблица = Объект.КонтактнаяИнформация.Добавить;
Таблица.Колонки.Добавить("НомерТелефона");
Таблица.Колонки.Добавить("НомерТелефонаБезКодов");
Для каждого Строка Из Таблица Цикл
Строка.НомерТелефона = Объект.КонтактнаяИнформация.Представление;
Строка.НомерТелефонаБезКодов = Объект.КонтактнаяИнформация.Представление;
КонецЦикла;
КонецПроцедуры
Давайте подробно проанализируем, почему этот код совершенно неработоспособен и вызывает ошибки выполнения:
Объект.КонтактнаяИнформация.Добавить() предназначен для добавления новой строки в табличную часть. Он возвращает конкретный объект строки (СтрокаТабличнойЧасти), а не всю коллекцию или таблицу. Присваивать его переменной с именем Таблица — грубая логическая ошибка.Колонки.Добавить(), как это делается в объекте ТаблицаЗначений.Для каждого приводит к ошибке, так как возвращенный объект строки не является коллекцией, по которой можно итерироваться.Объект.КонтактнаяИнформация.Представление не имеет смысла, так как КонтактнаяИнформация — это табличная часть (коллекция строк), и у самой коллекции нет свойства Представление. Обращаться к этому реквизиту можно только у конкретной строки коллекции.Для того чтобы исправить базовую логику кода и заставить программу корректно обойти уже существующие строки контактной информации, необходимо использовать цикл по коллекции строк табличной части объекта. Вот простое решение, которое часто предлагают на форумах в качестве быстрого исправления:
Для каждого ТекСтрока Из Объект.КонтактнаяИнформация Цикл
// Проверяем, что это строка с типом "Телефон"
Если ТекСтрока.Тип = Перечисления.ТипыКонтактнойИнформации.Телефон Тогда
ТекСтрока.НомерТелефона = ТекСтрока.Представление;
ТекСтрока.НомерТелефонаБезКодов = ТекСтрока.Представление;
КонецЕсли;
КонецЦикла;
Этот код действительно решает проблему синтаксических ошибок и корректно перезаписывает реквизиты в цикле. Мы обращаемся к каждой строке ТекСтрока внутри табличной части КонтактнаяИнформация и напрямую копируем строковое представление телефона в служебные поля.
Однако использовать такое решение в реальных рабочих базах категорически не рекомендуется! Ниже мы подробно разберем, почему этот "быстрый" способ является миной замедленного действия для вашей конфигурации.
Современная контактная информация в конфигурациях 1С на базе БСП устроена гораздо сложнее, чем просто набор текстовых строк. Для обеспечения интеграции с мобильными устройствами, IP-телефонией, отправки SMS-сообщений и корректного поиска дублей система требует строгого форматирования данных.
Давайте выясним, к каким критическим проблемам приведет прямое копирование строки представления в поля номеров телефонов:
ЗначенияПолей (в формате XML или JSON). При открытии карточки партнера на управляемой форме система парсит именно это сериализованное поле. Если вы обновили поля НомерТелефона и НомерТелефонаБезКодов вручную, но оставили поле ЗначенияПолей пустым или старым, то при первом же интерактивном переоткрытии и сохранении карточки пользователем ваши внесенные программно телефоны будут затерты пустыми значениями!НомерТелефона должно содержать строго очищенную от лишних символов последовательность цифр (например, 74951234567). Поле НомерТелефонаБезКодов должно содержать только локальный номер абонента (например, 1234567). Если записать туда сырую строку со скобками и дефисами типа "+7 (495) 123-45-67", сломаются стандартные механизмы фильтрации, поиска дублей партнеров по телефонам, а также интеграция со встроенными софтфонами.Для того чтобы сделать заполнение контактной информации надежным и корректным, мы должны задействовать экспортные функции общих модулей БСП. Система сама умеет разбирать текстовую строку на составляющие (код страны, код города, номер), приводить её к каноническому виду и формировать правильный XML или JSON для записи в базу данных.
Ниже представлен профессиональный шаблон кода для обработчика подписки на событие ПередЗаписью справочника Партнеры:
Процедура ПартнерПередЗаписьюПроверитьДополнитьКИПередЗаписью(Объект, Отказ) Экспорт
// Предотвращаем выполнение кода при обмене данными, если это необходимо
Если Объект.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
ТипТелефон = Перечисления.ТипыКонтактнойИнформации.Телефон;
Для Каждого ТекСтрока Из Объект.КонтактнаяИнформация Цикл
// Обрабатываем только телефонные номера с заполненным текстовым представлением
Если ТекСтрока.Тип = ТипТелефон И ЗначениеЗаполнено(ТекСтрока.Представление) Тогда
// Запускаем процесс парсинга и нормализации, только если служебные поля еще не заполнены
Если ПустаяСтрока(ТекСтрока.НомерТелефона) Или ПустаяСтрока(ТекСтрока.ЗначенияПолей) Тогда
Попытка
// 1. Используем БСП для разбора строки представления в структурированный XDTO-объект
ОбъектXDTO = УправлениеКонтактнойИнформациейСлужебный.КонтактнаяИнформацияXDTOПоПредставлению(
ТекСтрока.Представление,
ТипТелефон
);
// 2. Сериализуем полученный объект в XML-строку для записи в поле "ЗначенияПолей"
ЗначенияПолейСтрокой = УправлениеКонтактнойИнформациейСлужебный.КонтактнаяИнформацияXDTOВXML(ОбъектXDTO);
ТекСтрока.ЗначенияПолей = ЗначенияПолейСтрокой;
// 3. Извлекаем нормализованные и очищенные составляющие телефона
ДеталиТелефона = УправлениеКонтактнойИнформацией.СведенияОТелефоне(ЗначенияПолейСтрокой);
// 4. Заполняем реквизиты строки табличной части "чистыми" данными
// НомерТелефона должен состоять только из цифр: КодСтраны + КодГорода + ЛокальныйНомер
ТекСтрока.НомерТелефона = ДеталиТелефона.КодСтраны + ДеталиТелефона.КодГорода + ДеталиТелефона.НомерТелефона;
// Записываем только локальную часть номера
ТекСтрока.НомерТелефонаБезКодов = ДеталиТелефона.НомерТелефона;
Исключение
// В случае непредвиденных ошибок парсинга (например, некорректный формат текста) фиксируем событие в журнале
ИнформационнаяБаза = "ПрограммноеЗаполнениеКИ";
ЗаписьЖурналаРегистрации(ИнформационнаяБаза,
УровеньЖурналаРегистрации.Предупреждение,
Объект.Метаданные(),
Объект.Ссылка,
"Не удалось корректно распознать и нормализовать номер телефона: " + ТекСтрока.Представление + ". Ошибка: " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Давайте пошагово проанализируем, как работает предложенный нами надежный алгоритм:
Объект.ОбменДанными.Загрузка. Если происходит синхронизация данных (РИБ или интеграция), этот код не должен мешать импорту объектов, чтобы избежать конфликтов блокировок и замедления работы.Перечисления.ТипыКонтактнойИнформации.Телефон."+7 (999) 123-45-67") и превращает его во внутренний XDTO-объект, разбивая на логические сегменты (страна, город, номер, добавочный).ЗначенияПолей. Теперь при интерактивном открытии карточки партнера на форме отобразятся корректные данные, и они не затрутся пустыми значениями при ручном сохранении.Для внедрения данного механизма рекомендуется следовать стандартной методике разработки в среде 1С:
_ПартнерыСобытия) или используйте существующий нетиповой модуль вашей организации. Обязательно установите у него свойства Сервер, Вызов сервера и отключите Глобальный._ПартнерыПередЗаписью.СправочникОбъект.Партнеры.ПередЗаписью.Если вы стремитесь сохранить конфигурацию на полной поддержке без включения возможности изменения, наилучшим решением будет реализация данного функционала через механизм Расширения конфигурации. Это современный стандарт разработки в экосистеме 1С.
Для реализации этой логики в расширении выполните следующие шаги:
Партнеры в расширение.Партнеры.ПередЗаписью с аннотацией &Перед. Это позволит выполнить ваш код непосредственно перед стандартной обработкой записи объекта.Пример кода в модуле объекта расширения:
&Перед("ПередЗаписью")
Процедура _Расш_ПередЗаписью(Отказ, Замещение)
// Вызываем нашу процедуру заполнения контактной информации
// Передаем "ЭтотОбъект", так как мы находимся внутри модуля объекта справочника
_ПартнерыСобытияСервер.ПартнерПередЗаписьюПроверитьДополнитьКИПередЗаписью(ЭтотОбъект, Отказ);
КонецПроцедуры
Этот метод гарантирует, что при обновлении основной конфигурации разработчикам не придется повторно объединять код подписок на события. Все доработки будут изолированы в расширении, что существенно упрощает администрирование и поддержку информационной системы.