Как передать значение из табличной части в реквизит шапки в 1С:Конвертация данных 2.1

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

При настройке правил обмена в конфигурации «1С:Конвертация данных 2.1» (КД 2) разработчики часто сталкиваются с необходимостью перенести информацию "снизу вверх" — то есть взять значение из какой-либо строки табличной части (ТЧ) документа-источника и записать его в реквизит шапки документа-приемника. На первый взгляд задача кажется простой, но при её реализации в обработчиках событий легко совершить ошибку, которая приведет к аварийному завершению выгрузки.

В этой статье мы подробно разберем, почему возникает популярная ошибка Метод объекта не обнаружен (УникальныйИдентификатор), рассмотрим правильные способы построения запросов к табличным частям, научимся безопасно обращаться к коллекциям напрямую и разберем наиболее архитектурно верный подход с использованием механизма исходящих данных.

В качестве примера рассмотрим следующую задачу: в документе-источнике есть табличная часть Транзит с реквизитом Касса. Нам необходимо передать значение этой кассы из первой строки ТЧ в реквизит шапки ПодразделениеКТ документа-приемника Приходный кассовый ордер.

Выясняем причину исходной ошибки

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


Если (Источник.ВидОперации = Перечисления.ВидыОперацийПКО.асТранзит) Тогда
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    ПриходныйКассовыйОрдер.Транзит.(
        |        Касса.Ссылка
        |    ) КАК Касса
        |ИЗ
        |    Документ.ПриходныйКассовыйОрдер КАК ПриходныйКассовыйОрдер
        |ГДЕ
        |    ПриходныйКассовыйОрдер.Ссылка = &Ссылка";
    Запрос.УстановитьПараметр("Ссылка", Источник.Ссылка);
    
    Выборка = Запрос.Выполнить().Выбрать();    
    Пока Выборка.Следующий() Цикл    
        Сообщить(Выборка.Касса);
        Значение = Выборка.Касса;
    КонецЦикла;
КонецЕсли;

При выполнении этого кода система выдает ошибку:

{Обработка.КонвертацияОбъектовИнформационныхБаз.МодульОбъекта(9411)}: Метод объекта не обнаружен (УникальныйИдентификатор) УникальныйИдентификатор = СсылкаНаИсточник.УникальныйИдентификатор();

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

Когда платформа 1С доходит до выгрузки свойства, она ожидает, что в переменной Значение находится конкретная ссылка на объект (например, СправочникСсылка.Кассы). Конвертация данных пытается получить уникальный идентификатор (GUID) этой ссылки, чтобы записать его в XML-файл выгрузки. Но вместо ссылки в переменную Значение попал объект выборки вложенной таблицы. У этого объекта, разумеется, нет метода УникальныйИдентификатор(), что и приводит к падению платформы.

Давайте разберем три надежных способа решения этой задачи.

Решение 1: Прямое обращение к коллекции табличной части

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

Реализуем этот алгоритм в обработчике ПередВыгрузкой для ПКС ПодразделениеКТ:


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

Этот способ прекрасно работает, он лаконичен и понятен. Однако у него есть ограничение: он жизнеспособен только тогда, когда в правилах обмена выгрузка осуществляется с передачей самого объекта Источник. Если объект выгружается "по ссылке" (например, при выгрузке связанных данных из других правил, когда Источник равен Неопределено, а доступна только переменная Ссылка), этот код вызовет ошибку обращения к свойству неинициализированного объекта.

Решение 2: Корректный запрос к табличной части

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

Перепишем наш запрос в обработчике ПередВыгрузкой ПКС правильно:


// Определяем источник данных для запроса
СсылкаДляЗапроса = Неопределено;
Если Источник <> Неопределено Тогда
    СсылкаДляЗапроса = Источник.Ссылка;
ИначеЕсли ЗначениеЗаполнено(Ссылка) Тогда
    СсылкаДляЗапроса = Ссылка;
КонецЕсли;

Если ЗначениеЗаполнено(СсылкаДляЗапроса) Тогда
    // Выполняем проверку вида операции (если выгружаем по ссылке, её тоже нужно получить запросом)
    ВидОперацииДокумента = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаДляЗапроса, "ВидОперации");
    
    Если (ВидОперацииДокумента = Перечисления.ВидыОперацийПКО.асТранзит) Тогда
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ ПЕРВЫЕ 1
            |    ТранзитТабличнаяЧасть.Касса КАК Касса
            |ИЗ
            |    Документ.ПриходныйКассовыйОрдер.Транзит КАК ТранзитТабличнаяЧасть
            |ГДЕ
            |    ТранзитТабличнаяЧасть.Ссылка = &Ссылка";
        
        Запрос.УстановитьПараметр("Ссылка", СсылкаДляЗапроса);
        Выборка = Запрос.Выполнить().Выбрать();
        
        Если Выборка.Следующий() Тогда
            Если ЗначениеЗаполнено(Выборка.Касса) Тогда
                Значение = Выборка.Касса;
            Иначе
                Сообщить("В первой строке ТЧ документа " + СсылкаДляЗапроса + " не заполнена Касса!");
                Отказ = Истина;
            КонецЕсли;
        Иначе
            Сообщить("В документе " + СсылкаДляЗапроса + " пустая табличная часть Транзит!");
            Отказ = Истина;
        КонецЕсли;
    КонецЕсли;
КонецЕсли;

При таком подходе запрос вернет обычную ссылку на элемент справочника Кассы. Правила конвертации данных легко распознают этот тип, сопоставят его с правилом конвертации свойств и корректно запишут уникальный идентификатор в XML-выгрузку.

Решение 3: Лучшая практика КД — Использование исходящих данных

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

Рекомендуется использовать механизм «Исходящих данных» на уровне самого правила конвертации объекта (ПКО) документа. Это позволяет собрать всю логику подготовки данных в одном месте.

Разберем этот метод по шагам:

  1. Откроем настройки нашего ПКО для документа ПриходныйКассовыйОрдер.
  2. Перейдем в обработчик события ПередВыгрузкой самого ПКО и напишем следующий код:

Если Источник <> Неопределено Тогда
    Если (Источник.ВидОперации = Перечисления.ВидыОперацийПКО.асТранзит) Тогда
        Если Источник.Транзит.Количество() > 0 Тогда
            // Создаем структуру для передачи параметров в подчиненные ПКС
            ИсходящиеДанные = Новый Структура;
            ИсходящиеДанные.Вставить("ПодразделениеКТ", Источник.Транзит[0].Касса);
        Иначе
            Сообщить("Документ " + Источник.Ссылка + " не будет выгружен: пустая ТЧ Транзит!");
            Отказ = Истина;
        КонецЕсли;
    КонецЕсли;
КонецЕсли;
  1. Теперь перейдем к настройке ПКС ПодразделениеКТ. В свойствах этого ПКС необходимо установить флаг «Получить из входящих данных».
  2. Если по каким-то причинам этот флаг установить нельзя или вы хотите контролировать процесс в коде, достаточно в обработчике ПередВыгрузкой этого ПКС написать всего одну строку:

Значение = ВходящиеДанные.ПодразделениеКТ;

В чем ключевые преимущества использования исходящих данных?

Используйте этот подход в своих проектах интеграции, чтобы ваши правила конвертации оставались производительными, чистыми и простыми в поддержке!

← На главную