Как программно записать документ и отменить его регистрацию изменений для планов обмена в 1С:Предприятии?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе работы с информационными системами на платформе 1С:Предприятие часто возникает задача массового изменения документов, при котором требуется предотвратить или отменить регистрацию этих изменений для узлов обмена данными. Это особенно актуально, когда изменения носят технический характер (например, массовое проставление значения реквизита без перепроведения) и не должны попадать в другие системы через механизмы обмена. Рассмотрим подробно, как можно решить эту задачу, используя различные подходы.

Начнем с анализа ситуации. У нас есть документы, которые подвергаются массовому изменению (например, проставление нового значения реквизита), после чего они записываются. При обычной записи документа система автоматически регистрирует изменения для всех узлов планов обмена, для которых настроена регистрация. Наша цель — избежать этой регистрации или удалить ее, если она уже произошла.

Подход 1: Предотвращение регистрации изменений до записи объекта (Рекомендуемый)

Наиболее эффективным и предпочтительным способом является предотвращение регистрации изменений до того, как документ будет записан. Этот подход позволяет избежать лишних операций по созданию и последующему удалению записей регистрации, что положительно сказывается на производительности системы, особенно при массовой обработке документов.

Для предотвращения регистрации изменений перед вызовом метода Записать() объекта (например, документа, справочника или другого прикладного объекта) нам необходимо установить определенные свойства объекта, связанные с механизмом обмена данными. Эти свойства находятся в служебной коллекции ОбменДанными, доступной у каждого прикладного объекта.

  1. Отключение автозаполнения получателей: Первым шагом является отключение автоматического определения узлов-получателей для регистрации изменений. Для этого установите свойство Автозаполнение коллекции Получатели объекта ОбменДанными в значение Ложь. Это указывает системе, что она не должна самостоятельно определять, в какие узлы нужно регистрировать изменения данного объекта.

  2. Очистка списка получателей: Вторым шагом необходимо убедиться, что список потенциальных получателей пуст. Даже если автозаполнение отключено, в списке Получатели могли остаться узлы, добавленные ранее или по умолчанию. Вызов метода Очистить() гарантирует, что список узлов, для которых система будет пытаться зарегистрировать изменения, будет пуст.

Давайте посмотрим на пример кода, демонстрирующий этот подход:


// Предположим, у нас есть ссылка на документ, который нужно изменить
// и предотвратить его регистрацию в планах обмена
СсылкаНаДокумент = Документы.РеализацияТоваровУслуг.НайтиПоНомеру("000000001");

// Получаем объект документа
Если СсылкаНаДокумент <> Неопределено Тогда
    ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();

    Если ДокОбъект <> Неопределено Тогда
        // 1. Изменяем необходимые реквизиты документа
        ДокОбъект.Переоценка = Истина; // Пример изменения реквизита
        // ... Другие изменения ...

        // 2. Отключаем регистрацию изменений для планов обмена
        // Важно: Эти строки должны быть выполнены до вызова метода Записать()
        ДокОбъект.ОбменДанными.Получатели.Автозаполнение = Ложь;
        ДокОбъект.ОбменДанными.Получатели.Очистить();

        // 3. Записываем документ
        Попытка
            ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
            Сообщить("Документ " + ДокОбъект.Номер + " успешно записан без регистрации изменений.");
        Исключение
            Сообщить("Ошибка при записи документа: " + ОписаниеОшибки());
        КонецПопытки;
    КонецЕсли;
Иначе
    Сообщить("Документ не найден.");
КонецЕсли;

Применение этих двух строк кода перед записью документа гарантирует, что при его сохранении никаких записей об изменениях для планов обмена создано не будет, даже если объект ранее имел зарегистрированные изменения. Это наиболее чистый и производительный метод, так как он исключает лишние операции по записи и последующему удалению регистрации изменений.

Подход 2: Удаление регистрации изменений после записи объекта

Иногда возникают ситуации, когда документ уже был записан, и его изменения автоматически зарегистрировались в планах обмена. В таких случаях нам необходимо программно удалить эти записи. Для этого платформа 1С:Предприятие предоставляет специальный метод УдалитьРегистрациюИзменений(), который находится в глобальном объекте ПланыОбмена.

Синтаксис метода ПланыОбмена.УдалитьРегистрациюИзменений() выглядит следующим образом:

ПланыОбмена.УдалитьРегистрациюИзменений(УзелОбмена, СсылкаНаОбъект);

Здесь:

Получение списка узлов для удаления

Прежде чем мы сможем использовать метод ПланыОбмена.УдалитьРегистрациюИзменений(), нам необходимо определить, в каких узлах обмена для конкретного документа были зарегистрированы изменения. Это особенно важно, так как попытка удалить регистрацию для узла, в котором объект не зарегистрирован, может привести к ошибке, если не использовать обработку исключений.

Для того чтобы получить список узлов, в которых зарегистрированы изменения определенного документа, мы можем выполнить запрос к системной таблице изменений этого документа. Каждой ссылке на документ или справочник в 1С:Предприятии соответствует служебная таблица <ИмяТипа>.Изменения, в которой хранятся записи о том, для каких узлов обмена зарегистрированы изменения данного объекта.

Рассмотрим пример запроса для документа "РеализацияТоваровУслуг":


ЗапросУзлов = Новый Запрос();
ЗапросУзлов.Текст = 
    "ВЫБРАТЬ
    |    РеализацияТоваровУслугИзменения.Узел
    |ИЗ
    |    Документ.РеализацияТоваровУслуг.Изменения КАК РеализацияТоваровУслугИзменения
    |ГДЕ
    |    РеализацияТоваровУслугИзменения.Ссылка = &Ссылка";

// Устанавливаем параметр &Ссылка - это ссылка на наш документ
ЗапросУзлов.УстановитьПараметр("Ссылка", Док.Ссылка); // Где Док - это объект или ссылка на документ

ВыборкаУзлов = ЗапросУзлов.Выполнить().Выбрать();

СписокУзловДляУдаления = Новый СписокЗначений();
Пока ВыборкаУзлов.Следующий() Цикл
    // Добавляем ссылку на узел в список. Узел.Ссылка - это непосредственно ссылка на элемент плана обмена.
    СписокУзловДляУдаления.Добавить(ВыборкаУзлов.Узел); 
КонецЦикла;

После выполнения этого запроса мы получим объект СписокЗначений, содержащий ссылки на все узлы планов обмена, для которых были зарегистрированы изменения нашего документа.

Вызов метода удаления регистрации изменений

Имея список узлов и ссылку на документ, мы можем пройтись по каждому узлу из списка и вызвать метод ПланыОбмена.УдалитьРегистрациюИзменений(). При этом важно передавать именно ссылку на узел и ссылку на документ.

Давайте соберем это воедино в рамках общего алгоритма:


// Предположим, у нас есть выборка документов, которые нужно обработать.
// Например, из результата запроса:
// ЗапросДокументов = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Документ.РеализацияТоваровУслуг ГДЕ ... ");
// Результат = ЗапросДокументов.Выполнить();
// Пока Результат.Следующий() Цикл
//     СсылкаДок = Результат.Ссылка;
//     ДокОбъект = СсылкаДок.ПолучитьОбъект();

// Пример: Обработка одного документа
СсылкаДок = Документы.РеализацияТоваровУслуг.НайтиПоНомеру("000000001");
Если СсылкаДок = Неопределено Тогда
    Сообщить("Документ не найден.");
    Возврат;
КонецЕсли;

ДокОбъект = СсылкаДок.ПолучитьОбъект();

// 1. Изменяем необходимые реквизиты документа
ДокОбъект.Переоценка = Истина; // Пример изменения реквизита
// ... Другие изменения ...

// 2. Записываем документ
// Если мы не использовали Подход 1, то при записи изменения зарегистрируются.
Попытка
    ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
    Сообщить("Документ " + ДокОбъект.Номер + " успешно записан.");
Исключение
    Сообщить("Ошибка при записи документа: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

// 3. Получаем список узлов, для которых зарегистрированы изменения этого документа
ЗапросУзлов = Новый Запрос();
ЗапросУзлов.Текст = 
    "ВЫБРАТЬ
    |    РеализацияТоваровУслугИзменения.Узел
    |ИЗ
    |    Документ.РеализацияТоваровУслуг.Изменения КАК РеализацияТоваровУслугИзменения
    |ГДЕ
    |    РеализацияТоваровУслугИзменения.Ссылка = &Ссылка";

ЗапросУзлов.УстановитьПараметр("Ссылка", ДокОбъект.Ссылка); // Используем ссылку на объект

ВыборкаУзлов = ЗапросУзлов.Выполнить().Выбрать();

СписокУзловДляУдаления = Новый СписокЗначений();
Пока ВыборкаУзлов.Следующий() Цикл
    СписокУзловДляУдаления.Добавить(ВыборкаУзлов.Узел); 
КонецЦикла;

// 4. Удаляем регистрацию изменений для каждого найденного узла
Для Каждого УзелПланаОбмена Из СписокУзловДляУдаления Цикл
    Попытка
        // Важно: в метод УдалитьРегистрациюИзменений передаем ссылку на узел и ссылку на документ
        ПланыОбмена.УдалитьРегистрациюИзменений(УзелПланаОбмена.Значение, ДокОбъект.Ссылка);
        Сообщить(Строка(ДокОбъект.Ссылка) + " - удалены изменения в узле: " + Строка(УзелПланаОбмена.Значение));
    Исключение
        // Обработка ошибок, если для какого-то узла удаление не удалось
        Сообщить("Ошибка удаления регистрации для узла " + УзелПланаОбмена.Значение + 
                 " и документа " + ДокОбъект.Ссылка + ": " + ОписаниеОшибки());
    КонецПопытки;
КонецЦикла;

// КонецЦикла; // Если обрабатываем выборку документов

В этом примере мы используем конструкцию Попытка...Исключение...КонецПопытки для обработки возможных ошибок при вызове ПланыОбмена.УдалитьРегистрациюИзменений(). Это хорошая практика, поскольку она позволяет избежать прерывания выполнения кода, если, например, в каком-то узле объект по какой-то причине уже не зарегистрирован или возникла другая непредвиденная ситуация.

Дополнительные нюансы и важные моменты

  1. Ссылка или Объект: Мы уже выяснили, что для метода ПланыОбмена.УдалитьРегистрациюИзменений() в качестве второго параметра требуется именно ссылка на объект. Если вы работаете с объектом (например, получили его через ПолучитьОбъект()), не забудьте обратиться к его свойству Ссылка при вызове метода: ДокОбъект.Ссылка.

  2. Свойство ОбменДанными.Загрузка = Истина: Иногда разработчики путают это свойство с отключением регистрации. Свойство Объект.ОбменДанными.Загрузка = Истина используется для указания системе, что текущий объект загружается из обмена данными. Оно отключает некоторые проверки уникальности и контроль ссылочной целостности при записи объекта, но не влияет на регистрацию изменений для исходящих планов обмена. Поэтому для нашей задачи оно бесполезно.

  3. Производительность: При массовой обработке документов и последующем удалении регистрации изменений, подход с удалением записей (Подход 2) является менее производительным по сравнению с предотвращением регистрации (Подход 1). Каждый раз, когда изменения регистрируются, это требует записи в служебные таблицы. Затем удаление этих записей также требует операций с базой данных. Если есть возможность, всегда отдавайте предпочтение предотвращению регистрации до записи.

  4. Универсальность получения узлов: Если вам нужно удалить регистрацию для всех планов обмена, независимо от того, зарегистрирован ли объект в них, вы можете получить список всех узлов всех планов обмена. Однако такой подход менее эффективен и более подвержен ошибкам, так как вы будете пытаться удалить регистрацию там, где ее, возможно, нет. Использование запроса к таблице изменений конкретного объекта (как в Подходе 2) является более точным и целевым.

    Пример получения всех узлов всех планов обмена (имеет смысл только если вы уверены, что объект регистрируется во всех возможных узлах или используете обработку ошибок):

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

    Однако, как уже говорилось, этот подход менее оптимален, чем целевое удаление по запросу к таблице изменений.

Выбор подходящего подхода зависит от конкретной ситуации и бизнес-логики. Если вы контролируете момент записи документа, то предотвращение регистрации до записи является лучшим выбором. Если же документ уже записан и изменения зарегистрированы, то удаление регистрации после записи с использованием запроса к таблице изменений будет эффективным решением.

← На главную