При разработке отчетов и обработок в платформе 1С:Предприятие 7.7 разработчики нередко сталкиваются с ситуацией, когда документ делает запись в регистр остатков с нулевыми значениями ресурсов (например, фиксируется сам факт проведения документа по определенным измерениям без изменения количественных или суммовых остатков). При прямом просмотре физических таблиц (например, через DBF-редактор) эти записи присутствуют, однако стандартный встроенный запрос платформы наотрез отказывается их выводить. Разберем причины такого поведения и рассмотрим рабочие способы извлечения подобных данных.
Почему встроенный язык запросов 1С 7.7 пропускает нулевые ресурсы?
Проанализируем внутреннюю механику работы механизма запросов платформы 1С 7.7. По умолчанию оптимизатор запросов отсекает из итоговой выборки те группировки, по которым все вычисляемые агрегатные функции (такие как Приход(), Расход(), КонОст() или Сумма()) возвращают нулевые значения. Платформа предполагает, что строки с нулевыми итогами не несут смысловой нагрузки для отчета, поэтому автоматически исключает их из результата. Если в запросе рассчитываются только стандартные ресурсы регистра, документ с нулевым движением просто не попадет в выборку.
Самый простой, элегантный и производительный способ обойти стандартное отсечение нулевых записей — добавить в текст запроса агрегатную функцию Счётчик(). Рассмотрим этот механизм подробнее.
Функция Счётчик() подсчитывает количество физических записей, вошедших в группировку. Даже если значение числового ресурса регистра равно нулю, факт наличия записи в таблице движений приводит к тому, что Счётчик() вернет значение больше нуля (как минимум 1). Поскольку у строки появляется ненулевой агрегатный результат, платформа перестает считать группировку пустой и включает документ в выборку.
Разберем пример построения такого запроса:
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(ВыборкаНулевых)
|Период с ВыбНачПериода по ВыбКонПериода;
|Док = Регистр.ОстаткиТоваров.ТекущийДокумент;
|Товар = Регистр.ОстаткиТоваров.Товар;
|Количество = Регистр.ОстаткиТоваров.Количество;
|Функция КолвоОборот = Приход(Количество);
|Функция ВсегоЗаписей = Счётчик();
|Группировка Товар без групп;
|Группировка Док;
|"//}}ЗАПРОС
;
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
Пока Запрос.Группировка("Товар") = 1 Цикл
Пока Запрос.Группировка("Док") = 1 Цикл
// Теперь сюда попадают документы даже с нулевым ресурсом Количество
Сообщить("Документ: " + Запрос.Док + " | Ресурс: " + Запрос.КолвоОборот);
КонецЦикла;
КонецЦикла;
КонецЕсли;
Вместо функции Счётчик() также можно использовать конструкцию вычисления фиксированной суммы, например: Функция Флаг = Сумма(1);, однако именно Счётчик() является наиболее наглядным и каноничным решением в рамках языка запросов 1С 7.7.
Другим штатным решением является использование модификатора Все в описании группировок запроса. По умолчанию группировка выводит только те элементы, по которым есть обороты или остатки, отличные от нуля. Модификатор Все указывает платформе, что необходимо вернуть абсолютно все значения указанного разреза, вошедшие в условия выборки.
Посмотрим на синтаксис такой группировки:
ТекстЗапроса =
"Период с ВыбНачПериода по ВыбКонПериода;
|Док = Регистр.ОстаткиТоваров.ТекущийДокумент;
|Количество = Регистр.ОстаткиТоваров.Количество;
|Функция ПриходКолво = Приход(Количество);
|Группировка Док Все;
|";
Важное замечание: использование ключевого слова Все для документов или справочников может заметно увеличить объем выборки и время выполнения запроса, поэтому рекомендуется сочетать его с точными фильтрами по периоду и измерениям.
Если задача заключается в том, чтобы точно проанализировать физические движения конкретного документа или просканировать журнал движений регистра без использования встроенного агрегатного языка запросов, наилучшим вариантом будет объектная модель Регистр.
Объект Регистр в коде 1С 7.7 предоставляет прямой доступ к записям движений без агрегирования и исключения нулевых итогов. Разберем по шагам, как получить нулевые движения конкретного документа:
Рег = СоздатьОбъект("Регистр.ОстаткиТоваров");
// Вариант 1: выборка всех движений конкретного документа
Если Рег.ВыбратьДвиженияДокумента(ВыбДокумент) = 1 Тогда
Пока Рег.ПолучитьДвижение() = 1 Цикл
Если Рег.Количество = 0 Тогда
Сообщить("Найден нулевой ресурс по товару: " + Рег.Товар);
КонецЕсли;
КонецЦикла;
КонецЕсли;
// Вариант 2: последовательный перебор всех движений регистра за период
Если Рег.ВыбратьДвижения(ВыбНачПериода, ВыбКонПериода) = 1 Тогда
Пока Рег.ПолучитьДвижение() = 1 Цикл
Если Рег.Количество = 0 Тогда
Сообщить("Документ " + Рег.ТекущийДокумент + " сформировал нулевую запись!");
КонецЕсли;
КонецЦикла;
КонецЕсли;
Методы ВыбратьДвиженияДокумента() и ВыбратьДвижения() читают строки непосредственно из таблицы журнала движений (в DBF-версии это файл с префиксом RA), благодаря чему никакие фильтрации пустых ресурсов со стороны агрегатного ядра не применяются.
Функция ВсегоЗаписей = Счётчик();. Это заставит платформу вывести документ с нулевыми ресурсами.Регистр и метод ВыбратьДвиженияДокумента().