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