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