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