При интеграции различных конфигураций 1С, особенно когда одна из них — старая и сильно доработанная (например, УПП на платформе 8.1), часто возникают специфические ошибки. Разберем ситуацию, когда из современной конфигурации ЗУП через COM-соединение необходимо создавать и изменять документы в старой УПП. Проанализируем две типичные проблемы, с которыми можно столкнуться, и найдем для них решение.
Представим ситуацию: мы устанавливаем COM-соединение из ЗУП к базе УПП и пытаемся программно создать документ, например, "Зарплата к выплате организации". Соединение успешно, но в момент создания объекта возникает ошибка:
Произошла исключительная ситуация (1C:Enterprise 8.2.19.106): {Документ.ЗарплатаКВыплатеОрганизации.МодульОбъекта(624,54)}: Переменная не определена (мРаботаСДиалогами)
При этом мРаботаСДиалогами — это существующий общий модуль в конфигурации УПП. Давайте выясним причину и найдем решение.
Ключ к пониманию проблемы лежит в контексте выполнения кода. COM-соединение всегда работает в серверном (или внешнего соединения) контексте. Это означает, что в сеансе, который мы создали программно, отсутствует пользовательский интерфейс. Соответственно, любой код, предназначенный для интерактивной работы с пользователем (открытие форм, показ сообщений, вызов диалоговых окон), будет недоступен.
Модули, подобные мРаботаСДиалогами, как правило, содержат именно такой "клиентский" код. В старых конфигурациях, разработанных во времена преобладания толстого клиента, разработчики не всегда строго разделяли логику на клиентскую и серверную. В результате, в модуле объекта документа может находиться прямой вызов процедуры из клиентского общего модуля, что и приводит к ошибке в серверном контексте.
Существует два основных подхода к решению этой проблемы: быстрый, но локальный, и правильный, но более трудоемкий.
Установка флага "Внешнее соединение" для общего модуля.
Это самое простое и быстрое решение. Чтобы общий модуль стал "видимым" для сеанса внешнего соединения, необходимо в его свойствах установить соответствующий флаг.
Пошаговая инструкция:
мРаботаСДиалогами).После этой операции ошибка "Переменная не определена" для данного модуля должна исчезнуть, так как он станет доступен в контексте нашего COM-соединения. Однако это может "вскрыть" следующие проблемы, если внутри модуля вызываются другие, недоступные в серверном контексте, методы.
Рефакторинг кода с использованием директив препроцессора.
Это архитектурно правильный подход, который делает код более надежным и предсказуемым. Суть в том, чтобы "обернуть" клиентский код в специальные директивы, которые укажут платформе, в каком контексте этот код можно выполнять.
Используются следующие директивы:
#Если Клиент Тогда ... #КонецЕсли — код внутри этого блока будет скомпилирован и выполнен только на клиенте.#Если Сервер Тогда ... #КонецЕсли — код для выполнения на сервере.#Если ВнешнееСоединение Тогда ... #КонецЕсли — код специально для сеансов внешнего соединения.В нашем случае, если в модуле объекта документа есть вызов какой-либо диалоговой функции, его нужно изолировать.
Пример:
Предположим, в коде модуля объекта есть такой фрагмент:
// ... какая-то логика
мРаботаСДиалогами.ПоказатьПредупреждениеПользователю("Документ создан!");
// ... дальнейшая логика
При выполнении через COM этот код вызовет ошибку. Чтобы ее избежать, нужно изменить код следующим образом:
// ... какая-то логика
#Если Клиент Тогда
мРаботаСДиалогами.ПоказатьПредупреждениеПользователю("Документ создан!");
#КонецЕсли
// ... дальнейшая логика
Такой подход требует анализа всего кода, который выполняется при создании документа, и может быть очень трудоемким для старой, переписанной конфигурации. Но в долгосрочной перспективе это самое надежное решение.
Итак, мы решили первую проблему и смогли создать документ. Теперь возникает новая задача: найти существующий документ и изменить его, например, отменить проведение. Мы пишем код, который находит документ, и пытаемся изменить его свойство Проведен:
// ... код подключения ...
// Находим документ в базе УПП
НайденныйДок = БазаУПП.Документы.ЗарплатаКВыплатеОрганизации.НайтиПоНомеру("...");
Если НЕ НайденныйДок.Пустая() Тогда
// Пытаемся отменить проведение
НайденныйДок.Проведен = Ложь; // Здесь возникает ошибка!
КонецЕсли;
В результате выполнения этого кода мы получаем ошибку:
Поле объекта недоступно для записи (Проведен)
При этом, если открыть тот же документ в интерфейсе УПП и отменить проведение вручную, все работает. Разберемся, в чем дело.
Эта ошибка — классический пример непонимания разницы между ссылкой на объект и самим объектом в 1С.
ДокументСсылка) — это, по сути, просто указатель на запись в базе данных. Она содержит уникальный идентификатор, номер, дату и позволяет прочитать основные реквизиты, но не дает возможности изменять документ. Методы вроде НайтиПоНомеру(), НайтиПоКоду() возвращают именно ссылку. Это быстрая операция, так как не требует загрузки всех данных документа в память.ДокументОбъект) — это полная копия данных документа, загруженная в оперативную память для редактирования. Только получив объект, мы можем менять его реквизиты, табличные части и статус проведения.Ошибка возникает потому, что мы пытаемся изменить свойство Проведен у ссылки, что является запрещенной операцией. Платформа защищает данные от прямого изменения в обход внутренней логики.
Чтобы корректно изменить документ, нужно выполнить следующую последовательность действий:
Получить объект из ссылки.
Для этого у любой ссылки есть метод ПолучитьОбъект(). Он загружает все данные документа из базы в память и возвращает объект, готовый к редактированию.
Внести изменения в объект.
Теперь мы можем менять реквизиты, табличные части и т.д.
Записать объект.
Для отмены проведения нельзя просто присвоить Проведен = Ложь. Необходимо использовать метод Записать() со специальным параметром — РежимЗаписиДокумента.ОтменаПроведения. Этот режим гарантирует, что платформа корректно отменит проведение и удалит все движения документа по регистрам.
Также при программной работе с данными в режиме интеграции или обмена полезно использовать свойство ОбменДанными.Загрузка. Установка его в Истина позволяет обойти многие проверки бизнес-логики (например, контроль остатков, проверку заполнения), которые не нужны при прямой загрузке данных.
Посмотрим на исправленный код:
// ... код подключения ...
// Находим ссылку на документ в базе УПП
НайденнаяСсылка = БазаУПП.Документы.ЗарплатаКВыплатеОрганизации.НайтиПоНомеру("...");
Если НЕ НайденнаяСсылка.Пустая() Тогда
// 1. Получаем ОБЪЕКТ из ссылки
ДокОбъект = НайденнаяСсылка.ПолучитьОбъект();
// 2. (Опционально) Включаем режим загрузки данных для обхода проверок
ДокОбъект.ОбменДанными.Загрузка = Истина;
// 3. (Опционально) Вносим другие изменения в реквизиты или таблицы
// ДокОбъект.Комментарий = "Изменено через COM";
// 4. Корректно записываем с отменой проведения
ДокОбъект.Записать(БазаУПП.РежимЗаписиДокумента.ОтменаПроведения);
Сообщить("Проведение документа успешно отменено!");
Иначе
Сообщить("Документ не найден!");
КонецЕсли;
Этот код сначала получает полноценный объект документа, а затем использует штатный механизм платформы для отмены проведения, что гарантирует целостность данных и корректное выполнение операции.