При работе с распределенными системами на базе 1С часто используется механизм COM-соединения для оперативной передачи данных между базами. Однако после обновления типовых конфигураций, таких как Управление торговлей 11 (начиная с 11.4.8), ERP 2 (с 2.4.8) или Комплексная автоматизация 2, многие разработчики сталкиваются с критической ошибкой при попытке программного создания или проведения документов через внешнее соединение. Ошибка выглядит следующим образом: Произошла исключительная ситуация (1C:Enterprise 8.3): Ошибка при выполнении обработчика - 'ПриЗаписи' {РегистрСведений.РеестрДокументов.МодульМенеджера(30)}: Ошибка при вызове метода контекста (Записать) Не удалось записать: "Реестр документов"!.
В этой статье мы подробно разберем причины возникновения этой ситуации, проанализируем изменения в типовом коде и рассмотрим несколько эффективных способов решения проблемы.
Проанализируем программный код, который вызывает исключение. Ошибка происходит не в самом документе, а в подчиненном регистре сведений РеестрДокументов. Этот регистр используется системой для оптимизации построения списков документов и хранения аналитических разрезов. Начиная с указанных версий конфигураций, разработчики фирмы «1С» добавили жесткую проверку прав доступа и режимов выполнения в модуль набора записей этого регистра.
Рассмотрим фрагмент кода в процедуре ПередЗаписью модуля набора записей регистра РеестрДокументов:
Процедура ПередЗаписью(Отказ, Замещение)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Если Не ПривилегированныйРежим() Тогда
// Здесь система проверяет, выполняется ли код в привилегированном режиме
Отказ = Истина;
КонецЕсли;
ОбновлениеИнформационнойБазы.ПроверитьОбъектОбработан(ЭтотОбъект);
КонецПроцедуры
Суть проблемы заключается в том, что при работе через COM-соединение код выполняется в контексте внешнего пользователя. Даже если в модуле менеджера или общем модуле, из которого инициируется запись, установлен привилегированный режим, при переходе к записи набора записей регистра сведений в контексте COM-соединения этот режим может «теряться» или не распознаваться платформой как достаточный. В результате срабатывает условие Если Не ПривилегированныйРежим() Тогда Отказ = Истина;, и транзакция записи документа откатывается.
Если нам необходимо быстро восстановить работоспособность обменов, не дожидаясь исправления в типовых модулях, мы можем воспользоваться механизмом расширений. Рассмотрим, как внести изменения в логику работы регистра.
Нам нужно перехватить процедуру ПередЗаписью в модуле набора записей регистра сведений РеестрДокументов. В расширении мы добавим условие, которое позволит игнорировать отсутствие привилегированного режима, если соединение является внешним:
&Вместо("ПередЗаписью")
Процедура Расш_ПередЗаписью(Отказ, Замещение)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Проверяем на внешнее соединение
#Если ВнешнееСоединение Тогда
Возврат; // Разрешаем запись для COM
#КонецЕсли
Если Не ПривилегированныйРежим() Тогда
// Стандартная логика для обычных сеансов
Отказ = Истина;
Возврат;
КонецЕсли;
ПродолжитьВызов(Отказ, Замещение);
КонецПроцедуры
Важный момент: Использование этого метода требует осторожности, так как проверка на привилегированный режим была введена для обеспечения целостности данных при обновлении информационной базы. Однако для задач интеграции через COM это зачастую единственный быстрый способ.
Разберем ситуацию, когда документ записывается дважды. Часто в коде интеграции встречается подобная конструкция, направленная на установку конкретного УникальныйИдентификатор (GUID):
// Ошибка часто возникает здесь
ДокV8.Записать(V8.РежимЗаписиДокумента.Запись);
ДокV8.ОбменДанными.Загрузка = Ложь;
ДокV8.Записать(V8.РежимЗаписиДокумента.Запись);
Проанализируем, почему так делать не стоит. Первая запись с установленным флагом ОбменДанными.Загрузка = Истина проходит успешно, так как регистр РеестрДокументов игнорирует проверку режима (см. первую строку кода модуля выше). Однако вторая запись с выключенным флагом загрузки вызывает срабатывание триггера и ошибку.
Чтобы избежать двойной записи и при этом сохранить нужный GUID, мы рекомендуем использовать метод УстановитьСсылкуНового(). Посмотрим на правильный пример кода на стороне вызывающей базы:
НовыйGUID = Новый УникальныйИдентификатор(СтрокаGUID);
СсылкаНаДокумент = Справочники.РеализацияТоваровУслуг.ПолучитьСсылку(НовыйGUID);
ДокументОбъект = Справочники.РеализацияТоваровУслуг.СоздатьДокумент();
ДокументОбъект.УстановитьСсылкуНового(СсылкаНаДокумент);
// Заполняем реквизиты...
// ...
// Устанавливаем признак загрузки, если не хотим выполнять лишние проверки
ДокументОбъект.ОбменДанными.Загрузка = Истина;
ДокументОбъект.Записать();
Использование УстановитьСсылкуНового позволяет зафиксировать идентификатор документа до его физической записи в базу данных, что исключает необходимость «перезаписывать» документ для смены GUID.
Более архитектурно правильным решением является вынос логики записи документа в общий модуль на стороне базы-приемника. Этот модуль должен иметь флаги «Сервер» и «Привилегированный».
Разберем этот подход по шагам:
ИнтеграцияВызовСервера.Привилегированный.В этом случае, когда COM-соединение вызывает функцию ИнтеграцияВызовСервера.СоздатьДокумент(Данные), весь код внутри функции будет выполняться в привилегированном режиме на стороне сервера. Это гарантирует, что запись в РеестрДокументов пройдет без ошибок, так как проверка ПривилегированныйРежим() вернет значение Истина.
Иногда корень проблемы кроется не в программном коде, а в состоянии данных после обновления. Проанализируем ситуацию: в новых версиях УТ 11/ERP задействован механизм формирования ключей аналитики учета номенклатуры. Если после обновления конфигурации фоновые задания по заполнению этих ключей не завершились или завершились с ошибкой, запись документа может блокироваться.
Посмотрим, как можно это исправить:
Склады. Это инициирует цепочку обновлений связанных регистров, включая РеестрДокументов.Если ошибка сохраняется, и вам необходимо увидеть, на какой именно строке и с какими значениями параметров падает система, нужно настроить отладку COM-соединения. Рассмотрим алгоритм действий:
-debug).После подключения вы сможете поставить точку останова в модуле менеджера регистра РеестрДокументов и воочию увидеть состояние флага ПривилегированныйРежим() в момент вызова метода Записать().
Подводя итог, можно сказать, что ошибка «Не удалось записать: Реестр документов» — это следствие повышения требований безопасности в типовых решениях 1С. Для исправления ситуации мы рекомендуем:
ОбменДанными.Загрузка = Истина перед каждой записью через COM.ПередЗаписью регистра сведений.В долгосрочной перспективе стоит рассмотреть переход с технологии COM на использование HTTP-сервисов. Это избавит вас от проблем с разрядностью приложений (32/64 бит), настройками DCOM и позволит более гибко управлять контекстом выполнения кода на стороне сервера.