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