Как выгрузить разные типы телефонов физлица из 1С:ЗУП в строгом порядке?

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

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

Рассмотрим эту проблему на конкретном примере и разберем, как прийти к эффективному и надежному решению, избежав типичных ошибок начинающих разработчиков.

Анализ исходной проблемы и неверного подхода

Давайте проанализируем ситуацию, с которой столкнулся автор. Стояла задача выгрузить три типа телефона (мобильный, домашний, рабочий) в массив из трех элементов. Исходный код выглядел примерно так:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ФизическиеЛицаКонтактнаяИнформация.НомерТелефона КАК НомерТелефона
    |ИЗ
    |    Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ФизическиеЛицаКонтактнаяИнформация
    |ГДЕ
    |    ФизическиеЛицаКонтактнаяИнформация.Ссылка.ИНН = &ИНН
    |    И ФизическиеЛицаКонтактнаяИнформация.Тип = &Тип
    |
    |УПОРЯДОЧИТЬ ПО
    |    ФизическиеЛицаКонтактнаяИнформация.НомерСтроки";

Запрос.УстановитьПараметр("ИНН", ЭлементСписка.ИНН);
Запрос.УстановитьПараметр("Тип", Перечисления.ТипыКонтактнойИнформации.Телефон);

РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();

Телефоны = Новый Массив(3);
Телефоны[0] = "";
Телефоны[1] = "";
Телефоны[2] = "";

Счетчик = 0;
Пока ВыборкаДетальныеЗаписи.Следующий() И Счетчик < 3 Цикл
    Телефоны[Счетчик] = ВыборкаДетальныеЗаписи.НомерТелефона;
    Счетчик = Счетчик + 1;
КонецЦикла;

Сообщить("Телефон1:" + Телефоны[0]);
Сообщить("Телефон2:" + Телефоны[1]);
Сообщить("Телефон3:" + Телефоны[2]);

В чем заключается ошибка? Проблема этого кода в его "слепоте". Запрос возвращает только существующие телефонные номера. Если у физлица есть мобильный и рабочий, но нет домашнего, запрос вернет всего две строки. Цикл Пока Выборка... отработает дважды:

  1. На первой итерации в Телефоны[0] запишется мобильный номер.
  2. На второй итерации в Телефоны[1] запишется рабочий номер (хотя мы ожидали его увидеть в Телефоны[2]).

В итоге массив будет заполнен неверно, а элемент Телефоны[2] останется пустым. Полагаться на порядок записей в базе данных (даже с УПОРЯДОЧИТЬ ПО) — очень ненадежная практика, так как мы не можем гарантировать, что номер строки для мобильного всегда будет 1, для домашнего — 2 и так далее.

Ключевой недостаток подхода — код не знает, какой именно тип телефона он обрабатывает в данный момент. Он просто берет следующую запись из выборки и кладет ее в следующий элемент массива.

Шаг к правильному решению: идентификация данных

Чтобы решить проблему, нам нужно в самом запросе получать не только значение (номер телефона), но и его идентификатор — вид контактной информации. Это позволит нам в цикле точно определять, с каким типом телефона мы имеем дело.

Модифицируем наш запрос, добавив в него поле Вид:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ФизическиеЛицаКонтактнаяИнформация.НомерТелефона КАК НомерТелефона,
    |    ФизическиеЛицаКонтактнаяИнформация.Вид КАК ВидТелефона
    |ИЗ
    |    Справочник.ФизическиеЛица.КонтактнаяИнформация КАК ФизическиеЛицаКонтактнаяИнформация
    |ГДЕ
    |    ФизическиеЛицаКонтактнаяИнформация.Ссылка = &ФизЛицо
    |    И ФизическиеЛицаКонтактнаяИнформация.Тип = &ТипТелефон";

Обратите внимание, что условие по ИНН заменено на прямую ссылку на физлицо (&ФизЛицо). Это более надежный и производительный способ идентификации объекта в базе данных.

Теперь в нашей выборке будет две колонки: НомерТелефона и ВидТелефона. Этой информации уже достаточно для построения правильной логики.

Оптимальная реализация: один запрос и структурированная обработка

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

1. Используем более универсальный источник данных

В современных конфигурациях 1С, включая ЗУП, контактная информация чаще всего хранится в регистре сведений КонтактнаяИнформация. Он является более универсальным хранилищем, чем табличная часть справочника. Наш запрос станет еще лучше, если мы обратимся к этому регистру.


Запрос = Новый Запрос;
Запрос.Текст =
    "ВЫБРАТЬ
    |    КонтактнаяИнформация.Представление КАК НомерТелефона,
    |    КонтактнаяИнформация.Вид КАК ВидТелефона
    |ИЗ
    |    РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
    |ГДЕ
    |    КонтактнаяИнформация.Объект = &ФизЛицо
    |    И КонтактнаяИнформация.Тип = &ТипТелефон";

Запрос.УстановитьПараметр("ФизЛицо", СсылкаНаФизЛицо);
Запрос.УстановитьПараметр("ТипТелефон", Перечисления.ТипыКонтактнойИнформации.Телефон);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Этот запрос делает ровно то, что нам нужно: получает все записи с типом "Телефон" для конкретного физлица, возвращая сам номер (поле Представление) и его вид.

2. Готовим структуру для результатов

Вместо массива с жестко заданными индексами гораздо удобнее использовать объект Структура. Мы можем заранее определить ключи, которые соответствуют нужным нам типам телефонов. Это делает код более читаемым и гибким.


// Инициализируем структуру с пустыми значениями по умолчанию
Телефоны = Новый Структура;
Телефоны.Вставить("Мобильный", "");
Телефоны.Вставить("Домашний", "");
Телефоны.Вставить("Рабочий", "");

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

3. Обрабатываем выборку и формируем XML

Теперь объединим все вместе. Пройдемся циклом по результату запроса и, анализируя поле ВидТелефона, будем заполнять нашу структуру. После цикла мы получим полностью готовую и упорядоченную информацию для выгрузки.


// Заполняем структуру данными из запроса
Пока Выборка.Следующий() Цикл
    Если Выборка.ВидТелефона = Справочники.ВидыКонтактнойИнформации.ТелефонМобильныйФизическиеЛица Тогда
        Телефоны.Мобильный = Выборка.НомерТелефона;
    ИначеЕсли Выборка.ВидТелефона = Справочники.ВидыКонтактнойИнформации.ТелефонДомашнийФизическиеЛица Тогда
        Телефоны.Домашний = Выборка.НомерТелефона;
    ИначеЕсли Выборка.ВидТелефона = Справочники.ВидыКонтактнойИнформации.ТелефонРабочийФизическиеЛица Тогда
        Телефоны.Рабочий = Выборка.НомерТелефона;
    КонецЕсли;
КонецЦикла;

// Теперь в структуре "Телефоны" находятся все номера в нужных "ячейках".
// Отсутствующие номера будут иметь пустые значения.
// Далее можно формировать XML или любой другой формат вывода.

// Пример записи в XML (переменная XML должна быть предварительно создана, например, как ЗаписьXML)
XML.ЗаписатьНачалоЭлемента("ТелефоныСотрудника");

XML.ЗаписатьНачалоЭлемента("МобильныйТелефон");
XML.ЗаписатьТекст(Телефоны.Мобильный);
XML.ЗаписатьКонецЭлемента();

XML.ЗаписатьНачалоЭлемента("ДомашнийТелефон");
XML.ЗаписатьТекст(Телефоны.Домашний);
XML.ЗаписатьКонецЭлемента();

XML.ЗаписатьНачалоЭлемента("РабочийТелефон");
XML.ЗаписатьТекст(Телефоны.Рабочий);
XML.ЗаписатьКонецЭлемента();

XML.ЗаписатьКонецЭлемента(); // ТелефоныСотрудника

Итоги и преимущества правильного подхода

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

  1. Эффективность: Мы делаем всего один запрос к базе данных, чтобы получить всю необходимую информацию. Это значительно снижает нагрузку по сравнению с выполнением трех отдельных запросов для каждого типа телефона.
  2. Надежность: Логика кода больше не зависит от порядка записей в базе данных. Благодаря проверке по ВидуКонтактнойИнформации, каждый номер телефона попадет на свое место, а отсутствующие номера корректно останутся пустыми строками.
  3. Читаемость и поддержка: Использование Структуры с осмысленными ключами ("Мобильный", "Домашний") делает код самодокументируемым и понятным. Если в будущем потребуется добавить, например, "Факс", нужно будет лишь добавить одну строку в инициализацию структуры и одно условие в ИначеЕсли.
  4. Гарантированный порядок вывода: При формировании итогового файла (например, XML) мы обращаемся к полям структуры в той последовательности, которая нам нужна, обеспечивая строгий и предсказуемый формат выгрузки.

Таким образом, ключ к решению подобных задач — это получение в одном запросе не только самих данных, но и их идентификаторов (типов, видов), с последующей логической обработкой в коде для формирования требуемой структуры.

← На главную