При интеграции систем на платформе 1С с использованием механизма Enterprise Data (ED) часто возникает потребность в передаче информации не только о созданных или измененных объектах, но и об удаленных. Для этих целей в 1С существует специальный служебный объект — УдалениеОбъекта, который регистрируется планами обмена при пометке объекта на удаление — для контроля этого процесса пригодится управление регистрацией изменений объектов на планах обмена. Однако, когда мы пытаемся выгрузить этот объект через стандартные методы Enterprise Data, например, используя функцию ОбменДаннымиXDTOСервер.ВыгрузкаОбъектаВыборки(), мы сталкиваемся с ошибкой, связанной с невозможностью доступа к метаданным объекта.
В этом руководстве мы подробно разберем, почему возникает эта проблема, и рассмотрим различные альтернативные подходы к корректной выгрузке информации об удаленных объектах через Enterprise Data, как при работе с планами обмена, так и без них.
Прежде всего, давайте выясним, что представляет собой объект УдалениеОбъекта в контексте 1С. В отличие от привычных нам прикладных объектов, таких как Справочник.Номенклатура или Документ.РеализацияТоваровУслуг, объект УдалениеОбъекта является системным, служебным объектом. Он не имеет собственного представления в метаданных конфигурации в том же смысле, как обычные объекты. Его основная задача — сигнализировать механизму обмена данными о факте пометки на удаление или физического удаления какого-либо прикладного объекта.
Когда мы пытаемся вызвать метод ВыгрузкаОбъектаВыборки() для УдалениеОбъекта, система пытается обратиться к его метаданным, чтобы определить структуру и правила выгрузки. Но поскольку у УдалениеОбъекта нет полноценных метаданных, как у прикладных объектов, попытка обращения к свойству Метаданные для такого объекта приводит к ошибке. Это подтверждается сообщениями на форуме: «Для объекта УдалениеОбъекта нет возможности обратиться к методу Метаданные». Таким образом, стандартные универсальные методы выгрузки объектов Enterprise Data, разработанные для работы с прикладными данными, оказываются непригодными для этого специального служебного объекта.
Из этого следует критически важный вывод: вместо попытки выгрузить сам объект УдалениеОбъекта, нам необходимо извлечь из него информацию об удалении (тип и УИД удаленного объекта) и сформировать эту информацию в виде сообщения, которое будет понятно принимающей стороне. Рассмотрим, как мы можем это реализовать.
Для успешной передачи информации об удаленных объектах через Enterprise Data, нам потребуется реализовать собственную логику — в этом поможет обработка для обмена данными в формате EnterpriseData между конфигурациями 1С. Мы рассмотрим три основных подхода, каждый из которых применим в зависимости от специфики нашей интеграции и использования планов обмена.
Это основной и наиболее универсальный подход. Мы не выгружаем УдалениеОбъекта напрямую, а используем его как источник информации. Нам понадобится извлечь из него ключевые данные: тип удаленного объекта и его уникальный идентификатор (УИД). Затем мы формируем собственное сообщение, содержащее эти данные, и передаем его через Enterprise Data. На стороне получателя это сообщение будет интерпретировано для выполнения фактического удаления или пометки на удаление соответствующего объекта.
Разберем шаги реализации:
Обход зарегистрированных объектов: Когда мы получаем выборку зарегистрированных объектов для обмена, мы должны проверять тип каждого объекта.
Идентификация УдалениеОбъекта: Если текущий объект в выборке имеет тип УдалениеОбъекта, мы перехватываем его обработку, не передавая в стандартный метод выгрузки.
Извлечение информации: Из объекта УдалениеОбъекта мы получаем значения его свойств ТипУдаляемогоОбъекта и УИДУдаляемогоОбъекта.
Формирование пользовательского сообщения: Мы создаем новую структуру данных или XDTO-объект, который будет содержать эту информацию. Например, это может быть XML-элемент <УдалениеОбъекта> с дочерними элементами <Тип> и <УИД>.
Включение сообщения в поток ED: Это сформированное сообщение затем добавляется в общий пакет данных, который выгружается Enterprise Data. Способ добавления зависит от реализации Enterprise Data – это может быть добавление в специальную коллекцию корневого объекта обмена, или запись в специализированный поток.
Посмотрим на пример псевдокода, иллюстрирующий этот подход:
// Предполагаем, что у нас есть КомпонентыОбмена (объект XDTOФабрика)
// и ОбъектОбмена - текущий объект из выборки зарегистрированных для выгрузки.
Если ТипЗнч(ОбъектОбмена) = Тип("УдалениеОбъекта") Тогда
// ОбъектОбмена является "УдалениеОбъекта", стандартный метод не подходит.
// Извлекаем необходимую информацию из объекта "УдалениеОбъекта"
ТипУдаляемогоОбъекта = ОбъектОбмена.ТипУдаляемогоОбъекта;
УИДУдаляемогоОбъекта = ОбъектОбмена.УИДУдаляемогоОбъекта;
// Формируем собственное сообщение об удалении.
// Это может быть Структура, Соответствие или специально созданный XDTO-объект.
// Для примера создадим XDTO-объект, соответствующий определенной схеме.
// Предположим, у нас есть XDTO-пакет "МойОбмен" с типом "ИнформацияОбУдалении",
// который содержит свойства "ИмяТипаОбъекта" и "ИдентификаторОбъекта".
Попытка
ИнформацияОбУдалении = КомпонентыОбмена.ФабрикаXDTO.Создать(
КомпонентыОбмена.ФабрикаXDTO.Пакеты.МойОбмен.ПолучитьТип("ИнформацияОбУдалении")
);
ИнформацияОбУдалении.ИмяТипаОбъекта = ТипУдаляемогоОбъекта.Имя; // Получаем строковое имя типа
ИнформацияОбУдалении.ИдентификаторОбъекта = Строка(УИДУдаляемогоОбъекта); // УИД как строка
// Теперь этот XDTO-объект "ИнформацияОбУдалении" нужно добавить
// в выгружаемый пакет Enterprise Data.
// Если КомпонентыОбмена - это объект, который управляет выгрузкой,
// то у него может быть метод для добавления таких специализированных сообщений.
// Например:
// КомпонентыОбмена.ДобавитьКВыгрузке(ИнформацияОбУдалении);
// Или, если вы формируете корневой XDTO-объект обмена вручную,
// вы добавляете "ИнформацияОбУдалении" в специальную коллекцию или свойство
// этого корневого объекта.
// Пример (псевдокод, если КомпонентыОбмена.КорневойОбъектВыгрузки существует):
// КомпонентыОбмена.КорневойОбъектВыгрузки.УдаленныеОбъекты.Добавить(ИнформацияОбУдалении);
// Важно: После обработки объекта "УдалениеОбъекта", мы должны прекратить
// его дальнейшую обработку стандартными методами.
Продолжить; // Переходим к следующему объекту выборки, пропуская стандартную выгрузку
Исключение
ЗаписьЖурналаРегистрации("Ошибка при формировании сообщения об удалении: " + ОписаниеОшибки(),
УровеньЖурналаРегистрации.Ошибка,
,
,
"ОбработкаУдаленияОбъектаED");
// Возможно, здесь стоит логировать ошибку и, возможно, пометить объект как необработанный
Продолжить;
КонецПопытки;
КонецЕсли;
// Для остальных объектов, не являющихся "УдалениеОбъекта", продолжаем стандартную выгрузку:
// ОбменДаннымиXDTOСервер.ВыгрузкаОбъектаВыборки(КомпонентыОбмена, ОбъектОбмена);
На стороне приемника, когда мы получаем этот XDTO-объект ИнформацияОбУдалении, мы сможем по его ИмяТипаОбъекта и ИдентификаторОбъекта найти соответствующий объект в своей базе и выполнить его пометку на удаление или физическое удаление.
Если ваша система Enterprise Data инициализирована с использованием планов обмена (т.е., параметр ЭтоОбменЧерезПланОбмена = Истина), тогда механизм регистрации изменений 1С автоматически будет регистрировать объекты УдалениеОбъекта. В этом сценарии мы можем рассматривать УдалениеОбъекта не как данные, которые нужно выгрузить, а как сигнал о том, что некий прикладной объект был удален.
Наша задача будет заключаться в следующем:
Получение зарегистрированного объекта: В процессе обхода зарегистрированных планом обмена объектов мы получаем объект УдалениеОбъекта.
Извлечение данных: Аналогично предыдущему подходу, мы извлекаем из него ТипУдаляемогоОбъекта и УИДУдаляемогоОбъекта.
Вызов специализированной логики: Вместо вызова универсального метода выгрузки, мы передаем эту информацию в нашу собственную, специфическую для обмена процедуру или функцию. Эта функция должна знать, как корректно встроить информацию об удалении в формат, который ожидает принимающая система.
Формирование специфичного сообщения: Эта специализированная логика может формировать отдельный XML-узел, добавлять запись в специальную таблицу в файле обмена, или использовать любой другой определенный нами протокол для передачи сведений об удалении. Важно, чтобы этот формат был четко согласован с принимающей стороной.
Этот подход особенно полезен, когда у нас уже есть хорошо проработанная логика обмена, и мы просто хотим добавить в нее возможность обработки удалений, используя встроенный механизм регистрации изменений 1С как триггер.
Ситуация становится более сложной, если Enterprise Data инициализирован без использования планов обмена (параметр ЭтоОбменЧерезПланОбмена = Ложь). В этом режиме 1С не ведет автоматическую регистрацию изменений, включая удаления. Следовательно, объект УдалениеОбъекта не будет регистрироваться и доступен через выборку плана обмена.
В таком случае ответственность за отслеживание и передачу информации об удаленных объектах полностью ложится на разработчика. Это требует построения собственной системы учета удалений. Разберем этот многоэтапный процесс:
Мы будем вести свой собственный журнал всех объектов, которые были помечены на удаление или физически удалены — для этого подойдёт расширение для ведения журнала изменений и фиксации удаления объектов. Для этого удобно использовать РегистрСведений или даже отдельный специализированный документ. В этом журнале нам необходимо хранить как минимум следующую информацию:
Для того чтобы заполнять этот журнал, нам потребуется подписаться на события удаления прикладных объектов. Наиболее надежный способ — это использование обработчиков событий ПередУдалением для каждого прикладного объекта, чьи удаления мы хотим отслеживать.
Посмотрим на пример псевдокода для подписки на событие ПередУдалением:
// Пример модуля объекта (например, СправочникОбъект.Номенклатура)
Процедура ПередУдалением(Отказ)
Если Отказ Тогда
Возврат; // Если отмена удаления, то ничего не регистрируем
КонецЕсли;
// Записываем информацию об удаляемом объекте в наш собственный регистр сведений
Попытка
МенеджерЗаписи = РегистрыСведений.МойЖурналУдалений.СоздатьМенеджерЗаписи();
МенеджерЗаписи.УИДУдаленногоОбъекта = ЭтотОбъект.УИД;
МенеджерЗаписи.ИмяТипаУдаленногоОбъекта = Метаданные().Имя; // Получаем строковое имя типа
МенеджерЗаписи.ДатаУдаления = ТекущаяДата();
МенеджерЗаписи.ПомеченКВыгрузке = Истина; // Помечаем, что запись ожидает выгрузки
МенеджерЗаписи.Записать();
Исключение
ЗаписьЖурналаРегистрации("Ошибка при регистрации удаления объекта: " + ОписаниеОшибки()
+ " Объект: " + Строка(ЭтотОбъект) + ", УИД: " + Строка(ЭтотОбъект.УИД),
УровеньЖурналаРегистрации.Ошибка,
,
,
"ОтслеживаниеУдалений");
// Возможно, стоит отменить удаление, если не удалось зарегистрировать
// Отказ = Истина;
КонецПопытки;
КонецПроцедуры;
Важное замечание: Подписки на события ПриЗаписи или ПередЗаписью, а также ПриУдалении должны быть реализованы для всех типов объектов, изменения которых должны быть переданы через Enterprise Data. Убедитесь, что ваша логика обработки удаления объектов выполняется до их фактического удаления из базы данных.
При выполнении обмена через Enterprise Data, помимо выгрузки обычных измененных объектов, мы должны дополнительно выгрузить записи из нашего "журнала удалений". Это можно сделать как обычную выборку данных, поскольку записи в регистре сведений являются полноценными данными. Для каждой записи журнала мы формируем XDTO-объект или другую структуру, которая затем передается через Enterprise Data.
Пример псевдокода для выгрузки записей из журнала удалений:
// В процедуре инициализации Enterprise Data или в модуле обмена
// Определяем дату, с которой нужно выгрузить удаления.
// Это может быть дата последней успешной выгрузки удалений.
ДатаНачалаВыгрузкиУдалений = ПолучитьДатуПоследнейВыгрузкиУдалений(); // Ваша функция
Запрос = Новый Запрос();
Запрос.Текст = "ВЫБРАТЬ
| МойЖурналУдалений.УИДУдаленногоОбъекта КАК УИД,
| МойЖурналУдалений.ИмяТипаУдаленногоОбъекта КАК ИмяТипа,
| МойЖурналУдалений.ДатаУдаления КАК ДатаУдаления
|ИЗ
| РегистрСведений.МойЖурналУдалений КАК МойЖурналУдалений
|ГДЕ
| МойЖурналУдалений.ДатаУдаления >= &ДатаНачалаВыгрузкиУдалений
| И МойЖурналУдалений.ПомеченКВыгрузке = ИСТИНА"; // Выгружаем только те, что еще не выгружены
Запрос.УстановитьПараметр("ДатаНачалаВыгрузкиУдалений", ДатаНачалаВыгрузкиУдалений);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Для каждой записи формируем объект, который будет выгружен через ED.
// Используем тот же XDTO-тип "ИнформацияОбУдалении", что и в первом примере.
Попытка
ИнформацияОбУдалении = КомпонентыОбмена.ФабрикаXDTO.Создать(
КомпонентыОбмена.ФабрикаXDTO.Пакеты.МойОбмен.ПолучитьТип("ИнформацияОбУдалении")
);
ИнформацияОбУдалении.ИмяТипаОбъекта = Выборка.ИмяТипа;
ИнформацияОбУдалении.ИдентификаторОбъекта = Строка(Выборка.УИД);
// Добавляем XDTO-объект в механизм выгрузки ED.
// КомпонентыОбмена.ДобавитьКВыгрузке(ИнформацияОбУдалении);
// Или, если это часть корневого объекта:
// КомпонентыОбмена.КорневойОбъектВыгрузки.УдаленныеОбъекты.Добавить(ИнформацияОбУдалении);
// Помечаем запись в журнале как выгруженную, чтобы не выгружать повторно
МенеджерЗаписиЖурнала = РегистрыСведений.МойЖурналУдалений.СоздатьМенеджерЗаписи();
МенеджерЗаписиЖурнала.УИДУдаленногоОбъекта = Выборка.УИД; // Ключ для поиска записи
МенеджерЗаписиЖурнала.ИмяТипаУдаленногоОбъекта = Выборка.ИмяТипа; // Ключ для поиска записи
МенеджерЗаписиЖурнала.Прочитать();
Если МенеджерЗаписиЖурнала.Найдена Тогда
МенеджерЗаписиЖурнала.ПомеченКВыгрузке = Ложь; // Или добавить поле "Выгружено"
МенеджерЗаписиЖурнала.Записать();
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("Ошибка при выгрузке информации об удалении из журнала: " + ОписаниеОшибки(),
УровеньЖурналаРегистрации.Ошибка,
,
,
"ВыгрузкаЖурналаУдалений");
КонецПопытки;
КонецЦикла;
Получающая система получает сформированные нами XDTO-объекты ИнформацияОбУдалении. На основе данных ИмяТипаОбъекта и ИдентификаторОбъекта она находит соответствующий прикладной объект в своей базе данных и программно выполняет его пометку на удаление или физическое удаление.
Пример псевдокода для обработки на стороне получателя:
// В процедуре импорта Enterprise Data
// Предполагаем, что у нас есть входящий XDTO-объект "ИнформацияОбУдалении"
// (например, его получили из коллекции КомпонентыОбмена.КорневойОбъектВыгрузки.УдаленныеОбъекты)
ИмяТипаОбъекта = ВходящийОбъектXDTO.ИмяТипаОбъекта;
ИдентификаторОбъекта = ВходящийОбъектXDTO.ИдентификаторОбъекта;
Попытка
// Получаем метаданные объекта по имени типа
МетаданныеОбъекта = Метаданные.НайтиПоПолномуИмени(ИмяТипаОбъекта);
Если МетаданныеОбъекта = Неопределено Тогда
ЗаписьЖурналаРегистрации("Не найден тип объекта для удаления: " + ИмяТипаОбъекта,
УровеньЖурналаРегистрации.Предупреждение,
,
,
"ИмпортУдалений");
Возврат;
КонецЕсли;
// Получаем менеджер объекта для получения ссылки по УИД
МенеджерОбъекта = МетаданныеОбъекта.ПолучитьМенеджер();
СсылкаНаОбъект = МенеджерОбъекта.ПолучитьСсылкуПоУИД(Новый УникальныйИдентификатор(ИдентификаторОбъекта));
Если СсылкаНаОбъект.Пустая() Тогда
ЗаписьЖурналаРегистрации("Объект для удаления не найден или уже удален: УИД=" + ИдентификаторОбъекта,
УровеньЖурналаРегистрации.Информация,
,
,
"ИмпортУдалений");
Иначе
// Проверяем, помечен ли объект уже на удаление
Если СсылкаНаОбъект.ПометкаУдаления Тогда
ЗаписьЖурналаРегистрации("Объект уже помечен на удаление: УИД=" + ИдентификаторОбъекта,
УровеньЖурналаРегистрации.Информация,
,
,
"ИмпортУдалений");
Иначе
// Помечаем объект на удаление
СсылкаНаОбъект.ПометитьУдаление(Истина);
ЗаписьЖурналаРегистрации("Объект успешно помечен на удаление: УИД=" + ИдентификаторОбъекта,
УровеньЖурналаРегистрации.Информация,
,
,
"ИмпортУдалений");
// Если требуется физическое удаление:
// СсылкаНаОбъект.Удалить(Истина); // Будьте крайне осторожны с физическим удалением!
КонецЕсли;
КонецЕсли;
Исключение
ЗаписьЖурналаРегистрации("Ошибка при обработке информации об удалении: " + ОписаниеОшибки()
+ " Тип: " + ИмяТипаОбъекта + ", УИД: " + ИдентификаторОбъекта,
УровеньЖурналаРегистрации.Ошибка,
,
,
"ИмпортУдалений");
КонецПопытки;
После того как информация об удалении успешно выгружена и подтверждена получателем (особенно важно в синхронных или двусторонних обменах), записи из нашего "журнала удалений" могут быть очищены или помечены как обработанные. Это позволит избежать повторной выгрузки уже переданных данных и поддерживать журнал в актуальном состоянии.
При реализации механизма выгрузки удаленных объектов через Enterprise Data, мы рекомендуем обратить внимание на следующие аспекты:
Разграничение ролей: Enterprise Data предоставляет мощный фреймворк для обмена данными, но для специфических объектов, таких как УдалениеОбъекта, мы должны дополнять его собственной логикой. Не стоит ожидать, что универсальный инструмент решит все нестандартные задачи "из коробки".
Четкий формат сообщений: Всегда определяйте четкий и однозначный формат для передачи информации об удалении. Это критически важно для корректной интерпретации данных принимающей стороной. Документируйте этот формат.
Тестирование сценариев: Тщательно тестируйте механизм выгрузки и обработки удалений. Проверьте различные сценарии: удаление существующего объекта, повторное удаление уже удаленного объекта, удаление объекта, который еще не был синхронизирован, и т.д. Это поможет выявить потенциальные ошибки и непредвиденное поведение.
Идемпотентность: Убедитесь, что повторная передача сообщения об удалении не вызовет ошибок на стороне получателя. Если объект уже удален, повторная попытка удаления не должна приводить к сбою. Наш пример обработки на стороне получателя учитывает это.
Логирование: Внедрите подробное логирование процесса отслеживания, выгрузки и обработки удалений. Это поможет в диагностике проблем, если они возникнут в производственной среде.
Производительность: Если в системе происходит большое количество удалений, оцените влияние вашего механизма отслеживания на производительность базы данных. Оптимизируйте запросы и работу с регистром сведений.
Как мы выяснили, прямое использование стандартных методов выгрузки Enterprise Data для объекта УдалениеОбъекта невозможно из-за отсутствия у него обычных метаданных. Однако это не означает, что задача передачи информации об удалениях нерешаема. Мы рассмотрели несколько подходов, которые позволяют эффективно обрабатывать этот сценарий, будь то через формирование пользовательских сообщений, использование объекта УдалениеОбъекта как сигнала при работе с планами обмена, или полную реализацию собственного механизма отслеживания удалений при отсутствии прямой связи с планами обмена.
Ключ к успеху заключается в понимании природы УдалениеОбъекта и в реализации соответствующей логики, которая извлекает из него необходимую информацию и передает ее в структурированном виде. С правильным подходом и тщательным тестированием, вы сможете надежно синхронизировать информацию об удаленных объектах между вашими системами 1С.