Как объединить подчиненные строки через запятую в СКД и избежать задвоения сумм?

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

При разработке отчетов в системе компоновки данных (СКД) разработчики часто сталкиваются с ситуацией, когда к одному родительскому документу или строке привязано несколько подчиненных объектов (например, несколько сопроводительных накладных или счетов-фактур к одной расходной накладной). При прямом левом соединении таблиц в запросе возникает связь «один ко многим» (1:N), которая размножает строки основного документа. В результате в отчете дублируются детальные записи, а числовые ресурсы (количество, сумма) удваиваются или утраиваются.

Разберем подробно, почему возникает эта проблема и как объединить номера подчиненных документов в одну строку через запятую без искажения итоговых данных.

Анализ проблемы: почему дублируются строки и суммы

Проанализируем типовую структуру запроса, приводящую к ошибке. Если в основном наборе данных выполняется левое соединение регистра или таблицы накладных с таблицей подчиненных документов:


ЛЕВОЕ СОЕДИНЕНИЕ Документ.СопроводительнаяНакладная КАК СНН
ПО РасходнаяНакладная.Ссылка = СНН.ДокументОснование

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

В языке запросов 1С отсутствуют функции строковой агрегации вроде STRING_AGG, поэтому задачу необходимо решать средствами СКД или языка 1С. Рассмотрим основные рабочие способы решения этой задачи.

Способ 1. Использование функции «СоединитьСтроки» и связей наборов данных

Наиболее чистый и производительный подход внутри СКД без изменения структуры метаданных — разделение выборки на два независимых набора данных и агрегация подчиненных записей в ресурс.

Разберем по шагам реализацию этого метода:

  1. Корректировка основного набора данных: удалим из текста запроса левое соединение с таблицей СопроводительнаяНакладная и выборку поля НомерСНН. Основной запрос должен возвращать только уникальные строки накладных и их табличных частей без размножения.
  2. Создание второго набора данных: добавим новый набор данных типа «Запрос» (назовем его НаборДанныхСНН) со следующим текстом:
    
    ВЫБРАТЬ
        СНН.ДокументОснование КАК ДокументОснование,
        СНН.НомерСНН КАК НомерСНН
    ИЗ
        Документ.СопроводительнаяНакладная КАК СНН
    ГДЕ
        НЕ СНН.ПометкаУдаления
    
  3. Настройка связей наборов данных: перейдем на вкладку Связи наборов данных и добавим связь:
    • Источник связи: ОсновнойНабор
    • Приемник связи: НаборДанныхСНН
    • Условие связи: Ссылка = ДокументОснование
    • Параметры: поле связи передается автоматически.
  4. Настройка агрегации на вкладке «Ресурсы»: чтобы подчиненные записи не размножали детальные строки основного набора, поле подчиненного набора данных обязательно должно стать ресурсом. Добавим поле НомерСНН в ресурсы и укажем формулу: СоединитьСтроки(Массив(НомерСНН), ", ")

В результате СКД для каждой строки накладной соберет подчиненные номера СНН в массив и склеит их через запятую, а числовые ресурсы основного набора сохранят корректные значения.

Способ 2. Использование функций агрегации строк языка выражений СКД

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

Для объединения номеров подчиненных документов через запятую по группировке документа накладной используем выражение:


СоединитьСтроки(ВычислитьВыражениеСГруппировкойМассив("НомерСНН", "Ссылка"), ", ")

Для предотвращения задвоения сумм числовых ресурсов скорректируем формулы их расчета в ресурсах, привязав суммирование к уникальному документу:

Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(Сумма)", "Ссылка"))

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

Способ 3. Вызов экспортной функции общего модуля в вычисляемом поле

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

Разберем шаги реализации:

  1. В общем модуле с признаком «Сервер» создадим экспортную функцию:
    
    Функция ПолучитьНомераСНН(ДокументСсылка) Экспорт
        
        Если Не ЗначениеЗаполнено(ДокументСсылка) Тогда
            Возврат "";
        КонецЕсли;
        
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |   СНН.НомерСНН КАК НомерСНН
            |ИЗ
            |   Документ.СопроводительнаяНакладная КАК СНН
            |ГДЕ
            |   СНН.ДокументОснование = &ДокументСсылка
            |   И НЕ СНН.ПометкаУдаления";
            
        Запрос.УстановитьПараметр("ДокументСсылка", ДокументСсылка);
        Выборка = Запрос.Выполнить().Выбрать();
        
        МассивНомеров = Новый Массив;
        Пока Выборка.Следующий() Цикл
            МассивНомеров.Добавить(СокрЛП(Выборка.НомерСНН));
        КонецЦикла;
        
        Возврат СтрСоединить(МассивНомеров, ", ");
        
    КонецФункции
    
  2. В основном запросе отчета полностью исключаем соединение с таблицей Документ.СопроводительнаяНакладная.
  3. На вкладке Вычисляемые поля схемы компоновки данных добавляем новое поле НомерСНН и в поле выражения прописываем вызов функции: ИмяОбщегоМодуля.ПолучитьНомераСНН(Ссылка)

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

Способ 4. Программная постобработка через набор данных «Объект»

В случаях со сложной логикой распределения данных отчет можно формировать программно. Запрос выполняется в коде модуля, результат выгружается в ТаблицаЗначений, где программно группируются дублирующиеся строки и заполняется поле со склеенными через СтрСоединить() номерами. После этого подготовленная таблица передается в СКД в качестве внешнего набора данных типа «Объект».

← На главную