При разработке функционала на управляемых формах 1С часто возникает задача: открыть форму выбора документов или записей регистра, отфильтрованных по указанной дате (независимо от времени), и после интерактивного выбора перенести реквизиты выбранного объекта в текущий документ. Разберем по шагам, с какими сложностями сталкивается разработчик при настройке отбора по дате, почему стандартная структура Отбор может не подойти и как правильно организовать передачу выбранных значений.
Рассмотрим, почему простая передача структуры отбора через параметры открытия формы часто приводит к неожиданным результатам. Когда мы открываем форму и передаем структуру:
ПараметрыФормы = Новый Структура;
Отбор = Новый Структура;
Отбор.Вставить("Дата", Объект.Дата);
ПараметрыФормы.Вставить("Отбор", Отбор);
ОткрытьФорму("Документ.ПериодическиеСписания.ФормаВыбора", ПараметрыФормы, ЭтаФорма);
Платформа 1С автоматически применяет этот параметр к динамическому списку формы выбора с видом сравнения Равно. Однако в реальной базе дата документа содержит время (например, 23.05.2017 14:35:10). Если реквизит формы содержит время 00:00:00 или отличается даже на секунду, отбор на точное равенство не вернет ни одного документа. Выясним, как наложить условие на весь интервал суток — от 00:00:00 до 23:59:59.
Проанализируем наиболее универсальный и чистый способ, не требующий модификации модуля формы выбора. Платформа 1С для динамических списков предоставляет специальный параметр формы — ФиксированныеНастройки. Мы можем сформировать объект НастройкиКомпоновкиДанных на клиенте в вызывающей форме и передать его при открытии.
Разберем реализацию команды открытия формы в модуле формы документа:
&НаКлиенте
Процедура КомандаВыбрать(Команда)
ПараметрыФормы = Новый Структура;
// Создаем настройки компоновки данных для передачи в динамический список
Настройки = Новый НастройкиКомпоновкиДанных;
// Устанавливаем левую границу диапазона (начало дня)
ЭлементОтбораДатаНачала = Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбораДатаНачала.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Дата");
ЭлементОтбораДатаНачала.ВидСравнения = ВидСравненияКомпоновкиДанных.БольшеИлиРавно;
ЭлементОтбораДатаНачала.ПравоеЗначение = НачалоДня(Объект.Дата);
ЭлементОтбораДатаНачала.Использование = Истина;
// Устанавливаем правую границу диапазона (конец дня)
ЭлементОтбораДатаОкончания = Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбораДатаОкончания.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Дата");
ЭлементОтбораДатаОкончания.ВидСравнения = ВидСравненияКомпоновкиДанных.МеньшеИлиРавно;
ЭлементОтбораДатаОкончания.ПравоеЗначение = КонецДня(Объект.Дата);
ЭлементОтбораДатаОкончания.Использование = Истина;
// Передаем сформированные настройки в параметры формы
ПараметрыФормы.Вставить("ФиксированныеНастройки", Настройки);
// Открываем форму выбора
ОткрытьФорму("Документ.ПериодическиеСписания.ФормаВыбора", ПараметрыФормы, ЭтаФорма);
КонецПроцедуры
Преимущества этого подхода:
Если по каким-то причинам мы хотим управлять списком внутри самой формы выбора, рассмотрим альтернативный вариант. Для этого передадим произвольный параметр (например, ДатаОтбора), чтобы платформа не пыталась применить типовую обработку структуры Отбор, и обработаем его на сервере.
Посмотрим на код в форме документа:
&НаКлиенте
Процедура КомандаВыбрать(Команда)
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("ДатаОтбора", Объект.Дата);
ОткрытьФорму("Документ.ПериодическиеСписания.ФормаВыбора", ПараметрыФормы, ЭтаФорма);
КонецПроцедуры
Теперь разберем обработчик в модуле формы выбора. Настроим отбор динамического списка Список в событии ПриСозданииНаСервере:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("ДатаОтбора") И ЗначениеЗаполнено(Параметры.ДатаОтбора) Тогда
ВыбраннаяДата = Параметры.ДатаОтбора;
// Отбор по началу дня
ЭлементОтбора1 = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора1.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Дата");
ЭлементОтбора1.ВидСравнения = ВидСравненияКомпоновкиДанных.БольшеИлиРавно;
ЭлементОтбора1.ПравоеЗначение = НачалоДня(ВыбраннаяДата);
ЭлементОтбора1.Использование = Истина;
// Отбор по концу дня
ЭлементОтбора2 = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора2.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Дата");
ЭлементОтбора2.ВидСравнения = ВидСравненияКомпоновкиДанных.МеньшеИлиРавно;
ЭлементОтбора2.ПравоеЗначение = КонецДня(ВыбраннаяДата);
ЭлементОтбора2.Использование = Истина;
КонецЕсли;
КонецПроцедуры
Рассмотрим частую ошибку начинающих разработчиков: попытку вручную считывать значения колонок из таблицы формы выбора через Элементы.Список.ТекущиеДанные и передавать структуру в процедуру Закрыть(). Это усложняет код и приводит к ошибкам при двойном клике или выборе через клавиатуру.
В платформе 1С правильный алгоритм выглядит следующим образом:
ОбработкаВыбора, либо через асинхронное ОписаниеОповещения.Посмотрим, как организовать получение данных через ОписаниеОповещения:
&НаКлиенте
Процедура КомандаВыбрать(Команда)
// Подготавливаем оповещение о закрытии формы выбора
Оповещение = Новый ОписаниеОповещения("ПослеВыбораДокумента", ЭтаФорма);
// Формируем параметры отбора
ПараметрыФормы = Новый Структура;
Настройки = Новый НастройкиКомпоновкиДанных;
ЭлементОтбора1 = Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора1.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Дата");
ЭлементОтбора1.ВидСравнения = ВидСравненияКомпоновкиДанных.БольшеИлиРавно;
ЭлементОтбора1.ПравоеЗначение = НачалоДня(Объект.Дата);
ЭлементОтбора1.Использование = Истина;
ЭлементОтбора2 = Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора2.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Дата");
ЭлементОтбора2.ВидСравнения = ВидСравненияКомпоновкиДанных.МеньшеИлиРавно;
ЭлементОтбора2.ПравоеЗначение = КонецДня(Объект.Дата);
ЭлементОтбора2.Использование = Истина;
ПараметрыФормы.Вставить("ФиксированныеНастройки", Настройки);
// Открываем форму с передачей обработчика оповещения
ОткрытьФорму("Документ.ПериодическиеСписания.ФормаВыбора", ПараметрыФормы, ЭтаФорма,,,, Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораДокумента(ВыбранноеЗначение, ДополнительныеПараметры) Экспорт
Если ЗначениеЗаполнено(ВыбранноеЗначение) Тогда
ЗаполнитьДокументПоВыбранному(ВыбранноеЗначение);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьДокументПоВыбранному(СсылкаНаДокумент)
// Считываем реквизиты объекта-образца и заполняем текущий документ
ОбъектОбразец = СсылкаНаДокумент.ПолучитьОбъект();
Объект.Получатель = ОбъектОбразец.Получатель;
Объект.Сумма = ОбъектОбразец.Сумма;
Объект.КошелекОтправления = ОбъектОбразец.КошелекОтправления;
КонецПроцедуры
Если на выбранную дату в базе нет ни одного документа, открывать пустую форму выбора не имеет смысла. Оптимально перед открытием формы проверить наличие данных серверным запросом:
&НаСервереБезКонтекста
Функция ЕстьДокументыНаДату(ПроверяемаяДата)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ПериодическиеСписания.Ссылка КАК Ссылка
|ИЗ
| Документ.ПериодическиеСписания КАК ПериодическиеСписания
|ГДЕ
| ПериодическиеСписания.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| И ПериодическиеСписания.Проведен = ИСТИНА";
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ПроверяемаяДата));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ПроверяемаяДата));
Возврат НЕ Запрос.Выполнить().Пустой();
КонецФункции
В обработчике команды вызываем проверку перед вызовом формы выбора:
&НаКлиенте
Процедура КомандаВыбрать(Команда)
Если НЕ ЕстьДокументыНаДату(Объект.Дата) Тогда
ПоказатьПредупреждение(, "На выбранную дату проведенных документов не обнаружено.");
Возврат;
КонецЕсли;
// Далее код открытия формы выбора...
КонецПроцедуры
Такой подход делает интерфейс более отзывчивым, защищает пользователя от работы с пустыми окнами и уменьшает количество лишних клиент-серверных вызовов при инициализации форм.