Каждый разработчик 1С рано или поздно сталкивается с мистической ситуацией: один и тот же запрос, выполненный на одних и тех же данных с визуально одинаковыми параметрами, выдает разное количество строк в консоли запросов и при запуске в коде конфигуратора. В консоли мы видим ожидаемый и правильный результат (например, 59 строк с отрицательными остатками), а в коде программы — существенно меньше (например, всего 12 строк).
Давайте подробно разберем, почему возникает такое поведение, проанализируем внутреннюю механику работы платформы с параметрами запросов и выясним, как избежать подобных ошибок при разработке.
Проанализируем ситуацию, когда в параметры виртуальной таблицы или в секцию ГДЕ передается список значений (например, список складов, номенклатуры или контрагентов). В тексте запроса это выглядит следующим образом:
И Склад В (&СписокСкладов)
Когда мы заполняем этот параметр в консоли запросов, мы делаем это интерактивно: открываем диалоговое окно и вручную галочками или кнопкой выбора добавляем конкретные элементы справочника Склады. Все выбранные значения гарантированно являются элементами (конечными складами), по которым в регистре накопления ТоварыНаСкладах есть движения.
В коде конфигуратора заполнение списка чаще всего происходит программно. Например, обходом какой-то коллекции или поиском по наименованию. Если при программном заполнении списка в него по ошибке попадает группа справочника (папка), поведение оператора В кардинально меняется.
Рассмотрим, как оператор В (аналог SQL-оператора IN) обрабатывает переданную коллекцию:
В выполняет строгое сравнение переданных значений. Он ищет точное совпадение ссылки в базе данных с элементами в списке параметров.&СписокСкладов попадает ссылка на группу «Оптовые склады», СУБД пытается найти в регистре накопления записи, где в поле Склад записана именно эта группа. Разумеется, таких записей в базе нет.Выясним, как группа справочника вообще может незаметно попасть в список параметров в коде конфигуратора. Чаще всего это происходит из-за использования метода НайтиПоНаименованию().
Представим структуру справочника Склады:
Когда мы пишем код программного поиска склада для заполнения параметров:
СкладСсылка = Справочники.Склады.НайтиПоНаименованию("Склад №1");
СписокСкладов.Добавить(СкладСсылка);
Метод НайтиПоНаименованию() ищет первый попавшийся объект с указанным именем. Порядок обхода базы данных СУБД при таком поиске не гарантирует, что первым вернется элемент, а не группа. В огромном количестве случаев платформа вернет именно ссылку на группу справочника (папку). В результате в список параметров попадает некондиционное значение, и запрос в коде возвращает урезанные данные по сравнению с консолью, где разработчик выбрал правильный склад вручную.
Рассмотрим несколько надежных способов исправления этой ошибки и подходов к безопасному написанию кода.
Способ 1. Использование оператора В ИЕРАРХИИ
Если бизнес-логика вашей задачи допускает, что в списке параметров могут находиться как конкретные элементы, так и группы (целые папки складов или номенклатуры), необходимо заменить оператор В на В ИЕРАРХИИ в тексте запроса:
Склад В ИЕРАРХИИ (&СписокСкладов)
В этом случае платформа 1С на уровне СУБД автоматически развернет каждую переданную группу до списка всех входящих в нее элементов (включая подчиненные группы любых уровней вложенности). Запрос отработает корректно и вернет данные по всем складам, находящимся внутри папки.
Способ 2. Проверка признака ЭтоГруппа при поиске
Если вам гарантированно нужен только конечный элемент справочника, никогда не доверяйте методу НайтиПоНаименованию() без последующей проверки. Обязательно проверяйте свойство ЭтоГруппа:
НайденныйСклад = Справочники.Склады.НайтиПоНаименованию("Склад №1", Истина); // Истина - поиск по полному соответствию
Если НайденныйСклад.ЭтоГруппа Тогда
// Обрабатываем нештатную ситуацию
ВызватьИсключение "Ошибка: вместо склада найдена группа справочника с аналогичным наименованием!";
КонецЕсли;
Способ 3. Безопасный поиск через запрос
Наиболее профессиональным и надежным подходом является отказ от использования стандартных методов поиска менеджера справочника в пользу запросов с жестко заданными условиями. Посмотрим на пример такого кода:
ЗапросПоиска = Новый Запрос;
ЗапросПоиска.Текст =
"ВЫБРАТЬ
| Склады.Ссылка КАК Ссылка
|ИЗ
| Справочник.Склады КАК Склады
|ГДЕ
| Склады.Наименование = &Наименование
| И Склады.ЭтоГруппа = ЛОЖЬ";
ЗапросПоиска.УстановитьПараметр("Наименование", "Склад №1");
РезультатЗапроса = ЗапросПоиска.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
СписокСкладов.Добавить(Выборка.Ссылка);
КонецЕсли;
Этот код полностью исключает вероятность попадания папки в список параметров, даже если пользователи создадут дублирующие наименования в информационной базе.
Если вы проверили параметры, и они абсолютно идентичны (включая типы данных и отсутствие групп), проанализируем другие популярные причины расхождения результатов:
ГДЕ или использование конструкции ВЫБРАТЬ РАЗРЕШЕННЫЕ автоматически скроет часть данных в коде.Новый Граница(КонецДня(Дата), ВидГраницы.Включая). В консолях запросов разработчики часто по ошибке передают просто чистую дату без границы. Из-за этого документы, записанные в ту же секунду, могут теряться в результатах запроса консоли.Таким образом, всегда детально проверяйте типы и структуру передаваемых параметров, избегайте поиска элементов по наименованию без проверок и учитывайте контекст исполнения вашего кода.