Как определить тип документа-владельца табличной части в общем модуле 1С

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача: в общем модуле обрабатывается табличная часть документа, переданная с клиента или из серверного контекста формы. Проблема заключается в том, что тип передаваемого объекта — ДанныеФормыКоллекция. Это универсальный системный тип, который сам по себе «не знает», какому именно документу он принадлежит (например, «Реализация товаров и услуг» или «Поступление товаров и услуг»). В этой статье мы подробно разберем, как вычислить владельца табличной части, рассмотрим различные подходы от «чистого кода» до настройки свойств элементов формы и проанализируем преимущества каждого метода.

Проблема универсального типа ДанныеФормыКоллекция

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

Рассмотрим пример кода, который часто приводит к ошибке:


// В общем модуле
Процедура ОбработатьТЧ(КоллекцияТЧ) Экспорт
    ТипТЧ = ТипЗнч(КоллекцияТЧ); // Вернет ДанныеФормыКоллекция
    МетаданныеОбъекта = Метаданные.НайтиПоТипу(ТипТЧ); // Вернет Неопределено
КонецПроцедуры

Выясним причину такого поведения: ДанныеФормыКоллекция — это программная «прослойка». У разных документов (например, у «Заказа клиента» и «Реализации») табличные части могут называться одинаково («Товары»), иметь идентичный набор колонок, но при этом они относятся к разным объектам метаданных. Поэтому нам необходимы альтернативные способы идентификации.

Решение 1: Расширение структуры параметров (Рекомендуемый способ)

Если процедура общего модуля уже принимает на вход структуру параметров (как это часто бывает в типовых конфигурациях на базе БСП, например, параметр СтруктураДействий), самым надежным и «чистым» способом будет добавление информации о типе в эту структуру. Это не нарушает совместимость с другими вызовами и позволяет избежать ошибки «Недостаточно фактических параметров».

Рассмотрим по шагам, как это реализовать:

  1. В коде формы, перед вызовом общего модуля, добавим в структуру новое свойство.
  2. В общем модуле считаем это свойство и выполним логику в зависимости от типа.

Пример реализации в форме документа:


// Код в модуле формы
СтруктураДействий.Вставить("ТипВладельцаТЧ", ТипЗнч(Объект.Ссылка));
ОбработкаТабличнойЧастиСервер.ОбработатьТЧ(Объект.Товары, СтруктураДействий, КэшированныеЗначения);

Пример обработки в общем модуле:


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

Решение 2: Использование необязательного параметра

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

Проанализируем пример объявления такой процедуры:


Процедура ОбработатьТЧ(ТЧ, СтруктураДействий, КэшированныеЗначения, ДополнительныеПараметры = Неопределено) Экспорт
    // Если ДополнительныеПараметры не переданы, процедура не упадет с ошибкой
КонецПроцедуры

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

Решение 3: Определение типа через реквизит «Ссылка» в строке ТЧ

Существует техническая хитрость, позволяющая определить тип владельца без изменения параметров вызова. Каждая табличная часть документа потенциально имеет связь с владельцем через скрытый или явный реквизит. Если в табличной части на форме есть колонка, связанная с данными (например, Ссылка или любой типизированный реквизит документа), мы можем программно проверить тип этой колонки.

Важное условие: Чтобы данные были доступны в ДанныеФормыКоллекция на сервере, для соответствующего элемента формы должно быть установлено свойство «Использовать всегда». Посмотрим, как это работает:

  1. В конфигураторе откройте форму документа.
  2. Найдите реквизит Ссылка внутри табличной части (или добавьте его, если его нет в списке реквизитов формы).
  3. В свойствах этого реквизита установите флаг «Использовать всегда».

Теперь в общем модуле мы можем написать следующий код:


Процедура ОбработатьТЧ(ТЧ, ...) Экспорт
    Если ТЧ.Количество() > 0 Тогда
        ПерваяСтрока = ТЧ[0];
        // Если флаг "Использовать всегда" стоит, мы сможем получить тип ссылки документа
        ТипВладельца = ТипЗнч(ПерваяСтрока.Ссылка); 
    Иначе
        // Если ТЧ пустая, можно временно добавить строку, проверить тип и удалить её
        НоваяСтрока = ТЧ.Добавить();
        ТипВладельца = ТипЗнч(НоваяСтрока.Ссылка);
        ТЧ.Удалить(НоваяСтрока);
    КонецЕсли;
КонецПроцедуры

Этот метод кажется привлекательным, так как не меняет сигнатуру функций, однако он требует настройки каждой формы документа в конфигураторе, что может быть трудоемко при большом количестве объектов.

Решение 4: Проверка состава колонок (Метод исключения)

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

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


// В общем модуле на сервере
Колонки = ТЧ.Выгрузить().Колонки;
Если Колонки.Найти("Склад") <> Неопределено И Колонки.Найти("Менеджер") <> Неопределено Тогда
    // Вероятно, это РеализацияТоваровУслуг
КонецЕсли;

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

Решение 5: Метаданные и свойство «Родитель»

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

Если бы в модуль передавалась настоящая табличная часть объекта (типа РезультатЗапроса или ТабличнаяЧасть), мы могли бы использовать свойство Метаданные():


// Работает только для реальных объектов ТЧ, не для ДанныхФормы
ИмяДокумента = ТЧ.Метаданные().Родитель().Имя;

Однако в контексте управляемых форм и передачи данных «с экрана», этот метод напрямую не сработает без предварительного преобразования ДанныеФормыВЗначение, для которого, в свою очередь, опять же нужно знать тип объекта. Получается замкнутый круг.

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

Рассмотрев все варианты, мы можем сделать следующие выводы по выбору стратегии:

Помните, что архитектурно правильнее всегда передавать контекст (владельца) явно. Попытки «угадать» тип объекта внутри общего модуля по косвенным признакам часто приводят к трудноуловимым ошибкам при развитии системы. Выбирайте решение, которое обеспечит максимальную прозрачность кода для ваших коллег.

← На главную