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