Как правильно создавать элементы справочников и документы при обмене через COM-соединение в 1С?

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

Работа с технологией COMConnector — одна из классических задач при интеграции различных информационных баз на платформе 1С:Предприятие. Однако начинающие разработчики часто сталкиваются с неочевидными на первый взгляд ошибками, самой распространенной из которых является "Несоответствие типов" при попытке записи или проведения документов.

В этой статье мы подробно разберем, почему возникает эта ошибка, как правильно сопоставлять данные между базами-источниками и базами-приемниками, а также рассмотрим лучшие практики по синхронизации объектов, оптимизации производительности и администрированию COM-соединений.

Выясняем причину: почему возникает «Несоответствие типов»?

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

Причина кроется в физической природе COM-объектов. Переменная стр.Контрагент или стр.Организация во внешней выборке — это не привычная локальная ссылка типа СправочникСсылка.Контрагенты, а так называемый COM-объект (прокси-объект), указывающий на область памяти в другом процессе (или на другом сервере). Локальная база 1С ничего не знает о внутренних идентификаторах объектов удаленной базы и не может напрямую записать прокси-ссылку в реквизит своей таблицы данных.

Для успешного обмена нам необходимо «приземлить» полученные данные: извлечь из COM-объекта примитивные типы (строки, числа, даты) и по ним выполнить поиск или создание аналогичных объектов в локальной базе-приемнике.

---

Решение 1. Синхронизация по примитивным реквизитам (ИНН, КПП, Наименование)

Самый простой и очевидный способ сопоставления объектов — это поиск по уникальным текстовым реквизитам. Для контрагентов идеальной парой являются ИНН и КПП, а для организаций — ИНН. Если поиск не дал результатов, мы создаем новый элемент локально.

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

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


Функция НайтиИлиСоздатьКонтрагентаЛокально(Знач Наименование, Знач ИНН, Знач КПП) Экспорт
    
    // 1. Попытка поиска по ИНН и КПП (для юрлиц)
    Если Не ПустаяСтрока(ИНН) Тогда
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |   Контрагенты.Ссылка КАК Ссылка
            |ИЗ
            |   Справочник.Контрагенты КАК Контрагенты
            |ГДЕ
            |   Контрагенты.ИНН = &ИНН
            |   И (&БезКПП ИЛИ Контрагенты.КПП = &КПП)";
        
        Запрос.УстановитьПараметр("ИНН", ИНН);
        Запрос.УстановитьПараметр("КПП", КПП);
        Запрос.УстановитьПараметр("БезКПП", ПустаяСтрока(КПП));
        
        Выборка = Запрос.Выполнить().Выбрать();
        Если Выборка.Следующий() Тогда
            Возврат Выборка.Ссылка;
        КонецЕсли;
    КонецЕсли;
    
    // 2. Попытка поиска по наименованию, если по ИНН не нашли
    СсылкаПоНаименованию = Справочники.Contragents.НайтиПоНаименованию(Наименование, Истина);
    Если ЗначениеЗаполнено(СсылкаПоНаименованию) Тогда
        Возврат СсылкаПоНаименованию;
    КонецЕсли;
    
    // 3. Создание нового контрагента, если объект не найден
    НовыйКонтрагент = Справочники.Контрагенты.СоздатьЭлемент();
    НовыйКонтрагент.Наименование = Наименование;
    НовыйКонтрагент.НаименованиеПолное = Наименование;
    НовыйКонтрагент.ИНН = ИНН;
    НовыйКонтрагент.КПП = КПП;
    
    // Заполняем другие обязательные реквизиты (например, ВидКонтрагента)
    НовыйКонтрагент.ЮридическоеФизическоеЛицо = Перечисления.ЮридическоеФизическоеЛицо.ЮрЛицо;
    
    Попытка
        НовыйКонтрагент.Записать();
        Возврат НовыйКонтрагент.Ссылка;
    Исключение
        ЗаписьЖурналаРегистрации("ОбменДанными", УровеньЖурналаРегистрации.Ошибка, , , 
            "Не удалось записать контрагента: " + ОписаниеОшибки());
        Возврат Справочники.Контрагенты.ПустаяСсылка();
    КонецПопытки;

КонецФункции

---

Решение 2. Синхронизация по Уникальному Идентификатору (UUID / GUID)

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

Передать ссылку напрямую через COM нельзя, но мы можем получить строковое представление UUID из базы-источника, передать его как строку и применить в базе-приемнике. Для этого используется метод контекста COM-соединения String().

Разберем подробнее алгоритм интеграции с использованием UUID:


// В цикле обработки результатов COM-запроса:
Для Каждого СтрИзИсточника Из РезультатЗапроса COM Цикл
    
    // Получаем UUID контрагента из COM-базы в виде строки
    СтрокаUUID = Подключение.String(СтрИзИсточника.Контрагент.УникальныйИдентификатор());
    НаименованиеКонтрагента = Подключение.String(СтрИзИсточника.Контрагент.Наименование);
    
    // Преобразуем строку в локальный УникальныйИдентификатор
    ЛокальныйUUID = Новый УникальныйИдентификатор(СтрокаUUID);
    
    // Ищем локальную ссылку по полученному UUID
    ЛокальнаяСсылкаКонтрагента = Справочники.Контрагенты.ПолучитьСсылку(ЛокальныйUUID);
    
    // Проверяем, существует ли физически элемент в базе с такой ссылкой
    Если ЛокальнаяСсылкаКонтрагента.ПолучитьОбъект() = Неопределено Тогда
        
        // Создаем новый элемент справочника
        НовыйКонтрагент = Справочники.Контрагенты.СоздатьЭлемент();
        
        // КРИТИЧЕСКИ ВАЖНО: Устанавливаем ту же ссылку, что и в базе-источнике!
        НовыйContragent.УстановитьСсылкуНового(ЛокальнаяСсылкаКонтрагента);
        
        НовыйКонтрагент.Наименование = НаименованиеКонтрагента;
        // Заполняем остальные реквизиты...
        
        Попытка
            НовыйКонтрагент.Записать();
        Исключение
            Сообщить("Ошибка записи контрагента: " + ОписаниеОшибки());
            Продолжить;
        КонецПопытки;
        
    КонецЕсли;
    
    // Теперь у нас есть гарантированно сопоставленная локальная ссылка
    // Используем ее при создании локального документа
    НовыйДокумент = Документы.ОтчетКомиссионераОПродажах.СоздатьДокумент();
    НовыйДокумент.Дата = СтрИзИсточника.Дата;
    НовыйДокумент.Контрагент = ЛокальнаяСсылкаКонтрагента;
    // Заполнение остальных реквизитов...
    НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);

КонецЦикла;

Использование метода УстановитьСсылкуНового() позволяет гарантировать, что элементы справочника в обеих базах будут иметь абсолютно идентичные UUID. Это навсегда решает проблему повторного поиска и исключает появление дублей.

---

Оптимизация производительности: избегаем "COM-границ"

Рассмотрим важную архитектурную проблему, из-за которой внешние обработки обмена могут работать часами. В коде автора темы обход результатов запроса выполняется напрямую: Для Каждого стр Из Результат Цикл. Если в результате запроса 5000 строк, то при каждом обращении к свойству (например, стр.Дата, стр.Организация) платформа осуществляет вызов через границу процессов операционной системы (cross-process call).

Тысячи таких сетевых обращений катастрофически снижают производительность. Как делать правильно?

  1. Выполняйте запрос на стороне COM-соединения.
  2. Вместо прямой передачи таблицы значений, сверните или сериализуйте данные на стороне источника в строку JSON или XML.
  3. Передайте одну большую текстовую строку JSON в базу-приемник за ОДИН COM-вызов.
  4. В базе-приемнике десериализуйте JSON в локальную таблицу значений или структуру и обрабатывайте данные на максимальной скорости внутри одного процесса.

---

Важные аспекты администрирования и стабильности COM

При промышленной эксплуатации интеграционных решений на базе COM-соединений часто возникают инфраструктурные сложности. Проанализируем основные из них:

Альтернативный взгляд: современные интеграционные механизмы

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

  1. HTTP-сервисы (REST API): Создайте в базе-источнике HTTP-сервис, который будет отдавать данные в формате JSON по GET-запросу. Это работает быстро, не зависит от операционной системы и не требует сложной настройки прав и COM-компонент в Windows.
  2. Web-сервисы (SOAP): Позволяют организовать строго типизированный обмен между конфигурациями.
  3. Планы обмена и формат EnterpriseData: Стандартизированный формат XML-обмена, разработанный фирмой 1С, который поддерживается большинством типовых конфигураций "из коробки" и позволяет настроить синхронизацию данных за несколько минут.
← На главную