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