Почему при обмене данными через Конвертацию данных вместо подразделения загружается ссылка на документ?

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

При настройке нетипового обмена данными между конфигурациями «Зарплата и управление персоналом 3» (ЗУП) и «Управление производственным предприятием 1.3» (УПП) разработчики часто сталкиваются с нетипичным поведением платформы. Одна из самых труднодиагностируемых ошибок — когда при загрузке документа (например, «Отражение зарплаты в регл. учете») в табличной части вместо ссылки на справочник ПодразделенияОрганизаций внезапно оказывается ссылка на сам загружаемый документ.

В этой статье мы подробно разберем, почему возникает этот сбой, проанализируем внутренние механизмы работы подсистемы обмена «Конвертация данных 2.0» (КД 2), выясним причину коллизии номеров поиска (Нпп) и рассмотрим три надежных способа решения этой проблемы.

В чем суть проблемы и как она проявляется

Рассмотрим реальную ситуацию. Выполняется перенос данных из ЗУП 3 в УПП 1.3. В исходной базе в табличной части документа содержится информация о подразделениях. Для переноса этих данных используется нетиповое правило обмена. На стороне источника табличная часть упаковывается в объект ХранилищеЗначения и передается в качестве параметра объекта. При загрузке в базу-приемник выполняется ручной парсинг XML-строки, извлеченной из хранилища.

При этом в коде загрузки используется следующий алгоритм:


НачисленнаяЗарплатаИВзносы    = Новый ТаблицаЗначений;
ХР    = ПараметрыОбъекта.Получить("ТабЧасть_НачисленнаяЗарплатаИВзносы");
    
Если ТипЗнч(ХР) = Тип("ХранилищеЗначения") Тогда
        
    НачисленнаяЗарплатаИВзносы    = ХР.Получить();
    Если ТипЗнч(НачисленнаяЗарплатаИВзносы) = Тип("ТаблицаЗначений") Тогда 
        НачисленнаяЗарплатаИВзносы.Колонки.Добавить("ПодразделениеСсылка");
            
        Для Каждого СтрТЧ Из НачисленнаяЗарплатаИВзносы  Цикл
            Если ЗначениеЗаполнено(СтрТЧ.Подразделение) Тогда
                ЧтениеСсылки    = Новый ЧтениеXML;
                ЧтениеСсылки.УстановитьСтроку(СтрТЧ.Подразделение);
                Если ЧтениеСсылки.Прочитать() Тогда
                    НппСсылки    = одАтрибут(ЧтениеСсылки, odTipChislo, "Нпп");
                    Если ЗначениеЗаполнено(НппСсылки) Тогда
                        СтрТЧ.ПодразделениеСсылка = НайтиОбъектПоНомеру(НппСсылки);
                    КонецЕсли;
                КонецЕсли;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
КонецЕсли;

Во время выполнения этого кода в лог отладки попадает крайне странная информация. XML-структура, которая считывается из строки, явно описывает объект типа СправочникСсылка.ПодразделенияОрганизаций с уникальным идентификатором, кодом и владельцем-организацией. Ей присвоен внутренний номер по порядку (атрибут Нпп="26"):


<Ссылка Нпп="26" НеСоздаватьЕслиНеНайден="true">
    <Свойство Имя="{УникальныйИдентификатор}" Тип="Строка">
        <Значение>f31346db-d642-11e6-ad63-00155d007500</Значение>
    </Свойство>
    <Свойство Имя="Владелец" Тип="СправочникСсылка.Организации">
        <Ссылка Нпп="2" НеСоздаватьЕслиНеНайден="true">
            ...
        </Ссылка>
    </Свойство>
    <Свойство Имя="Код" Тип="Строка">
        <Значение>ПГ-000005</Значение>
    </Свойство>
</Ссылка>

Однако при выполнении функции НайтиОбъектПоНомеру(26) платформа возвращает ссылку на сам загружаемый документ: «Отражение зарплаты в регл учете ОТК-000011 от 30.11.2018». В результате реквизит табличной части Подразделение остается незаполненным, так как тип полученного значения не соответствует ожидаемому типу справочника.

Выясняем причину коллизии (механизм работы Нпп в КД 2)

Давайте подробно разберем, как работает сквозная нумерация объектов в XML-файле обмена 1С и почему возникает подобное наложение данных.

При выгрузке данных обработка обмена присваивает каждому уникальному объекту свой порядковый номер — Нпп (Номер по порядку). Этот механизм предназначен для оптимизации размера файла обмена и кэширования объектов в оперативной памяти. Если объект выгружается несколько раз (например, одно и то же подразделение используется во многих строках документа), в файл записывается подробная информация о нем только один раз с атрибутом Нпп. При последующих обращениях выгружается только короткая ссылка вида <Ссылка Нпп="26" />.

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

ЗагруженныеОбъекты[Нпп] = Ссылка;

В нашем случае коллизия возникает из-за использования ХранилищеЗначения для передачи ТЗ:

  1. При выгрузке документа на стороне источника табличная часть принудительно преобразуется в двоичные данные (Base64) и записывается в параметр. Главный парсер XML при этом «не видит» внутреннюю структуру XML-строк подразделений, скрытых внутри хранилища данных. Он воспринимает параметр просто как набор байтов.
  2. В то же время, при формировании XML-кода самого документа, генератор файлов обмена назначает порядковые номера выгружаемым объектам. Случилось так, что самому загружаемому документу (или другому объекту основного потока XML) был назначен тот же самый номер Нпп="26".
  3. Во время импорта парсер сначала обрабатывает узел документа. Он успешно считывает документ, создает его ссылку в базе и записывает в кэш: ЗагруженныеОбъекты[26] = СсылкаНаДокумент.
  4. Затем, уже в обработчике «После загрузки» документа, разработчик программно извлекает таблицу значений из ХранилищаЗначения, запускает вспомогательный объект ЧтениеXML и начинает парсить строку. Из строки считывается номер Нпп="26". Вызывается функция НайтиОбъектПоНомеру(26).
  5. Поскольку в глобальной таблице ЗагруженныеОбъекты под индексом 26 уже записана ссылка на сам документ, функция добросовестно возвращает её вместо подразделения. Происходит логический сбой.

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

Способы решения проблемы

Рассмотрим три варианта исправления этой ошибки — от быстрого «костыля» в коде до правильного архитектурного рефакторинга правил обмена.

Вариант 1. Использование штатного параметра типа «ТаблицаЗначений» (Рекомендуемый)

Самый надежный способ — отказаться от использования ХранилищеЗначения для упаковки табличных частей. Конвертация данных 2.0 отлично умеет работать с таблицами значений напрямую в качестве параметров объектов.

Разберем этот вариант по шагам:

  1. В правилах конвертации объектов (ПКО) вашего документа перейдите на вкладку «Параметры».
  2. Добавьте новый параметр (например, ТабЧасть_НачисленнаяЗарплатаИВзносы) и укажите для него тип значения ТаблицаЗначений.
  3. В обработчике «Перед выгрузкой» самого ПКО документа заполните этот параметр обычной таблицей значений. Важно: колонки таблицы должны содержать реальные ссылки на объекты источника (включая ссылки на подразделения), а не их XML-представления. Например:

ТаблицаПараметра = Новый ТаблицаЗначений;
ТаблицаПараметра.Колонки.Добавить("Подразделение");
ТаблицаПараметра.Колонки.Добавить("Сумма");

Для Каждого СтрокаТЧ Из Источник.Начисления Цикл
    НоваяСтрока = ТаблицаПараметра.Добавить();
    НоваяСтрока.Подразделение = СтрокаТЧ.Подразделение;
    НоваяСтрока.Сумма = СтрокаТЧ.Сумма;
КонецЦикла;

Параметры.Вставить("ТабЧасть_НачисленнаяЗарплатаИВзносы", ТаблицаПараметра);

При такой настройке КД 2 автоматически сериализует таблицу значений. Она выгрузит подразделения как полноценные объекты обмена, сопоставит их по правилам конвертации свойств (ПКО) подразделений, выделит им уникальные неконфликтующие номера Нпп и гарантированно запишет их в файл обмена.

При загрузке в базу-приемник в обработчике «После загрузки» документа вам больше не потребуется использовать ЧтениеXML и парсить строки вручную. Вы получите готовую таблицу значений, в которой уже будут находиться сопоставленные ссылки базы-приемника:


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

Вариант 2. Настройка обмена через ПКГС и входящие данные

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

Рассмотрим алгоритм настройки:

  1. В ПКО документа создайте новую группу свойств (ПКГС) для вашей табличной части приемника. Поле «Источник» для этой группы оставьте незаполненным.
  2. Внутри этой группы свойств создайте ПКС (Правила конвертации свойств) для каждого реквизита табличной части (включая Подразделение).
  3. В обработчике «Перед выгрузкой» этой группы свойств (ПКГС) сформируйте таблицу значений на основе данных источника. Колонки таблицы должны называться в точности так же, как реквизиты табличной части документа-приемника. Передайте эту таблицу в коллекцию ВходящиеДанные:

ТаблицаДляВыгрузки = Новый ТаблицаЗначений;
ТаблицаДляВыгрузки.Колонки.Добавить("Подразделение");
ТаблицаДляВыгрузки.Колонки.Добавить("Сумма");

// Заполняем таблицу на основе данных Источника...
// ...

ВходящиеДанные = ТаблицаДляВыгрузки;

При такой схеме КД 2 самостоятельно выполнит всю рутинную работу: обойдет строки переданной таблицы, применит ПКО для поиска и сопоставления подразделений и заполнит табличную часть документа-приемника без написания сложного кода загрузки.

Вариант 3. Быстрое исправление кода («костыль»)

Если вам необходимо срочно исправить ошибку в уже работающей системе и у вас нет времени на перепроектирование правил обмена, можно применить быстрое программное решение.

Во-первых, на стороне выгрузки (в источнике) перед сериализацией подразделения в XML-строку необходимо принудительно заставить КД выгрузить объект в основной поток XML-файла. Это позволит выделить ему корректный уникальный номер Нпп:

ВыгрузитьПоПравилу(СтрокаТЗ.Подразделение, , , , "ПодразделенияОрганизаций");

Во-вторых, на стороне загрузки (в приемнике) при вызове функции поиска объекта обязательно указывайте второй параметр — тип ожидаемого значения. Это позволит подсистеме обмена (особенно при интеграции через БСП) корректно фильтровать кэш объектов по типам метаданных:


СтрТЧ.ПодразделениеСсылка = НайтиОбъектПоНомеру(НппСсылки, "СправочникСсылка.ПодразделенияОрганизаций");

Этот простой шаг исключит ситуацию, когда вместо подразделения функция возвращает документ, даже если их внутренние идентификаторы Нпп в XML-файле совпали.

Резюме

Мы проанализировали причину сбоя при обмене данными между конфигурациями ЗУП и УПП. Использование ручной сериализации ссылок в XML через ХранилищеЗначения часто приводит к коллизиям в механизме адресации Нпп Конвертации данных 2.0.

Для предотвращения подобных ошибок всегда старайтесь использовать штатные механизмы КД 2 — передачу таблиц значений через встроенные параметры или настройку ПКГС с использованием входящих данных. Это сделает ваши правила обмена стабильными, быстрыми и простыми в поддержке.

← На главную