При реализации механизмов интеграции между различными базами данных 1С с использованием COM-соединения программисты часто сталкиваются с ситуацией, когда код выполняется без ошибок, но данные в таблицах регистрации изменений плана обмена не появляются. Эта проблема может быть крайне коварной, так как визуально код выглядит абсолютно корректным. Рассмотрим подробнее причины такого поведения системы и разберем по шагам, как правильно реализовать регистрацию изменений во внешней базе.
Разберем ситуацию, с которой столкнулся автор темы. Процедура в базе «1С:Бухгалтерия» подключается к базе «1С:Зарплата и управление персоналом» через COM, находит нужный узел плана обмена и пытается зарегистрировать в нем данные. Однако регистрация не происходит.
Первая и самая важная причина кроется в архитектуре COM-соединения. Когда мы получаем объект (например, ссылку на узел плана обмена) из внешней базы, 1С создает так называемый прокси-объект. Если мы попытаемся сравнить две ссылки, полученные через COM, или сравнить ссылку из COM-базы с результатом метода ЭтотУзел(), стандартный оператор сравнения = или <> может вернуть неверный результат. Это происходит потому, что на уровне памяти это разные объекты-обертки, даже если они указывают на один и тот же элемент в базе данных.
Проанализируем фрагмент кода, который вызвал затруднение:
Если ВыборкаУзлов.Ссылка <> СоединениеСЗУП.ПланыОбмена.ЗиУП_БП_КОРП.ЭтотУзел() Тогда
Узел = ВыборкаУзлов.ПолучитьОбъект();
КонецЕсли;
В данном случае условие <> всегда может возвращать Истина, либо, наоборот, работать нестабильно. Для надежного сравнения объектов через COM-соединение необходимо использовать примитивные типы данных: Код, Наименование или УникальныйИдентификатор в строковом представлении.
Чтобы гарантированно отличить удаленный узел от текущего («ЭтотУзел») во внешней базе, следует сравнивать их коды. Выясним причину, почему это надежнее: код является строкой или числом, а примитивные типы передаются через COM-интерфейс без создания сложных прокси-структур, требующих сопоставления ссылок.
Рассмотрим исправленный вариант логики поиска узла:
// Получаем код текущего узла внешней базы
КодЭтогоУзла = СоединениеСЗУП.ПланыОбмена.ЗиУП_БП_КОРП.ЭтотУзел().Код;
ВыборкаУзлов = СоединениеСЗУП.ПланыОбмена.ЗиУП_БП_КОРП.Выбрать();
Пока ВыборкаУзлов.Следующий() Цикл
// Сравниваем коды, а не ссылки
Если ВыборкаУзлов.Код <> КодЭтогоУзла Тогда
УзелСсылка = ВыборкаУзлов.Ссылка;
// Теперь у нас есть верная ссылка на удаленный узел
Прервать;
КонецЕсли;
КонецЦикла;
Важный момент: регистрация изменений всегда производится только для удаленных узлов. Платформа 1С игнорирует попытки зарегистрировать изменения для узла, который возвращается методом ЭтотУзел() в текущем сеансе (в данном случае — в сеансе COM-соединения).
После того как мы получили корректную ссылку на узел, перейдем к самой регистрации. При работе с регистрами сведений важно помнить о режиме записи. Если регистр подчинен регистратору, необходимо регистрировать именно набор записей с установленным отбором по регистратору.
Проанализируем пример регистрации данных в регистре сведений РаботникиОрганизаций:
// Создаем набор записей во внешней базе через COM
НаборЗаписей = СоединениеСЗУП.РегистрыСведений.РаботникиОрганизаций.СоздатьНаборЗаписей();
// Устанавливаем отбор по регистратору (ссылка на регистратор должна быть из той же COM-базы!)
НаборЗаписей.Отбор.Регистратор.Установить(Выборка.Регистратор);
// Читаем данные (необязательно для регистрации, но полезно для проверки состава)
НаборЗаписей.Прочитать();
// Регистрируем изменения для конкретного узла
СоединениеСЗУП.ПланыОбмена.ЗарегистрироватьИзменения(УзелСсылка, НаборЗаписей);
Посмотрим на частую ошибку: попытку передать в метод ЗарегистрироватьИзменения объект, полученный в «родной» базе (например, в Бухгалтерии), вместо объекта из внешней базы (ЗУП). Все параметры метода должны принадлежать контексту СоединениеСЗУП.
Если после исправления логики сравнения узлов данные все равно не регистрируются, следует проверить метаданные во внешней базе (в данном примере — в ЗУП). Выполним следующие шаги:
ЗиУП_БП_КОРП.СотрудникиОрганизаций и регистр сведений РаботникиОрганизаций.Если объект отсутствует в составе плана обмена, метод ЗарегистрироватьИзменения отработает «молча», не вызывая исключения, но запись в таблице регистрации создана не будет.
Вызов методов через COM-соединение является достаточно ресурсоемкой операцией. Если нам нужно зарегистрировать тысячи объектов, выполнение метода ЗарегистрироватьИзменения в цикле приведет к значительным задержкам. Рассмотрим, как ускорить этот процесс.
Вместо того чтобы совершать сотни вызовов из основной базы, рекомендуется создать во внешней базе специальную экспортную процедуру в общем модуле. Эта процедура может принимать массив ссылок и выполнять регистрацию локально. Таким образом, мы совершим всего один COM-вызов вместо тысячи.
Пример структуры такой оптимизации:
В базе ЗУП (общий модуль):
Процедура ЗарегистрироватьМассивОбъектов(МассивСсылок, КодУзла) Экспорт
Узел = ПланыОбмена.ЗиУП_БП_КОРП.НайтиПоКоду(КодУзла);
Если Узел.Пустая() Тогда
Возврат;
КонецЕсли;
Для Каждого СсылкаИзМассива Из МассивСсылок Цикл
ПланыОбмена.ЗарегистрироватьИзменения(Узел, СсылкаИзМассива);
КонецЦикла;
КонецПроцедуры
В базе Бухгалтерия (вызов):
// Формируем массив идентификаторов или кодов во внешней базе
// И вызываем одну процедуру
СоединениеСЗУП.ИмяОбщегоМодуля.ЗарегистрироватьМассивОбъектов(МассивСсылокЗУП, "Узел001");
Подведем итог: для успешной регистрации данных в плане обмена через COM-соединение необходимо всегда использовать сравнение узлов по коду, убедиться, что регистрация идет на удаленный узел (не ЭтотУзел), и проверять вхождение объектов в состав плана обмена в конфигурации-приемнике.