Как в 1С 7.7 выбрать данные одновременно из шапки и табличной части документа одним запросом

Программист 1С v7.7 Управленческий учет
← На главную

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

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

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

Рассмотрим код, который часто пытаются использовать начинающие разработчики:


ТекстЗапроса = "
|Номенклатура = Документ.КомплектацияТМЦ.Номенклатура, Документ.КомплектацияТМЦ.Комплект;
|Количество = Документ.КомплектацияТМЦ.Количество, Документ.КомплектацияТМЦ.КоличествоКомп;
";

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

Решение №1: Использование Таблицы Значений как инструмента объединения (UNION)

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

Сначала мы создаем общую таблицу значений, которая будет хранить итоговые данные:


ТЗ = СоздатьОбъект("ТаблицаЗначений");
ТЗ.НоваяКолонка("Номенклатура", "Справочник.Номенклатура");
ТЗ.НоваяКолонка("Количество", "Число");
ТЗ.НоваяКолонка("Документ", "Документ.КомплектацияТМЦ");

Затем мы выполняем первый запрос — по табличной части документа:


ТекстЗапросаТЧ = "
|Период с ВыбНач по ВыбКон;
|Ном = Документ.КомплектацияТМЦ.Номенклатура;
|Кол = Документ.КомплектацияТМЦ.Количество;
|ТекДок = Документ.КомплектацияТМЦ.ТекущийДокумент;
|Группировка ТекДок;
|Группировка Ном;
|";
// После выполнения запроса выгружаем результат в ТЗ
Запрос.Выгрузить(ТЗ, 1, 1); 

Далее мы выполняем второй запрос — только по шапкам документов, или обходим документы выборкой и добавляем данные в ту же ТЗ:


Док = СоздатьОбъект("Документ.КомплектацияТМЦ");
Док.ВыбратьДокументы(ВыбНач, ВыбКон);
Пока Док.ПолучитьДокумент() = 1 Цикл
    ТЗ.НоваяСтрока();
    ТЗ.Номенклатура = Док.Комплект;
    ТЗ.Количество = Док.КоличествоКомп;
    ТЗ.Документ = Док.ТекущийДокумент();
КонецЦикла;

В завершение мы можем свернуть таблицу для получения итогов:


ТЗ.Свернуть("Номенклатура, Документ", "Количество");
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
    // Вывод в печатную форму
КонецЦикла;

Решение №2: Запрос к регистрам (если документ делает движения)

Проанализируем еще один эффективный метод. Если документ КомплектацияТМЦ делает движения по регистрам (например, ОстаткиТМЦ или ПартииНаличие), то задача упрощается в разы. При проведении комплектации система обычно списывает комплектующие и приходует готовый комплект. Это значит, что в регистре накопления уже лежат готовые записи и для шапки, и для строк ТЧ.

Рассмотрим пример запроса к регистру:


ТекстЗапроса = "
|Период с ВыбНач по ВыбКон;
|Ном = Регистр.ОстаткиТМЦ.Номенклатура;
|Кол = Регистр.ОстаткиТМЦ.Количество;
|ТекДок = Регистр.ОстаткиТМЦ.ТекущийДокумент;
|Функция ПриходКол = Приход(Кол);
|Функция РасходКол = Расход(Кол);
|Группировка Ном;
|Группировка ТекДок;
|Условие(ТекДок.Вид() = ""КомплектацияТМЦ"");
|";

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

Решение №3: Обработка шапки при обходе табличной части

Если нам критично использовать именно запрос к документам, мы можем применить комбинированный метод. Мы пишем запрос только по табличной части, но внутри цикла обхода результатов запроса обращаемся к реквизитам шапки через объект ТекущийДокумент.

Разберем, как это реализовать:

  1. Выполняем запрос по табличной части.
  2. При обходе группировки по документу фиксируем данные комплекта из шапки.
  3. При обходе строк — данные комплектующих.

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

Важные нюансы работы с пустыми значениями

Выясним причину, по которой иногда данные все равно пропадают. В 1С 7.7, если вы используете фильтры в запросе, например Условие(Номенклатура.Артикул = ВыбАртикул), и при этом объединяете поля через запятую, платформа может применить внутреннее соединение INNER JOIN. Если в одной из строк табличной части номенклатура не заполнена, или если реквизит шапки пуст, вся запись (строка) может выпасть из итоговой выборки.

Поэтому при работе со сложными выборками в 7.7 всегда рекомендуется использовать промежуточную ТаблицуЗначений. Это дает вам полный контроль над данными: вы можете программно проверить каждое значение на Выбран(), прежде чем добавлять его в отчет.

Подведем итог: хотя штатный запрос 1С 7.7 имеет свои ограничения, использование Таблиц Значений и запросов к Регистрам позволяет эффективно решать задачу объединения данных шапки и табличной части без привлечения внешних компонент типа 1С++.

← На главную