Как получить телефон и почту контрагента в запросе 1С 8.3

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

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

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

Архитектурные особенности: где искать контактную информацию?

Прежде чем писать текст запроса, нам необходимо определить, какая архитектурная модель хранения контактной информации используется в вашей конфигурации. На платформе «1С:Предприятие 8.3» сосуществуют два основных подхода:

  1. Регистр сведений «Контактная информация» (старый формат): Характерен для конфигураций на обычных формах (например, «Управление производственным предприятием 1.1», «Управление торговлей 10.3», «Бухгалтерия предприятия 2.0»). Все контактные данные хранятся в специальном регистре сведений. Измерение Объект хранит ссылку на владельца (контрагента, партнера, физическое лицо), а измерения или реквизиты определяют тип и вид информации.
  2. Табличные части справочников (новый формат БСП): Применяется в современных конфигурациях на управляемых формах, использующих Библиотеку стандартных подсистем (БСП), таких как «ERP Управление предприятием 2», «Комплексная автоматизация 2», «Управление торговлей 11», «Бухгалтерия предприятия 3.0». Здесь контактная информация вынесена в подчиненную табличную часть самого справочника — например, Справочник.Контрагенты.КонтактнаяИнформация.

Понимание этой разницы критически важно, так как запрос, написанный для одной конфигурации, выдаст ошибку отсутствия таблицы в другой. Проблему различия форматов при миграции решает перенос данных УПП 1.3 — ERP, который автоматически преобразует данные под новую архитектуру целевой базы.


Решение для конфигураций с регистром сведений (Классический метод)

Рассмотрим ситуацию, когда контактные данные хранятся в регистре сведений РегистрСведений.КонтактнаяИнформация. Типичной ошибкой новичка является построение запроса с одним левым соединением и последующей фильтрацией через условие ГДЕ с оператором ИЛИ. Посмотрим на этот пример, чтобы понять, почему он работает неэффективно:


// Пример неоптимального запроса, приводящего к дублированию строк
ВЫБРАТЬ
    Контрагенты.Ссылка КАК Покупатель,
    ВЫБОР КОГДА КонтактнаяИнформация.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.Телефон) 
        ТОГДА КонтактнаяИнформация.Представление КОНЕЦ КАК Телефон,
    ВЫБОР КОГДА КонтактнаяИнформация.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты) 
        ТОГДА КонтактнаяИнформация.Представление КОНЕЦ КАК Почта
ИЗ
    Справочник.Контрагенты КАК Контрагенты
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация 
        ПО КонтактнаяИнформация.Объект = Контрагенты.Ссылка
ГДЕ
    Контрагенты.Покупатель = ИСТИНА
    И (КонтактнаяИнформация.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.Телефон)
    ИЛИ КонтактнаяИнформация.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты))

Почему такой подход нежелателен?

Правильный вариант запроса с раздельными левыми соединениями

Для того чтобы получить телефон и почту в одной строке без дублирования записей, мы применим раздельные левые соединения к одной и той же таблице регистра сведений. Каждое соединение будет отвечать исключительно за свой тип контактной информации. Условия фильтрации по типу данных мы вынесем непосредственно в параметры связи ПО.

Проанализируем оптимальный текст запроса:


ВЫБРАТЬ
    Контрагенты.Ссылка КАК Покупатель,
    Контрагенты.ЮрФизЛицо КАК ЮрФизЛицо,
    ЕСТЬNULL(КонтактнаяИнформацияТелефон.Представление, "") КАК Телефон,
    ЕСТЬNULL(КонтактнаяИнформацияПочта.Представление, "") КАК Почта
ИЗ
    Справочник.Контрагенты КАК Контрагенты
    
    // Первое соединение — только для получения телефона
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформацияТелефон 
        ПО (КонтактнаяИнформацияТелефон.Объект = Контрагенты.Ссылка)
        И (КонтактнаяИнформацияТелефон.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.Телефон))
        
    // Второе соединение — только для получения адреса электронной почты
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформацияПочта 
        ПО (КонтактнаяИнформацияПочта.Объект = Контрагенты.Ссылка)
        И (КонтактнаяИнформацияПочта.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты))
ГДЕ
    Контрагенты.Покупатель = ИСТИНА

Преимущества данного решения:


Важный аспект: Оптимизация и отборы в запросе

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

Если вам требуется получить данные не по всем покупателям, а по конкретному списку или одному контрагенту, обязательно передавайте этот отбор в секцию ГДЕ через параметры запроса:


ГДЕ
    Контрагенты.Ссылка В (&СписокКонтрагентов)

Это позволит СУБД мгновенно отфильтровать выборку по индексу и присоединить контактную информацию только к выбранным записям.


Решение для современных конфигураций (БСП)

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


ВЫБРАТЬ
    Контрагенты.Ссылка КАК Покупатель,
    ЕСТЬNULL(КИ_Телефон.Представление, "") КАК Телефон,
    ЕСТЬNULL(КИ_Почта.Представление, "") КАК Почта
ИЗ
    Справочник.Contragents КАК Контрагенты // (В зависимости от локализации метаданных)
    
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК КИ_Телефон 
        ПО (КИ_Telephone.Ссылка = Контрагенты.Ссылка)
        И (КИ_Telephone.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.Телефон))
        
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК КИ_Почта 
        ПО (КИ_Почта.Ссылка = Контрагенты.Ссылка)
        И (КИ_Почта.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты))

Обратите внимание на важный нюанс современных версий БСП:
Внутри табличной части КонтактнаяИнформация детальные сведения (например, код города, индекс, город, улица) хранятся в сериализованном виде — в полях ЗначенияПолей (формат XML по схемам XDTO) и Значение (формат JSON). Распарсить эти строки внутри запроса средствами платформы невозможно. Поэтому через запрос мы можем надежно получить только готовое строковое представление КИ из поля Представление.


Альтернативный вариант: Использование программного интерфейса БСП

В реальной разработке в конфигурациях, использующих БСП, писать прямые запросы к табличным частям контактной информации для получения представлений адресов или телефонов считается плохой практикой (bad practice). Структура метаданных может измениться при очередном обновлении БСП, что приведет к неработоспособности ваших доработок.

Для безопасного и быстрого извлечения контактной информации рекомендуется использовать готовые функции программного интерфейса общего модуля УправлениеКонтактнойИнформацией. Для быстрого поиска подобных стандартных функций и ознакомления с примерами их вызова в коде можно использовать справочник по методам БСП с примерами использования.

Пример А. Получение контактной информации для одного объекта:


// Возвращает строку представления или таблицу со всеми полями адреса/телефона
КонтрагентСсылка = Справочники.Контрагенты.НайтиПоРеквизиту("ИНН", "7700000000");
ВидАдреса = Справочники.ВидыКонтактнойИнформации.ЮрАдресКонтрагента;

ПредставлениеАдреса = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъекта(
    КонтрагентСсылка, 
    ВидАдреса, 
    ТекущаяДата(), 
    Истина // Возвращать только представление строкой
);

Пример Б. Массовое получение данных для списка объектов (оптимизировано):

Если вам нужно получить данные по массиву контрагентов (например, при выводе списка в табличный документ), используйте пакетный метод. Под капотом БСП сама выполнит быстрый и правильный запрос к базе данных:


МассивКонтрагентов = Новый Массив;
// Заполняем массив ссылками...
МассивКонтрагентов.Добавить(Ссылка1);
МассивКонтрагентов.Добавить(Ссылка2);

// Определяем типы КИ, которые хотим получить
МассивТипов = Новый Массив;
МассивТипов.Добавить(Перечисления.ТипыКонтактнойИнформации.Телефон);
МассивТипов.Добавить(Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты);

// Получаем таблицу значений с результатами
ТаблицаРезультат = УправлениеКонтактнойИнформацией.КонтактнаяИнформацияОбъектов(
    МассивКонтрагентов, 
    МассивТипов
);

Решение проблемы объединения нескольких телефонов в одну ячейку

Рассмотрим более сложную практическую задачу. Что делать, если у одного контрагента в базе данных зарегистрировано несколько телефонных номеров (например, рабочий и мобильный)?

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

Важное ограничение платформы 1С:
В языке запросов 1С **отсутствуют** встроенные агрегатные функции для конкатенации строк (такие как GROUP_CONCAT в MySQL или STRING_AGG в MS SQL Server). Объединить произвольное количество строк в одну ячейку напрямую в СУБД средствами запроса 1С невозможно.

Тем не менее, эту задачу можно элегантно решить тремя альтернативными способами.

Способ 1. Использование Системы компоновки данных (СКД) — без программирования

Если ваш отчет или выборка выводятся через СКД, платформа предоставляет встроенный механизм склеивания строк на уровне вычисления ресурсов:

  1. Перейдите на вкладку «Ресурсы» в схеме компоновки данных.
  2. Добавьте ваше поле (например, Телефон) в список ресурсов.
  3. В колонке «Выражение» напишите формулу компоновки:
    СоединитьСтроки(Массив(Телефон)) или СоединитьСтроки(ВычислитьВыражениеСГруппировкойМассив("Представление", "Покупатель")).
  4. СКД автоматически сгруппирует контрагентов и выведет все телефоны каждого контрагента в одну строку через запятую. В параметрах функции можно указать и собственный разделитель (например, точку с запятой).

Способ 2. Пост-обработка результата запроса в коде 1С

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


// 1. Выполняем базовый запрос, который возвращает дублирующие строки контрагентов
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Контрагенты.Ссылка КАК Контрагент,
    |	КИ.Представление КАК Телефон
    |ИЗ
    |	Справочник.Контрагенты КАК Контрагенты
    |	ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КИ
    |		ПО КИ.Объект = Контрагенты.Ссылка
    |		И КИ.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.Телефон)
    |УПОРЯДОЧИТЬ ПО
    |	Контрагент";

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

// 2. Создаем результирующую Таблицу Значений для свертки данных
ТаблицаТелефонов = Новый ТаблицаЗначений;
ТаблицаТелефонов.Колонки.Добавить("Контрагент");
ТаблицаТелефонов.Колонки.Добавить("Телефоны");

СоответствиеКонтрагентов = Новый Соответствие;

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

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

Способ 3. Сложный запрос для фиксированного количества телефонов

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


// Временная таблица со всеми телефонами контрагентов
ВЫБРАТЬ
    КИ.Объект КАК Контрагент,
    КИ.Представление КАК Телефон
ПОМЕСТИТЬ ВТ_Телефоны
ИЗ
    РегистрСведений.КонтактнаяИнформация КАК КИ
ГДЕ
    КИ.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.Телефон)
;

////////////////////////////////////////////////////////////////////////////////
// Нумеруем телефоны и соединяем их для вывода первых двух номеров
ВЫБРАТЬ
    Контрагенты.Ссылка КАК Покупатель,
    // Выбираем первый попавшийся телефон
    МИНИМУМ(ВТ1.Телефон) КАК Телефон1,
    // Выбираем второй телефон (который больше первого)
    МИНИМУМ(ВТ2.Телефон) КАК Телефон2
ПОМЕСТИТЬ ВТ_СгруппированныеТелефоны
ИЗ
    Справочник.Контрагенты КАК Контрагенты
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Телефоны КАК ВТ1 
        ПО ВТ1.Контрагент = Контрагенты.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Телефоны КАК ВТ2 
        ПО ВТ2.Контрагент = Контрагенты.Ссылка 
        И ВТ2.Телефон > ВТ1.Телефон
ГРУППИРОВАТЬ ПО
    Контрагенты.Ссылка
;

////////////////////////////////////////////////////////////////////////////////
// Итоговый запрос со склеиванием двух полей в одну строку
ВЫБРАТЬ
    ВТ.Покупатель,
    ВТ.Телефон1 + ВЫБОР 
        КОГДА ВТ.Телефон2 ЕСТЬ NULL ТОГДА "" 
        ИНАЧЕ ", " + ВТ.Телефон2 
    КОНЕЦ КАК ТелефоныСтрокой
ИЗ
    ВТ_СгруппированныеТелефоны КАК ВТ

Этот способ достаточно сложен и подходит только тогда, когда количество записей на одного контрагента ограничено и детерминировано. Для объединения произвольного количества строк всегда выбирайте Способ 1 (СКД) или Способ 2 (программный обход).


Подведем итоги

Мы детально изучили методы работы с контактной информацией в 1С 8.3. Подводя итог, выделим несколько важных правил для качественной разработки:

← На главную