При разработке сложных интерфейсов на управляемых формах 1С часто возникает необходимость программно добавлять колонки в табличную часть. Например, когда количество столбцов зависит от выбранного периода (месяцев) или настроек пользователя. Основная сложность заключается в том, что реквизиты, созданные методом ИзменитьРеквизиты(), существуют только в памяти формы. Как только пользователь закрывает форму, все введенные в эти колонки данные бесследно исчезают, так как они не связаны с объектом информационной базы (ИБ).
В этой статье мы подробно разберем, как правильно организовать хранение данных из динамических колонок, рассмотрим архитектурные подходы и приведем примеры кода для реализации надежного механизма записи и чтения.
Разберем ситуацию: вы программно создали реквизит формы и добавили соответствующий элемент (поле ввода) в таблицу. С точки зрения платформы 1С, этот реквизит принадлежит объекту УправляемаяФорма, а не объекту ДокументОбъект или СправочникОбъект. Когда срабатывает стандартная команда "Записать", платформа сохраняет только те данные, которые входят в структуру самого объекта. Программно добавленные "эфемерные" реквизиты в эту структуру не попадают. Следовательно, наша задача — перед записью переместить данные из формы в физические реквизиты объекта, а при открытии формы выполнить обратную операцию.
Проанализируем самый надежный и "правильный" с точки зрения архитектуры 1С подход. Он заключается в разделении модели хранения и модели представления.
Шаг 1. Подготовка метаданных.
В конфигураторе в табличную часть документа (например, Состав) мы добавляем только те реквизиты, которые неизменны (Код, Номенклатура). А для динамических данных создаем вторую, скрытую табличную часть — назовем ее ДополнительныеДанные. В ней должны быть следующие колонки:
КлючСвязи (Число или УникальныйИдентификатор) — для связи со строкой основной ТЧ.ИмяРеквизита (Строка) — имя динамической колонки (например, "Январь2024").Значение (Число, Строка или любой нужный тип).Шаг 2. Развертывание данных при открытии формы.
В обработчике ПриЧтенииНаСервере мы должны программно создать колонки на форме и заполнить их значениями из нашей скрытой ТЧ.
// Пример создания динамических реквизитов
МассивДобавляемыхРеквизитов = Новый Массив;
Для Каждого МесяцИзПериода Из СписокМесяцев Цикл
ИмяРеквизита = "М_" + Формат(МесяцИзПериода, "ДФ=MMyyyy");
ТипРеквизита = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2));
НовыйРеквизит = Новый РеквизитФормы(ИмяРеквизита, ТипРеквизита, "Объект.Состав", "План на " + ИмяРеквизита);
МассивДобавляемыхРеквизитов.Добавить(НовыйРеквизит);
КонецЦикла;
ЭтаФорма.ИзменитьРеквизиты(МассивДобавляемыхРеквизитов);
Шаг 3. Синхронизация данных перед записью.
Рассмотрим процесс сохранения. В обработчике ПередЗаписьюНаСервере нам необходимо очистить скрытую ТЧ ДополнительныеДанные и заново заполнить ее, перебрав все строки основной ТЧ и прочитав значения из динамических колонок формы.
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
ТекущийОбъект.ДополнительныеДанные.Очистить();
Для Каждого СтрокаСостава Из ТекущийОбъект.Состав Цикл
// Перебираем имена созданных ранее колонок
Для Каждого ИмяКолонки Из МассивИменДинамическихКолонок Цикл
НоваяЗапись = ТекущийОбъект.ДополнительныеДанные.Добавить();
НоваяЗапись.КлючСвязи = СтрокаСостава.НомерСтроки;
НоваяЗапись.ИмяРеквизита = ИмяКолонки;
// Получаем значение из реквизита формы, который связан со строкой
НоваяЗапись.Значение = СтрокаСостава[ИмяКолонки];
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Если структура данных слишком сложная или вы не хотите загромождать базу данных дополнительными табличными частями, можно воспользоваться методом сериализации. Выясним, как это работает: все динамические данные упаковываются в одну длинную строку (или ХранилищеЗначения) и сохраняются в одном реквизите объекта.
Рассмотрим алгоритм реализации:
ДанныеФормыСтрокой с типом "Строка неограниченной длины".ТаблицуЗначений или Соответствие.ЗаписатьJSON().ПрочитатьJSON() и распределяем значения по ячейкам таблицы на форме.Важный нюанс: Этот метод прост в реализации, но имеет существенный минус — вы не сможете использовать эти данные в запросах для построения отчетов, так как они хранятся в виде "текстовой каши".
Если данных в динамических колонках очень много и они не должны напрямую влиять на проведение документа, проанализируем вариант с Регистром Сведений. Мы создаем подчиненный регистр, где измерениями будут Ссылка на документ, НомерСтроки и ИмяКолонки, а ресурсом — само Значение.
Этот подход позволяет:
Разберем несколько тонкостей, которые помогут избежать ошибок при программном создании колонок:
1. Уникальность имен: При создании реквизитов формы всегда добавляйте префикс (например, ПрограммнаяКолонка_), чтобы избежать конфликта с именами стандартных реквизитов, которые могут появиться при обновлении конфигурации.
2. Удаление старых реквизитов: Если пользователь меняет период (например, с 3 месяцев на 6), перед добавлением новых колонок обязательно нужно удалить старые программные реквизиты, иначе вы получите ошибку дублирования имен. Метод ИзменитьРеквизиты позволяет передавать массив имен для удаления во втором параметре.
3. Типизация: Всегда четко задавайте ОписаниеТипов. Если колонка предназначена для ввода чисел, не оставляйте тип "Неопределено", иначе пользователь не сможет вводить данные корректно, а платформа не поймет, как их суммировать в подвалах таблицы.
Заключение: Какой бы метод вы ни выбрали, помните главное правило управляемых форм — форма лишь отображает данные, но не хранит их. Правильная организация связки между динамическими элементами формы и физическими таблицами базы данных — залог стабильной работы программы и целостности данных вашего решения.