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