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