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