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