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