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