###
При разработке внешних печатных форм и обработок в среде «1С:Предприятие 8.3» разработчики часто сталкиваются с задачей вывода контактной информации (например, номеров телефонов или адресов) физических лиц. Конфигурации, построенные на современных версиях БСП (Библиотеки стандартных подсистем), таких как «1С:Бухгалтерия предприятия», «1С:Зарплата и управление персоналом» и другие, используют динамическое построение интерфейса для работы с контактной информацией. Из-за этого табличная часть контактной информации отсутствует на форме справочника в конфигураторе, что вызывает трудности у начинающих разработчиков.
В этой статье мы подробно разберем, как правильно получить номер телефона физического лица, рассмотрим несколько эффективных способов решения этой задачи (с помощью встроенного программного интерфейса БСП и посредством прямых запросов к базе данных), а также изучим профессиональные приемы отладки внешних печатных форм.
Если мы откроем форму справочника ФизическиеЛица в конфигураторе, то не увидим там привычных полей для ввода адресов и телефонов. Это связано с тем, что в современных конфигурациях за ведение контактной информации отвечает подсистема БСП «Контактная информация». Все элементы управления (поля ввода, кнопки, группы) создаются программно на лету в режиме выполнения (в модуле формы).
Физически же все эти данные хранятся либо в табличной части КонтактнаяИнформация самого справочника ФизическиеЛица, либо в отдельном табличном регистре сведений СоставКонтактнойИнформации (в зависимости от версии БСП). Поэтому мы можем спокойно получать эти данные как программным путем, так и с помощью запросов.
Использование готового программного интерфейса БСП — это наиболее надежный и простой способ. Он гарантирует, что ваш код продолжит работать даже после масштабного обновления конфигурации, когда структура табличных частей базы данных может измениться.
Для быстрого получения представления контактной информации (например, мобильного телефона) конкретного физического лица мы можем использовать функцию общего модуля УправлениеКонтактнойИнформацией. Рассмотрим пример кода:
ФизическоеЛицо = СсылкаНаФизЛицо; // Ваша ссылка на элемент справочника ФизическиеЛица
ВидТелефона = Справочники.ВидыКонтактнойИнформации.ТелефонМобильныйФизическиеЛица;
ТелефонПредставление = УправлениеКонтактнойИнформацией.ПредставлениеContactнойИнформацииОбъекта(ФизическоеЛицо, ВидТелефона);
Этот метод возвращает готовую отформатированную строку, которую можно сразу передавать в параметр макета печатной формы. Если вам нужно получить рабочий или домашний телефон, достаточно указать соответствующий предопределенный элемент справочника ВидыКонтактнойИнформации, например, ТелефонРабочийФизическиеЛица или ТелефонДомашнийФизическиеЛица.
Если нам необходимо получить контактную информацию сразу по списку или массиву физических лиц (что часто требуется при формировании групповых печатных форм для оптимизации производительности), вызывать функцию в цикле крайне не рекомендуется. В этом случае мы соберем все ссылки в массив и воспользуемся пакетным методом:
МассивФизЛиц = Новый Массив;
// Заполняем массив ссылками на физических лиц
МассивФизЛиц.Добавить(Ссылка1);
МассивФизЛиц.Добавить(Ссылка2);
// Получаем таблицу контактной информации для всех объектов одним запросом внутри БСП
ТаблицаКИ = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъектов(
МассивФизЛиц,
Перечисления.ТипыКонтактнойИнформации.Телефон
);
// Теперь мы можем быстро находить нужные телефоны в полученной таблице значений
Для Каждого СтрокаКИ Из ТаблицаКИ Цикл
Если СтрокаКИ.Вид = Справочники.ВидыКонтактнойИнформации.ТелефонМобильныйФизическиеЛица Тогда
ФизЛицо = СтрокаКИ.Объект;
НомерТелефона = СтрокаКИ.Представление;
// Выполняем заполнение параметров макета печатной формы
КонецЕсли;
КонецЦикла;
Полученная таблица значений ТаблицаКИ содержит очень полезные колонки:
Бывают ситуации, когда обращение к программным методам БСП затруднено (например, при формировании сложного отчета на СКД или внутри одного большого запроса для печатной формы, где не хочется плодить лишний программный код обработки данных). В таком случае мы можем обратиться напрямую к табличной части КонтактнаяИнформация справочника ФизическиеЛица.
Рассмотрим пример простого и быстрого запроса для получения мобильного телефона конкретного физического лица:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ФизЛицаКИ.Представление КАК НомерТелефона
|ИЗ
| Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ФизЛицаКИ
|ГДЕ
| ФизЛицаКИ.Ссылка = &ФизическоеЛицо
| И ФизЛицаКИ.Вид = ЗНАЧЕНИЕ(Справочник.ВидыКонтактнойИнформации.ТелефонМобильныйФизическиеЛица)";
Запрос.УстановитьПараметр("ФизическоеЛицо", СсылкаНаФизическоеЛицо);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ТелефонДляПечати = Выборка.НомерТелефона;
Иначе
ТелефонДляПечати = "";
КонецЕсли;
Обратите внимание на важный нюанс при использовании левых соединений в сложных запросах. Поле Представление в базе данных может иметь неограниченную длину. При связывании таблиц это может привести к значительному снижению производительности СУБД. Чтобы оптимизировать запрос и избежать потенциальных ошибок, рекомендуется ограничивать длину строки с помощью конструкции ВЫРАЗИТЬ:
Запрос.Текст =
"ВЫБРАТЬ
| ФизическиеЛица.Ссылка КАК ФизЛицо,
| ФизическиеЛица.Наименование КАК ФИО,
| ВЫРАЗИТЬ(ФизЛицаКИ.Представление КАК СТРОКА(100)) КАК НомерТелефона
|ИЗ
| Справочник.ФизическиеЛица КАК ФизическиеЛица
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ФизическиеЛица.ContactнаяИнформация КАК ФизЛицаКИ
| ПО ФизЛицаКИ.Ссылка = ФизическиеЛица.Ссылка
| И ФизЛицаКИ.Вид = ЗНАЧЕНИЕ(Справочник.ВидыКонтактнойИнформации.ТелефонМобильныйФизическиеЛица)
|ГДЕ
| ФизическиеЛица.Ссылка В (&МассивФизЛиц)";
Важное предупреждение: Если у одного физического лица по какой-то причине будет заполнено два мобильных телефона одного и того же вида контактной информации, левое соединение в запросе приведет к дублированию (задвоению) строк в результирующей таблице. Использование методов БСП УправлениеКонтактнойИнформацией защищает разработчика от этой проблемы, так как корректно сворачивает дублирующиеся данные.
Также разберем распространенную ошибку начинающих разработчиков при попытке выгрузить табличную часть КИ непосредственно из выборки детальных записей. Если в вашем основном запросе вы получаете поле табличной части КонтактнаяИнформация, помните, что в выборку оно придет с типом РезультатЗапроса. Попытка обойти его циклом напрямую вызовет ошибку. Для решения этой проблемы используйте метод выгрузки:
// ОШИБОЧНЫЙ ВАРИАНТ:
// Для Каждого СтрокаКИ Из Выборка.КонтактнаяИнформация Цикл ... КонецЦикла;
// ПРАВИЛЬНЫЙ ВАРИАНТ:
ТаблицаКонтактнойИнформации = Выборка.КонтактнаяИнформация.Выгрузить();
Для Каждого СтрокаКИ Из ТаблицаКонтактнойИнформации Цикл
// Обработка строк контактной информации
КонецЦикла;
Часто перед программистом ставят задачу не просто вывести телефон в печатную форму договора, а сформировать список номеров телефонов для дальнейшей интеграции с СМС-шлюзом (например, для проведения рассылки по клиентам или заемщикам микрофинансовой организации).
Для СМС-шлюзов текстовое представление номера с форматированием вроде +7 (900) 123-45-67 не подходит, так как они принимают исключительно очищенные цифровые последовательности вида 79001234567. Напишем готовый и оптимальный код, который решает эту задачу для списка физических лиц:
// 1. Предположим, у нас есть массив получателей рассылки
МассивФизЛиц = Новый Массив;
Для Каждого СтрокаАдресатов Из Объект.ТабличнаяЧастьАдресатов Цикл
МассивФизЛиц.Добавить(СтрокаАдресатов.ФизическоеЛицо);
КонецЦикла;
// 2. Получаем контактную информацию пакетным запросом БСП
ТаблицаТелефонов = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъектов(
МассивФизЛиц,
Перечисления.ТипыКонтактнойИнформации.Телефон
);
// 3. Собираем очищенные номера мобильных телефонов
МассивНомеровДляОтправки = Новый Массив;
Для Каждого СтрокаКИ Из ТаблицаТелефонов Цикл
// Отбираем только мобильные телефоны физических лиц
Если СтрокаКИ.Вид = Справочники.ВидыКонтактнойИнформации.ТелефонМобильныйФизическиеЛица Тогда
// Поле "НомерТелефона" в БСП по умолчанию содержит очищенную строку из цифр
ЧистыйНомер = СтрокаКИ.НомерТелефона;
// В некоторых старых версиях БСП поле "НомерТелефона" может быть пустым.
// В таком случае очистим представление программным методом БСП:
Если Не ЗначениеЗаполнено(ЧистыйНомер) Тогда
ЧистыйНомер = УправлениеКонтактнойИнформациейКлиентСервер.ОчиститьСтрокуНомераТелефона(СтрокаКИ.Представление);
КонецЕсли;
// Проверяем заполненность и добавляем в массив результатов
Если ЗначениеЗаполнено(ЧистыйНомер) Тогда
// Убедимся, что номер не дублируется в рассылке
Если МассивНомеровДляОтправки.Найти(ЧистыйНомер) = Неопределено Тогда
МассивНомеровДляОтправки.Добавить(ЧистыйНомер);
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Наш массив "МассивНомеровДляОтправки" полностью готов для передачи в API СМС-провайдера
Разработка внешних обработок и печатных форм (ВПФ) имеет одну неприятную особенность: при их запуске через механизм дополнительных отчетов и обработок в режиме «1С:Предприятие» код выполняется во временном сеансе, и отладчик стандартным образом (по кнопке F9 на строке кода ВПФ) не срабатывает. Разработчики часто тратят много времени, пытаясь понять, почему "не взлетает" запрос или какие данные приходят в переменные.
Рассмотрим два лучших способа, позволяющих полноценно использовать точки останова в конфигураторе при работе с внешними печатными формами.
Этот элегантный способ позволяет отлаживать вашу печатную форму прямо "на лету", не загружая ее повторно в справочник дополнительных обработок после каждого изменения в коде. Разберем его по шагам:
Печать(), напишите следующий перенаправляющий код:
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Путь к вашей РАБОЧЕЙ обработке на жестком диске, где вы фактически пишете код
ПутьКЛокальномуФайлу = "C:\MyProjects\ПечатнаяФормаТелефоны.epf";
// Динамически загружаем файл с диска и передаем управление ему
РабочаяОбработка = ВнешниеОбработки.Create(ПутьКЛокальномуФайлу, Ложь);
РабочаяОбработка.Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода);
Возврат; // Прерываем выполнение встроенной версии
КонецПроцедуры
Теперь вы можете открыть файл C:\MyProjects\ПечатнаяФормаТелефоны.epf в конфигураторе, расставить в нем любые точки останова, менять алгоритмы формирования макета и нажимать кнопку «Сохранить» (Ctrl+S). При вызове печатной формы из интерфейса 1С система обратится к зарегистрированному файлу, который мгновенно перенаправит выполнение на ваш локальный диск. Отладчик сработает без лишних манипуляций по перезагрузке файла в базу!
Если вам не хочется регистрировать обработку в базе, можно встроить механизм самотестирования прямо внутрь печатной формы:
ФормаОтладки) и сделайте ее основной формой обработки. Пользователи при вызове печати из документов ее не увидят, так как БСП обращается напрямую к экспортному методу Печать() в модуле объекта.СсылкаНаОбъект (тип ссылки на документ или справочник, для которого предназначена форма) и кнопку «Запустить печать».
&НаСервере
Процедура ЗапуститьПечатьНаСервере()
// Получаем объект обработки
ОбъектОбработки = РеквизитФормыВЗначение("Объект");
// Подготавливаем необходимые БСП параметры
МассивОбъектов = Новый Массив;
МассивОбъектов.Добавить(Объект.СсылкаНаОбъект);
КоллекцияПечатныхФорм = УправлениеПечатью.ПодготовитьКоллекциюПечатныхФорм("ВашИдентификаторПечатнойФормы");
ОбъектыПечати = Новый СписокЗначений;
ПараметрыВывода = УправлениеПечатью.ПодготовитьСтруктуруПараметровВывода();
// Вызываем процедуру формирования печатной формы
ОбъектОбработки.Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода);
// Выводим полученный табличный документ на экран (для визуального контроля)
ТабличныйДокумент = КоллекцияПечатныхФорм[0].ТабличныйДокумент;
ТабличныйДокумент.Показать("Результат тестирования печати");
КонецПроцедуры
&НаКлиенте
Процедура ЗапуститьПечать(Команда)
ЗапуститьПечатьНаСервере();
КонецПроцедуры
Теперь вам достаточно открыть вашу внешнюю обработку через меню Файл -> Открыть в режиме Предприятия, выбрать тестовый документ и нажать кнопку. Процесс пойдет по стандартной цепочке выполнения, и все точки останова в модуле объекта будут идеально отрабатывать в конфигураторе.