В практике разработки на платформе 1С 8.3 часто встречается загадочная ситуация: один и тот же запрос, выполненный в Консоли запросов, возвращает нужные данные, а в Динамическом списке на форме (например, в форме выбора) эти данные отсутствуют. Особенно остро эта проблема проявляется при работе с временными показателями, когда записи в регистрах сведений или накопления создаются «внутри» текущего дня.
Рассмотрим типичный сценарий: оборудование сменило место базирования в 7:00 утра. Мы пытаемся оформить документ выдачи этого оборудования в 8:00 того же дня. В форме выбора пусто, хотя запрос в консоли подтверждает наличие записи. Оборудование магическим образом появляется в списке только на следующие сутки. Проанализируем, почему так происходит, и найдем изящное техническое решение.
Разберем, почему стандартный подход с использованием функций внутри запроса динамического списка может давать сбой. Динамический список — это не просто прямой запрос к базе данных, это объект, работающий на базе Системы компоновки данных (СКД). Когда мы пишем внутри текста запроса конструкцию типа ДОБАВИТЬКДАТЕ(&Период, СЕКУНДА, &Время * 3600) для виртуальной таблицы СрезПоследних, платформа может интерпретировать это некорректно при оптимизации запроса.
Основные факторы, влияющие на результат:
ГДЕ. Это критично для среза последних: вместо получения состояния на конкретный момент времени, система может сначала получить текущий срез, а потом отфильтровать его по дате, что логически неверно.Выясним причину на примере исходного (проблемного) кода запроса:
ВЫБРАТЬ
БазировкаСрезПоследних.Оборудование,
БазировкаСрезПоследних.АдресБазы
ИЗ
РегистрСведений.Базировка.СрезПоследних(
ДОБАВИТЬКДАТЕ(&Период, СЕКУНДА, &Время * 3600),
) КАК БазировкаСрезПоследних
Здесь &Период — это дата документа (часто начало дня), а &Время — число часов. В консоли запросов этот механизм отрабатывает «честно», но в динамическом списке такая сложность в параметрах виртуальной таблицы является крайне нежелательной.
Самый надежный способ решить проблему — полностью отказаться от вычислений внутри текста запроса динамического списка. Нам необходимо вычислить точную дату и время на стороне сервера 1С и передать в параметр уже готовый объект типа Дата.
Рассмотрим, как правильно модифицировать процедуру ПриСозданииНаСервере в модуле формы выбора:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Получаем исходные данные из параметров формы
ДатаДокумента = Параметры.Период;
Часы = Параметры.Время; // Например, число 8
// Формируем точную дату для среза последних
// Используем НачалоДня, чтобы гарантированно уйти от лишних секунд в дате документа
ТочныйПериод = НачалоДня(ДатаДокумента) + (Часы * 3600);
// Устанавливаем готовое значение в параметр динамического списка
Список.Параметры.УстановитьЗначениеПараметра("ПериодОтбора", ТочныйПериод);
Список.Параметры.УстановитьЗначениеПараметра("АдресБазы", Параметры.АдресБазы);
КонецПроцедуры
В самом запросе динамического списка текст теперь будет выглядеть гораздо проще и стабильнее:
ВЫБРАТЬ
БазировкаСрезПоследних.Оборудование КАК Оборудование,
БазировкаСрезПоследних.АдресБазы КАК АдресБазы
ИЗ
РегистрСведений.Базировка.SrezПоследних(&ПериодОтбора, ) КАК БазировкаСрезПоследних
ГДЕ
БазировкаСрезПоследних.АдресБазы = &АдресБазы
Часто в формах выбора требуется исключить оборудование, которое уже занято (например, есть движения по регистру накопления). Использование виртуальных таблиц ОстаткиИОбороты внутри запроса динамического списка — плохая практика, которая сильно снижает производительность.
Проанализируем более эффективный подход: сначала получим список исключаемого оборудования отдельным запросом, а затем передадим этот список (массив ссылок) в параметр динамического списка.
Пример реализации в модуле формы:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
МояДата = Параметры.Период;
МоеВремя = Параметры.Время;
НужныйМомент = НачалоДня(МояДата) + МоеВремя * 3600;
// 1. Получаем список занятого оборудования
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РегистрАрендыОстатки.Оборудование КАК Оборудование
|ИЗ
| РегистрНакопления.РегистрАренды.Остатки(&Период, ) КАК РегистрАрендыОстатки";
// Для получения остатка "на сейчас" можно добавить 1 секунду
Запрос.УстановитьПараметр("Период", НужныйМомент + 1);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
МассивИсключений = РезультатЗапроса.ВыгрузитьКолонку("Оборудование");
// 2. Настраиваем динамический список
Список.Параметры.УстановитьЗначениеПараметра("ПериодОтбора", НужныйМомент);
Список.Параметры.УстановитьЗначениеПараметра("ИсключаемоеОборудование", МассивИсключений);
// Остальные параметры...
КонецПроцедуры
В тексте запроса динамического списка используем условие: ГДЕ НЕ Оборудование В (&ИсключаемоеОборудование). Это позволит избежать сложных соединений «на лету», и платформа сможет эффективно использовать индексы основной таблицы справочника.
Чтобы избежать расхождений в результатах, всегда помните о следующих правилах:
Граница с видом ВидГраницы.Включая. Это особенно актуально для регистров сведений с режимом записи «В пределах секунды».ПОМЕСТИТЬ (временные таблицы), использование основной таблицы становится невозможным, что отключает стандартные механизмы поиска и сортировки 1С.НачалоДня() от исходной даты. Это исключит ситуацию, когда в исходной дате документа уже были записаны какие-то секунды или миллисекунды, которые при сложении дадут непредвиденный сдвиг.&Период будет игнорироваться платформой, и срез всегда будет браться на конец дня. Убедитесь, что ваш регистр имеет периодичность «В пределах секунды».Проанализировав ситуацию, мы выяснили, что ключ к исправлению ошибки — детерминированность параметров. Когда мы передаем в динамический список готовую дату, рассчитанную на сервере, мы лишаем СКД возможности «ошибиться» при трансляции запроса в SQL, обеспечивая идентичность работы формы и консоли запросов.