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