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