Как в запросе 1С 7.7 подменить товар на аналог для вывода итогов

Программист 1С v7.7
← На главную

В работе с платформой 1С:Предприятие 7.7 разработчики часто сталкиваются с существенными ограничениями встроенного языка запросов. Одной из таких задач является необходимость вывести итоги по товарам, но с условием: если у товара заполнен реквизит Аналог, то все количественные и суммовые показатели должны быть отнесены именно к аналогу, а не к исходному товару. В современных версиях платформы (8.x) это легко решается конструкцией ВЫБОР КОГДА... (CASE WHEN), однако в "семерке" штатный конструктор запросов не позволяет гибко манипулировать полями группировки в момент выполнения.

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

Проблема использования конструкции выбора в тексте запроса

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


|ТоварДляОтчета = Справочник.Товары.ТекущийЭлемент, Справочник.Товары.Аналог;
|Группировка ТоварДляОтчета;

Важный момент: такая конструкция в 1С 7.7 не объединяет значения в одну логическую сущность для целей группировки с суммированием итогов. Запрос будет работать либо по первому полю, либо по условию "ИЛИ" для фильтрации, но он не сможет "схлопнуть" записи разного происхождения в одну строку итогов. Для решения нашей задачи нам придется прибегнуть к постобработке данных.

Способ №1: Использование промежуточной Таблицы Значений (ТЗ)

Это самый надежный и универсальный метод для 1С 7.7, если объем данных невелик. Суть метода заключается в том, чтобы получить из базы все необходимые данные (и Товар, и Аналог), а затем программно "перебросить" показатели с основного товара на аналог в объекте ТаблицаЗначений.

Рассмотрим алгоритм реализации по шагам:

  1. Создаем запрос, в который включаем оба реквизита: и основной товар, и его аналог.
  2. Выгружаем результат запроса в таблицу значений методом Выгрузить().
  3. Обходим таблицу в цикле, подменяя значение в колонке товара значением из колонки аналога (если аналог выбран).
  4. Используем метод Свернуть() для окончательного подсчета итогов.

Посмотрим на пример реализации этого кода:


// Описываем запрос
ТекстЗапроса = "
|Товар = Справочник.Товары.ТекущийЭлемент;
|Аналог = Справочник.Товары.Аналог;
|Кол = Справочник.Товары.Количество;
|Функция ВсегоКол = Сумма(Кол);
|Группировка Товар без групп;
|";

Запрос = СоздатьОбъект("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    ТЗ = СоздатьОбъект("ТаблицаЗначений");
    // Выгружаем результат запроса в ТЗ
    Запрос.Выгрузить(ТЗ, 0, 0);
    
    // Подменяем товар на аналог там, где это необходимо
    ТЗ.ВыбратьСтроки();
    Пока ТЗ.ПолучитьСтроку() = 1 Цикл
        Если ПустоеЗначение(ТЗ.Аналог) = 0 Тогда
            ТЗ.Товар = ТЗ.Аналог;
        КонецЕсли;
    КонецЦикла;
    
    // Схлопываем строки по товару (теперь там и товары, и их аналоги)
    ТЗ.Свернуть("Товар", "ВсегоКол");
    
    // Теперь ТЗ содержит корректные итоги, которые можно выводить в отчет
    ТЗ.ВыбратьСтроки();
    Пока ТЗ.ПолучитьСтроку() = 1 Цикл
        // Вывод данных в таблицу отчета
    КонецЦикла;
КонецЕсли;

Способ №2: Ручной обход группировок с условием

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

Разберем пример, где мы анализируем наличие аналога непосредственно при обходе выборки:


Процедура ДобавитьВТЗ(ТЗ_Итогов, ТоварДляЗаписи, ЗначениеСуммы)
    СтрокаНомер = 0;
    Если ТЗ_Итогов.НайтиЗначение(ТоварДляЗаписи, СтрокаНомер, "Товар") = 1 Тогда
        ТЗ_Итогов.ПолучитьСтрокуПоНомеру(СтрокаНомер);
        ТЗ_Итогов.Сумма = ТЗ_Итогов.Сумма + ЗначениеСуммы;
    Иначе
        ТЗ_Итогов.НоваяСтрока();
        ТЗ_Итогов.Товар = ТоварДляЗаписи;
        ТЗ_Итогов.Сумма = ЗначениеСуммы;
    КонецЕсли;
КонецПроцедуры

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

Этот метод позволяет гибко управлять логикой подмены прямо "на лету", однако стоит помнить, что метод НайтиЗначение в большой таблице значений может работать медленнее, чем метод Свернуть.

Особенности работы с функцией ПустоеЗначение() в запросах

При составлении условий внутри запроса (секция Когда), следует быть осторожными. В 1С 7.7 (особенно в SQL-версии) проверка ПустоеЗначение(Реквизит) = 1 внутри текста запроса иногда может отрабатывать некорректно из-за особенностей хранения пустых ссылок в базе данных SQL. Если вам необходимо фильтровать данные в самом запросе, лучше использовать сравнение с заранее полученным пустым значением справочника или проверять через Выбран() = 0 при обходе результата.

Прямые SQL-запросы как альтернатива

Если данных очень много и отчет работает медленно, программисты часто используют внешние компоненты, такие как 1C++. Прямой SQL-запрос позволяет выполнить подмену на уровне сервера базы данных, что в десятки раз быстрее штатных средств. В SQL это выглядело бы примерно так:


SELECT 
    CASE WHEN $Спр.Аналог = '   0     ' THEN $Спр.ID ELSE $Спр.Аналог END AS [ТоварИлиАналог $Справочник.Товары],
    SUM($Док.Количество) as [Кол]
FROM ...
GROUP BY CASE WHEN $Спр.Аналог = '   0     ' THEN $Спр.ID ELSE $Спр.Аналог END

Подведем итог: для стандартных задач в 1С 7.7 оптимальным выбором будет Способ №1 (выгрузка в ТЗ и метод Свернуть). Он прост в отладке, не требует внешних компонент и гарантирует правильный расчет итогов даже в тех случаях, когда несколько разных товаров ссылаются на один и тот же аналог.

← На главную