Как объединить дублирующиеся строки при выводе контактной информации в 1С без СКД?

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

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

Если вывод отчета спроектирован без использования Системы компоновки данных (СКД), стандартные механизмы группировки компоновщика нам недоступны. В данной статье мы подробно разберем, как решить проблему дублирования строк программными и архитектурными методами в 1С.

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

---

Способ 1. Несколько левых соединений по видам контактной информации в запросе

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

Проанализируем пример такого запроса. Мы используем левое соединение, чтобы контрагенты выводились в отчет даже в том случае, если у них не заполнен телефон или email.


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

    // Получаем Email
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК КИ_Email
        ПО СпрКонтрагенты.Ссылка = КИ_Email.Ссылка
        И КИ_Email.Тип = ЗНАЧЕНИЕ(Перечисление.ТипыКонтактнойИнформации.АдресЭлектроннойПочты)

Преимущества метода:

  1. Простота реализации непосредственно в конструкторе запросов.
  2. Высокая скорость выполнения на уровне СУБД.
  3. Результат запроса сразу готов к выводу построчно без дополнительного разбора в коде.

Важное ограничение: Если у контрагента в базе данных по какому-то стечению обстоятельств окажется два телефона или два email с одинаковым типом, СУБД выполнит декартово произведение, и строки в отчете снова начнут дублироваться. Поэтому данный метод надежно работает только при жестком контроле уникальности видов контактной информации.

---

Способ 2. Группировка данных и использование функции МАКСИМУМ

Выясним, как свернуть дублирующиеся строки в запросе, если нам достаточно получить ровно один (любой из имеющихся) телефон и один email. Для этого мы можем применить группировку по ключевым полям контрагента, а для полей контактной информации использовать агрегатную функцию МАКСИМУМ.

Давайте посмотрим, как будет выглядеть такой запрос:


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

Как это работает: Конструкция ВЫБОР КОГДА проверяет тип строки контактной информации. Если тип совпадает, поле заполняется представлением контакта, иначе — пустой строкой. С помощью группировки СГРУППИРОВАТЬ ПО и функции МАКСИМУМ пустые строки отсекаются, а заполненное значение поднимается в итоговую строку контрагента. Мы гарантированно получаем одну строку на одного контрагента.

---

Способ 3. Программная обработка выборки (Объединение строк через запятую)

Что делать, если у контрагента действительно может быть записано несколько телефонов и несколько адресов электронной почты, и нам принципиально важно вывести их все в одну ячейку (например, в формате "телефон 1, телефон 2")? Напрямую в языке запросов 1С выполнить конкатенацию строк из разных записей одной группировки (аналог функции GROUP_CONCAT или STRING_AGG в SQL) невозможно.

Решить эту задачу поможет обход выборки на встроенном языке с использованием механизма итогов ИТОГИ ПО. Разберем этот алгоритм по шагам.

Шаг 1. Пишем запрос с расчетом итогов по контрагенту:


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

Шаг 2. Обходим выборку в цикле и склеиваем контакты:

Теперь напишем программный код, который будет перебирать выборку по группировке "Контрагент", а внутри каждой группы собирать детальные записи контактов в единые текстовые строки.


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

// Начинаем обход по верхнему уровню группировки (Контрагенты)
ВыборкаКонтрагенты = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаКонтрагенты.Следующий() Цикл
    
    // Инициализируем переменные-накопители для контактов текущего контрагента
    СтрокаТелефонов = "";
    СтрокаEmail = "";
    
    // Получаем подчиненную выборку детальных записей
    ВыборкаДетали = ВыборкаКонтрагенты.Выбрать();
    Пока ВыборкаДетали.Следующий() Цикл
        
        Если ВыборкаДетали.Тип = Перечисления.ТипыКонтактнойИнформации.Телефон Тогда
            Если ЗначениеЗаполнено(ВыборкаДетали.Представление) Тогда
                СтрокаТелефонов = СтрокаТелефонов + ?(СтрокаТелефонов = "", "", ", ") + ВыборкаДетали.Представление;
            КонецЕсли;
        ИначеЕсли ВыборкаДетали.Тип = Перечисления.ТипыКонтактнойИнформации.АдресЭлектроннойПочты Тогда
            Если ЗначениеЗаполнено(ВыборкаДетали.Представление) Тогда
                СтрокаEmail = СтрокаEmail + ?(СтрокаEmail = "", "", ", ") + ВыборкаДетали.Представление;
            КонецЕсли;
        КонецЕсли;
        
    КонецЦикла;
    
    // Теперь выводим строку в табличный документ
    ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
    ОбластьСтрока.Параметры.Контрагент = ВыборкаКонтрагенты.Контрагент;
    ОбластьСтрока.Параметры.Телефоны   = СтрокаТелефонов;
    ОбластьСтрока.Параметры.Email       = СтрокаEmail;
    
    ТабличныйДокумент.Вывести(ОбластьСтрока);
    
КонецЦикла;

Этот способ является наиболее универсальным и гибким, так как исключает появление дублирующихся строк в отчете и сохраняет абсолютно все контактные данные клиентов.

---

Способ 4. Использование готовых механизмов БСП

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

Проанализируем работу с общим модулем УправлениеКонтактнойИнформацией. Функция КонтактнаяИнформацияОбъектов() позволяет быстро получить готовые представления контактов для переданного массива объектов.

Рассмотрим пример кода:


// Сначала получаем список контрагентов обычным простым запросом без соединений с КИ
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Контрагенты.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.Контрагенты КАК Контрагенты";

ТаблицаКонтрагентов = Запрос.Выполнить().Выгрузить();
МассивКонтрагентов = ТаблицаКонтрагентов.ВыгрузитьКолонку("Ссылка");

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

// ТаблицаКИ вернет готовую структуру, где для каждого контрагента 
// виды информации уже будут сопоставлены. Нам останется только вывести их в макет.

Использование механизмов БСП значительно сокращает трудозатраты разработчика, минимизирует вероятность ошибок и обеспечивает соответствие кода стандартам разработки фирмы "1С".

---

Резюме и рекомендации

Подводя итоги, выделим ключевые правила выбора метода решения:

← На главную