Как реализовать вложенную таблицу значений в табличной части на управляемых формах 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При переходе с обычных форм (например, в старых редакциях УПП) на управляемые формы (Управление холдингом, ERP, КА) разработчики часто сталкиваются с проблемой: старые методы программного изменения структуры таблиц перестают работать. Попытка добавить колонку с типом ТаблицаЗначений в существующую табличную часть на форме приводит к ошибке: "Нельзя изменять поле, содержащее объект данных формы". Разберем подробно, почему это происходит и как решить задачу современными методами платформы 1С 8.3.

Почему возникает ошибка и в чем ограничение управляемого приложения

Проанализируем ситуацию: в обычном приложении объект ТаблицаЗначений мог существовать непосредственно в памяти формы и гибко настраиваться. В управляемом приложении данные разделены между сервером и клиентом. То, что мы видим на экране — это не сама таблица, а специальный объект-декоратор ДанныеФормыКоллекция.

Этот объект имеет жесткую структуру, которая должна быть известна платформе для сериализации (передачи данных между сервером и клиентом). Тип ТаблицаЗначений не может быть типом колонки внутри ДанныеФормыКоллекция, так как платформа не умеет автоматически отрисовывать таблицу внутри другой таблицы в одной ячейке и передавать такую сложную вложенную структуру «на лету».

Рассмотрим по шагам основные способы обхода этого ограничения, которые позволят добиться нужного функционала.

Решение 1: Использование Дерева Значений (ValueTree)

Если вам критически важно иметь иерархическую структуру, где каждая «строка» основной таблицы может раскрываться и показывать «вложенную таблицу», лучшим инструментом будет ДеревоЗначений. Рассмотрим алгоритм его реализации:

  1. Создание реквизита формы: Вместо стандартной табличной части добавьте реквизит формы с типом ДеревоЗначений.
  2. Добавление колонок: Определите все колонки, которые должны быть у основной таблицы и у вложенных данных.
  3. Программное заполнение: При заполнении данных верхний уровень дерева будет играть роль основной ТЧ, а подчиненные узлы — роль вложенной таблицы.

Посмотрим на пример кода для заполнения такого дерева:


// На сервере
&НаСервере
Процедура ЗаполнитьДеревоНаСервере()
    // Преобразуем реквизит формы в объект ДеревоЗначений для удобной работы
    ДеревоОбъект = РеквизитФормыВЗначение("ДеревоДанных", Тип("ДеревоЗначений"));
    
    // Очищаем дерево
    ДеревоОбъект.Строки.Очистить();
    
    // Создаем строку верхнего уровня (аналог строки основной ТЧ)
    НоваяСтрокаРодитель = ДеревоОбъект.Строки.Добавить();
    НоваяСтрокаРодитель.ID = "001";
    НоваяСтрокаРодитель.Наименование = "Основной товар";
    
    // Добавляем вложенные данные (аналог вложенной ТЗ)
    ВложеннаяСтрока = НоваяСтрокаРодитель.Строки.Добавить();
    ВложеннаяСтрока.Наименование = "Спецификация 1";
    ВложеннаяСтрока.Значение = 100;
    
    // Возвращаем данные в реквизит формы
    ЗначениеВРеквизитФормы(ДеревоОбъект, "ДеревоДанных");
КонецПроцедуры

Решение 2: Реализация связи «Ведущий-Ведомый» (Master-Detail)

Это наиболее «правильный» с точки зрения интерфейсов 1С подход. Вместо того чтобы пытаться втиснуть таблицу в таблицу, мы разделим интерфейс на две части.

Выясним причину, почему это удобно: пользователь видит основную таблицу, и при выделении строки в ней (событие ПриАктивизацииСтроки), вторая таблица на форме мгновенно перерисовывается, показывая связанные данные.

Для реализации выполните следующие действия:

  1. Добавьте на форму две таблицы (два реквизита типа ТаблицаЗначений).
  2. Для основной таблицы создайте обработчик события ПриАктивизацииСтроки.
  3. В этом обработчике вызывайте серверную процедуру, которая будет фильтровать или заполнять вторую таблицу на основе ключа связи из первой.

Пример реализации связи:


&НаКлиенте
Процедура ОсновнаяТаблицаПриАктивизацииСтроки(Элемент)
    ТекущиеДанные = Элемент.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        ОбновитьВложеннуюТаблицу(ТекущиеДанные.КлючСвязи);
    КонецЕсли;
КонецПроцедуры

&НаСервере
Процедура ОбновитьВложеннуюТаблицу(КлючСвязи)
    // Здесь мы ищем данные, соответствующие ключу, и заполняем вторую таблицу
    // Например, из общего соответствия или запросом
    КоллекцияВложенныхДанных = НайтиДанныеПоКлючу(КлючСвязи);
    ЗначениеВРеквизитФормы(КоллекцияВложенныхДанных, "ВложеннаяТаблицаФормы");
КонецПроцедуры

Решение 3: Сериализация таблицы в строку (хранение данных)

Если вложенная таблица нужна вам не для редактирования пользователем «в ячейке», а для хранения результатов расчетов или последующего вывода в макет, используйте упаковку данных в строку. Это позволяет обойти ограничение типов ДанныеФормыКоллекция.

Разберем по шагам, как упаковать ТаблицуЗначений:

  1. В основной табличной части создайте скрытую колонку с типом Строка (неограниченной длины).
  2. При обработке данных (например, загрузке из Excel) преобразуйте вложенную ТЗ в строку с помощью JSON или хранилища значения.
  3. При необходимости вывода в макет или обработки — восстановите объект ТЗ из строки.

Рассмотрим пример использования ЗначениеВСтрокуВнутр (или более современного JSON):


// Упаковка в строку
ДанныеСтроки.ВложеннаяТЗ_Строкой = ЗначениеВСтрокуВнутр(ТЗ_АЗСт);

// Распаковка обратно в объект
ТЗ_ДляОбработки = ЗначениеИзСтрокиВнутр(ДанныеСтроки.ВложеннаяТЗ_Строкой);

Важный момент: если вы используете платформу выше 8.3.6, мы рекомендуем использовать JSON, так как он работает быстрее и занимает меньше места:


// Сериализация ТЗ в JSON
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ТЗ_ДляУпаковки);
РезультатСтрока = ЗаписьJSON.Закрыть();

Решение 4: Использование временного хранилища

Этот метод подходит для временных расчетов в рамках одной сессии работы с формой. Вместо того чтобы хранить тяжелые данные в реквизитах формы (что замедляет передачу данных между сервером и клиентом), мы можем хранить их во временном хранилище.

Проанализируем ситуацию: в колонку основной таблицы мы записываем только уникальный адрес (Строка), по которому на сервере можно получить полноценный объект ТаблицаЗначений.


// Помещаем таблицу в хранилище и сохраняем адрес в строке ТЧ
АдресХранилища = ПоместитьВоВременноеХранилище(ТЗ_АЗСт, ЭтаФорма.УникальныйИдентификатор);
НоваяСтрока.АдресДанных = АдресХранилища;

// Позже, когда данные нужны для макета
ТЗ_ИзХранилища = ПолучитьИзВременногоХранилища(ЭлементТЧ.АдресДанных);

Подведение итогов

Мы проанализировали проблему невозможности прямого добавления ТаблицыЗначений в колонку экранной таблицы. Подведем итоги выбора решения:

Использование данных методов позволит вам избежать ошибки изменения поля данных формы и обеспечит стабильную работу обработки в режиме управляемого приложения.

← На главную