При разработке отчетов по складским остаткам и движениям товаров программисты часто сталкиваются с ситуацией, когда данные о поступлении и списании (расходе) по одной и той же номенклатуре выводятся в разных строках. Это происходит из-за того, что в базе данных эти операции представлены разными записями или разными видами движений. Наша задача — сгруппировать эти данные так, чтобы пользователь видел итоговые показатели за период в одной компактной строке.
Рассмотрим подробнее несколько проверенных способов решения этой задачи: от корректировки SQL-подобного запроса 1С до тонкой настройки Системы компоновки данных (СКД).
Проанализируем структуру исходного запроса. Если мы используем ОБЪЕДИНИТЬ ВСЕ, система честно добавляет новые строки для каждого блока данных. Чтобы "схлопнуть" эти строки на уровне запроса, нам необходимо применить группировку и агрегатные функции СУММА с условием.
Вместо того чтобы просто выбирать количество и сумму, мы разделим их по колонкам прямо в блоке ВЫБРАТЬ. Разберем пример кода, который позволит разнести приход и расход в разные поля одной записи:
ВЫБРАТЬ
ХозрасчетныйОбороты.Субконто1 КАК Номенклатура,
СУММА(ВЫБОР
КОГДА ХозрасчетныйОбороты.Счет В ИЕРАРХИИ (&СчетаПоступлений)
ТОГДА ХозрасчетныйОбороты.КоличествоОборотДт
ИНАЧЕ 0
КОНЕЦ) КАК КоличествоПоступление,
СУММА(ВЫБОР
КОГДА ХозрасчетныйОбороты.Счет В ИЕРАРХИИ (&СчетаСписаний)
ТОГДА ХозрасчетныйОбороты.КоличествоОборотКт
ИНАЧЕ 0
КОНЕЦ) КАК КоличествоСписание,
СУММА(ХозрасчетныйОбороты.СуммаОборотДт) КАК СуммаПоступление,
СУММА(ХозрасчетныйОбороты.СуммаОборотКт) КАК СуммаСписание
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(&ДатаНачала, &ДатаОкончания, , Счет В ИЕРАРХИИ (&Счета10), , , , ) КАК ХозрасчетныйОбороты
СГРУППИРОВАТЬ ПО
ХозрасчетныйОбороты.Субконто1
В этом примере мы используем функцию СУММА в сочетании с ВЫБОР. Это позволяет системе при обходе записей проверять условие и прибавлять значение к нужному ресурсу (Поступление или Списание). В итоге для каждой номенклатуры сформируется ровно одна строка с заполненными показателями.
Если вы используете Систему компоновки данных, проблему часто можно решить без изменения текста запроса, только за счет настроек компоновки. Выясним, как правильно организовать структуру отчета.
В СКД часто возникает ситуация, когда детальные записи (регистраторы) "разрывают" общую строку номенклатуры. Чтобы этого избежать, выполним следующие шаги:
Номенклатура.Важный момент: чтобы поля не дублировались и отображались в одной строке, проверим свойство "Расположение полей". Установка значения "Вместе" или использование вертикальной группировки в выбранных полях позволяет визуально объединить данные.
Бывает так, что в одну строку нужно вывести не только цифры, но и перечень документов (регистраторов), которыми оформлялись движения. Начиная с версии платформы 8.3.15, мы можем использовать функцию СтрокаСоединить.
Рассмотрим ситуацию: нам нужно вывести все номера накладных через запятую в одной ячейке рядом с общим количеством. Для этого в СКД на закладке "Вычисляемые поля" добавим новое поле и в выражении укажем:
СтрокаСоединить(ВычислитьВыражениеСГруппировкойМассив("Регистратор"), ", ")
Это выражение соберет все ссылки на документы в массив в рамках текущей группировки (например, по Номенклатуре) и преобразует их в одну текстовую строку. Это элегантное решение, избавляющее от необходимости плодить лишние строки в печатной форме.
Одной из самых коварных проблем при объединении остатков и оборотов в одном запросе является эффект "умножения" (Cartesian Product). Если соединять таблицу остатков с таблицей оборотов через ЛЕВОЕ СОЕДИНЕНИЕ по номенклатуре в обычном запросе, то начальный остаток может продублироваться столько раз, сколько было движений по этой позиции.
Чтобы этого избежать, проанализируем архитектуру наборов данных в СКД. Вместо одного огромного запроса создадим два отдельных Набора данных - запрос:
Остатки.Обороты или ОборотыДтКт.Свяжем их на закладке "Связи наборов данных" по полю Номенклатура. В этом случае СКД самостоятельно выполнит соединение результатов, корректно рассчитает итоги и выведет данные по остаткам и всем видам движений в одной строке без дублирования сумм.
Если данные уже получены в виде ТаблицыЗначений (например, после выполнения запроса или программной обработки), мы можем воспользоваться встроенным методом Свернуть(). Посмотрим на пример реализации:
// Предположим, ТЗДвижения содержит колонки: Номенклатура, КоличествоПриход, КоличествоРасход
// Сначала подготавливаем данные, чтобы приход и расход были в разных колонках
ТЗДвижения.Свернуть("Номенклатура", "КоличествоПриход, КоличествоРасход, СуммаПриход, СуммаРасход");
Метод Свернуть объединит все строки с одинаковой номенклатурой, сложив при этом числа в колонках ресурсов. Это самый быстрый способ получить одну строку на одну позицию номенклатуры при постобработке данных в коде модуля.
Выбор конкретного метода зависит от сложности вашей задачи. Для простых отчетов идеально подходит Способ 1 (конструкция ВЫБОР в запросе). Если вы строите сложный интерактивный отчет, лучше всего использовать Способ 4 (связи наборов данных в СКД), так как это гарантирует правильность итогов и гибкость настроек для конечного пользователя. Помните, что главной целью является минимизация визуального шума для пользователя: отчет должен быть лаконичным и информативным.