Почему запрос в 1С 7.7 по регистру ОстаткиТМЦ возвращает пустые поля и как правильно сделать выборку по складу?

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

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

Причина 1: Применение агрегатных функций к измерениям и реквизитам

В языке запросов 1С 7.7 встроенные агрегатные функции работы с регистрами (НачОст, КонОст, Приход, Расход) могут применяться только к ресурсам регистра. Посмотрим на строку из запроса:

|Функция СуммаНачОст = НачОст(ЦенаПродажи);

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


Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Предупреждение("Ошибка выполнения запроса!");
    Возврат;
КонецЕсли;

Причина 2: Нарушение структуры обхода многоуровневых группировок

Вызов Запрос.Группировка(2) без предварительного позиционирования на верхнем уровне группировки Запрос.Группировка(1) не позволяет получить данные вложенного уровня. При обходе дерева результатов необходимо использовать один из двух подходов:

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

Причина 3: Оптимизация условия по фильтру склада

В тексте запроса вместо конкатенации строки проверки на пустое значение можно использовать оператор В. Если переменная ВыбСклад пустая (не выбрана в диалоге), условие Склад В ВыбСклад автоматически трактуется как Истина для всех складов, что избавляет от необходимости динамически формировать секцию Условие:

|Условие(Склад В ВыбСклад);

Решение 1: Корректный запрос по регистру «ОстаткиТМЦ» с расчетом суммы продажи

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


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

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Возврат;
КонецЕсли;

Пока Запрос.Группировка("Склад") = 1 Цикл
    Пока Запрос.Группировка("Номенклатура") = 1 Цикл
        Пока Запрос.Группировка("ЦенаПродажи") = 1 Цикл
            ОстатокКолво = Запрос.КонОстКолво;
            Цена = Запрос.ЦенаПродажи;
            СуммаРозн = ОстатокКолво * Цена;
            // Вывод данных в таблицу
        КонецЦикла;
    КонецЦикла;
КонецЦикла;

Решение 2: Выборка себестоимости и учетных сумм через регистр «ПартииНаличие»

Если вам необходимы не только розничные цены, но и стоимостная оценка остатков (учетная стоимость, сумма без НДС, себестоимость), следует строить запрос по регистру ПартииНаличие, так как именно в нем содержатся суммовые ресурсы:


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

Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    Пока Запрос.Группировка("Склад") = 1 Цикл
        Пока Запрос.Группировка("Номенклатура") = 1 Цикл
            // Доступны Запрос.КонОстКол и Запрос.КонОстСумма
        КонецЦикла;
    КонецЦикла;
КонецЕсли;

Решение 3: Быстрое получение остатков без объекта «Запрос»

Если в отчете не нужны обороты за период (приход и расход), а требуется получить только срез остатков на определенную дату, эффективнее и быстрее использовать объект работы с регистром напрямую через метод ВыгрузитьИтоги:


РегОстатки = СоздатьОбъект("Регистр.ОстаткиТМЦ");
ТЗ = СоздатьОбъект("ТаблицаЗначений");

// Устанавливаем фильтр по складу, если он выбран
Если ПустоеЗначение(ВыбСклад) = 0 Тогда
    РегОстатки.УстановитьЗначениеФильтра("Склад", ВыбСклад, 1);
КонецЕсли;

// Получаем срез остатков на ДатуК и выгружаем в Таблицу Значений
РегОстатки.ВременныйРасчет(1);
РассчитатьРегистрыНа(ДатаК);
РегОстатки.ВыгрузитьИтоги(ТЗ, 1, 1);

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

← На главную