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