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

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

При разработке управляемых форм в системе 1С:Предприятие мы часто сталкиваемся с необходимостью динамического определения типа данных, хранящихся в реквизите, особенно если этот реквизит имеет составной тип. Составной тип позволяет реквизиту хранить значения разных видов, например, ссылку на элемент справочника "Организации" или на элемент справочника "ДоговорыКонтрагентов". Эта задача становится еще более специфичной, когда такой реквизит находится в табличной части формы, и нам требуется получить его тип для текущей строки.

Рассмотрим типовую ситуацию: у нас есть форма документа или справочника, содержащая табличную часть. В этой табличной части имеется реквизит, допустим, РазмещениеДенежныхСредствБанковскийСчет, который может принимать значения как типа СправочникСсылка.Организации, так и типа СправочникСсылка.ДоговорыКонтрагентов. Наша цель — в обработчике события ПриИзменении для этого элемента формы установить различные параметры выбора для другого поля в зависимости от того, какой тип значения выбран в текущей строке.

Основные концепции: функции ТипЗнч и Тип

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

  1. ТипЗнч(<Значение>): Эта функция используется для получения объекта типа по фактическому значению переменной или реквизита. Она принимает произвольное значение и возвращает объект типа Тип, который представляет собой описание конкретного типа данных (например, тип "Строка", тип "Число", или тип "СправочникСсылка.Организации"). Важно понимать, что ТипЗнч() всегда работает со значением данных.

  2. Тип("<ИмяТипа>"): Эта функция используется для получения объекта типа по его строковому представлению. Например, Тип("СправочникСсылка.Организации") вернет объект, описывающий тип "Ссылка на справочник Организации". Мы используем эту функцию для сравнения с результатом ТипЗнч().

Ключевой момент: для сравнения типов мы всегда должны сравнивать объекты типа Тип. Например, ТипЗнч(МояПеременная) = Тип("МойТип").

Распространенные ошибки при определении типа на форме

На начальном этапе часто возникают следующие заблуждения:

  1. Сравнение элемента формы напрямую: Попытка сравнить сам элемент формы с типом, например, Элементы.РазмещениеДенежныхСредствБанковскийСчет = Тип("СправочникСсылка.Организации"). Это не сработает, потому что Элементы.РазмещениеДенежныхСредствБанковскийСчет является объектом типа ПолеФормы (визуальный элемент управления на форме), а не хранящимся значением. Функция ТипЗнч(Элементы.РазмещениеДенежныхСредствБанковскийСчет) вернет именно Тип("ПолеФормы"), что не является тем, что нам нужно.

  2. Обращение к реквизиту табличной части через Объект напрямую: Попытка получить значение реквизита табличной части с помощью Объект.ИмяРеквизитаТабличнойЧасти. Например, ТипЗнч(Объект.РазмещениеДенежныхСредствБанковскийСчет). Это также некорректно для табличной части. Объект формы (например, документ) имеет собственные реквизиты, но реквизиты табличной части являются частью коллекции строк этой табличной части. Для доступа к реквизиту в конкретной строке табличной части требуется особый подход.

Корректное получение типа реквизита табличной части на клиенте

Чтобы правильно получить тип значения реквизита, расположенного в табличной части, нам необходимо обратиться к текущим данным этой табличной части. На управляемых формах это делается с помощью свойства ТекущиеДанные элемента формы, представляющего табличную часть.

Шаги для получения типа реквизита табличной части:

  1. Идентифицируем элемент формы, отвечающий за табличную часть: Допустим, ваша табличная часть называется РазмещениеДенежныхСредств, и элемент формы, который ее отображает, тоже имеет имя РазмещениеДенежныхСредств (или любое другое имя, заданное в свойствах формы). Тогда к нему мы обращаемся как Элементы.РазмещениеДенежныхСредств.

  2. Получаем доступ к текущей строке: Свойство ТекущиеДанные элемента формы типа "Таблица" (который отображает табличную часть) предоставляет объект, который содержит данные текущей, активной строки табличной части. Соответственно, мы используем Элементы.РазмещениеДенежныхСредств.ТекущиеДанные.

  3. Обращаемся к реквизиту в текущей строке: Теперь, имея доступ к данным текущей строки, мы можем обратиться к нужному реквизиту, например, РазмещениеДенежныхСредствБанковскийСчет. Полный путь будет выглядеть так: Элементы.РазмещениеДенежныхСредств.ТекущиеДанные.РазмещениеДенежныхСредствБанковскийСчет.

  4. Применяем функцию ТипЗнч(): Наконец, к полученному значению мы применяем функцию ТипЗнч() для определения его фактического типа: ТипЗнч(Элементы.РазмещениеДенежныхСредств.ТекущиеДанные.РазмещениеДенежныхСредствБанковскийСчет).

Таким образом, ваш код для процедуры ПриИзменении будет выглядеть следующим образом:


&НаКлиенте
Процедура РазмещениеДенежныхСредствБанковскийСчетПриИзменении(Элемент)
    // Предполагаем, что "Элемент" - это поле формы для реквизита РазмещениеДенежныхСредствБанковскийСчет
    // в текущей строке табличной части. Родитель "Элемента" - это табличная часть.
    
    ВладелецСчета = Объект.Организация; // Это, вероятно, реквизит шапки документа
    НовыйМассивПараметров = Новый Массив;

    // Получаем текущее значение реквизита из табличной части
    // Предполагаем, что имя элемента формы для табличной части такое же, как и имя табличной части.
    // Если имя элемента формы другое, используйте его.
    ТекущийРеквизитТЧ = Элементы.РазмещениеДенежныхСредств.ТекущиеДанные.РазмещениеДенежныхСредствБанковскийСчет;

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

    Элементы.РазмещениеДенежныхСредствБанковскийСчет.ПараметрыВыбора = Новый ФиксированныйМассив(НовыйМассивПараметров);
КонецПроцедуры

Обратите внимание, что в данном примере мы явно обращаемся к элементу формы табличной части по его имени Элементы.РазмещениеДенежныхСредств. Если имя вашего элемента формы, отображающего табличную часть, отличается, используйте соответствующее имя. Например, если ваша табличная часть называется "СписокРазмещений", а элемент формы, отображающий ее, называется "ТаблицаРазмещений", то обращение будет Элементы.ТаблицаРазмещений.ТекущиеДанные....

Когда может понадобиться серверный вызов для определения типа

В большинстве случаев, описанных выше, значение реквизита табличной части доступно на клиенте через ТекущиеДанные, и нет необходимости делать серверный вызов. Однако бывают ситуации, когда серверный вызов может быть полезен или даже необходим:

  1. Значение не загружено на клиент: Если реквизит, тип которого вы хотите определить, не отображается на форме и не был загружен в данные формы при открытии.

  2. Работа с объектными данными, которые не синхронизированы: Если вам нужно получить тип реквизита не из текущих данных формы, а из самого объекта базы данных (например, если объект формы еще не записан, а вы хотите получить тип реквизита уже существующего в базе объекта).

  3. Дополнительная логика на сервере: Если после определения типа вам требуется выполнить сложную логику, связанную с обращением к другим данным или объектам, которые эффективнее обрабатывать на сервере.

  4. Полные права доступа: В редких случаях, когда для определения типа или доступа к определенным данным на клиенте не хватает прав, а на сервере эти права есть.

Пример реализации серверной функции для получения типа:


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

// Пример вызова на клиенте (в процедуре ПриИзменении):
&НаКлиенте
Процедура РазмещениеДенежныхСредствБанковскийСчетПриИзменении(Элемент)
    // ... (предварительный код) ...

    // Получаем текущую строку табличной части
    ТекущиеДанныеСтрокиТЧ = Элементы.РазмещениеДенежныхСредств.ТекущиеДанные;
    
    // Если вам требуется получить тип реквизита, который НЕ является текущим значением
    // или по какой-то причине нужно именно серверное получение:
    // В данном случае, если нам нужен тип для ТЕКУЩЕЙ строки,
    // то передача всего Объекта на сервер и поиск по индексу или УИД.
    // Для простоты, если нам нужно получить тип для реквизита из текущего Объекта формы
    // (после того как данные формы синхронизированы с Объектом при вызове сервера):
    
    // Предположим, что мы передаем весь объект формы на сервер, и там
    // определяем тип реквизита из ТЕКУЩЕЙ строки.
    // Важно: при контекстном серверном вызове, Объект формы будет синхронизирован.
    // Если бы нам нужно было получить тип из любой строки:
    // ИндексТекущейСтроки = Элементы.РазмещениеДенежныхСредств.ТекущаяСтрока.НомерСтроки - 1; // НомерСтроки начинается с 1
    // ТипНаСервере = ПолучитьТипРеквизитаКонтекстноНаСервере(ИндексТекущейСтроки);
    
    // Упрощенный пример для контекстного серверного вызова,
    // где Объект формы будет доступен на сервере в актуальном состоянии:
    ТипНаСервере = ПолучитьТипРеквизитаИзТекущегоОбъектаНаСервере();

    Если ТипНаСервере = Тип("СправочникСсылка.Организации") Тогда
        НовыйМассивПараметров.Добавить(Новый ПараметрВыбора("Отбор.Владелец", ВладелецСчета));
    // ...
    КонецЕсли;
КонецПроцедуры

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

Важно понимать, что при контекстном вызове сервера (то есть, когда серверная процедура/функция вызывается из модуля формы с директивой &НаСервере без указания параметров), объект формы автоматически передается на сервер и становится доступен как переменная Объект. Это позволяет обращаться к его реквизитам и табличным частям в актуальном состоянии. Однако, для табличных частей, если вам нужен тип реквизита из *конкретной* текущей строки, вы должны убедиться, что соответствующая строка или ее идентификатор либо переданы в серверную функцию как параметр, либо вы можете найти ее на сервере по каким-либо критериям (например, по УникальныйИдентификатор строки, если он используется).

В нашем случае, когда мы обрабатываем событие ПриИзменении для поля в табличной части, данные текущей строки уже находятся на клиенте и доступны через Элементы.ИмяТабличнойЧасти.ТекущиеДанные. Поэтому серверный вызов для определения типа реквизита текущей строки обычно избыточен и не требуется, если только нет специфических причин, таких как сложные вычисления на сервере или ограничения прав доступа.

Настройка ПараметровВыбора для составного типа

После того как мы определили тип реквизита, мы можем динамически настроить параметры выбора для элемента формы. В приведенном примере мы используем массив объектов ПараметрВыбора, чтобы установить отбор для связанного поля. Это позволяет нам фильтровать список выбора в зависимости от того, является ли реквизит "Организацией" или "ДоговоромКонтрагента".

Свойство ПараметрыВыбора элемента формы ожидает фиксированный массив или фиксированную структуру объектов ПараметрВыбора. Это позволяет нам передавать необходимые параметры для ограничения выбора данных, что делает интерфейс более удобным и предотвращает ошибки ввода.

Заключение

Мы видим, что для успешного получения типа реквизита из составного типа, находящегося в табличной части управляемой формы 1С, ключевым является правильное использование функции ТипЗнч() и корректное обращение к данным текущей строки табличной части через Элементы.ИмяТабличнойЧасти.ТекущиеДанные.ИмяРеквизита. Этот подход позволяет нам на клиенте без лишних серверных вызовов динамически изменять логику формы, например, настраивать параметры выбора. Важно всегда применять ТипЗнч() к самому значению данных, а не к элементу формы, который его отображает.

← На главную